diff --git a/.claude/skills/web-info/SKILL.md b/.claude/skills/web-info/SKILL.md new file mode 100644 index 00000000..e8b13d98 --- /dev/null +++ b/.claude/skills/web-info/SKILL.md @@ -0,0 +1,62 @@ +--- +name: web-info +description: Статус Apache и веб-публикаций 1С — запущен ли сервер, какие базы опубликованы, ошибки. Используй когда пользователь спрашивает про статус веб-сервера, опубликованные базы, работает ли Apache +argument-hint: "" +allowed-tools: + - Bash + - Read + - Glob +--- + +# /web-info — Статус Apache и публикаций 1С + +Показывает состояние Apache HTTP Server, список опубликованных баз и последние ошибки. + +## Usage + +``` +/web-info +``` + +## Параметры подключения + +Прочитай `.v8-project.json` из корня проекта. Если задан `webPath` — используй как `-ApachePath`. +По умолчанию `tools/apache24` от корня проекта. + +## Команда + +```powershell +powershell.exe -NoProfile -File .claude/skills/web-info/scripts/web-info.ps1 <параметры> +``` + +### Параметры скрипта + +| Параметр | Обязательный | Описание | +|----------|:------------:|----------| +| `-ApachePath <путь>` | нет | Корень Apache (по умолчанию `tools/apache24`) | + +## Формат вывода + +``` +=== Apache Web Server === +Status: Запущен (PID: 12345) +Path: C:\...\tools\apache24 +Port: 8081 +Module: C:/Program Files/1cv8/8.3.24.1691/bin/wsap24.dll + +=== Опубликованные базы === + mydb http://localhost:8081/mydb File="C:\Bases\MyDB"; + +=== Последние ошибки === +(пусто) +``` + +## Примеры + +```powershell +# Статус по умолчанию +powershell.exe -NoProfile -File .claude/skills/web-info/scripts/web-info.ps1 + +# Указать путь к Apache +powershell.exe -NoProfile -File .claude/skills/web-info/scripts/web-info.ps1 -ApachePath "C:\tools\apache24" +``` diff --git a/.claude/skills/web-info/scripts/web-info.ps1 b/.claude/skills/web-info/scripts/web-info.ps1 new file mode 100644 index 00000000..b8ddff73 --- /dev/null +++ b/.claude/skills/web-info/scripts/web-info.ps1 @@ -0,0 +1,148 @@ +# web-info v1.0 — Apache & 1C publication status +# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills +<# +.SYNOPSIS + Статус Apache и публикаций 1С + +.DESCRIPTION + Показывает состояние Apache HTTP Server, список опубликованных баз + и последние ошибки из error.log. + +.PARAMETER ApachePath + Корень Apache (по умолчанию tools\apache24) + +.EXAMPLE + .\web-info.ps1 + +.EXAMPLE + .\web-info.ps1 -ApachePath "C:\tools\apache24" +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory=$false)] + [string]$ApachePath +) + +$OutputEncoding = [System.Text.Encoding]::UTF8 +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 + +# --- Resolve ApachePath --- +if (-not $ApachePath) { + $projectRoot = (Get-Item $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + $ApachePath = Join-Path $projectRoot "tools\apache24" +} + +# --- Check Apache installation --- +$httpdExe = Join-Path (Join-Path $ApachePath "bin") "httpd.exe" + +Write-Host "=== Apache Web Server ===" -ForegroundColor Cyan + +if (-not (Test-Path $httpdExe)) { + Write-Host "Status: Не установлен" -ForegroundColor Red + Write-Host "Path: $ApachePath (не найден)" + Write-Host "" + Write-Host "Используйте /web-publish для установки Apache." -ForegroundColor Yellow + exit 0 +} + +# --- Check process (only our Apache) --- +$httpdExeNorm = (Resolve-Path $httpdExe -ErrorAction SilentlyContinue).Path +$ourProc = Get-Process httpd -ErrorAction SilentlyContinue | Where-Object { + try { $_.Path -eq $httpdExeNorm } catch { $false } +} +$foreignProc = Get-Process httpd -ErrorAction SilentlyContinue | Where-Object { + try { $_.Path -ne $httpdExeNorm } catch { $true } +} +if ($ourProc) { + $pids = ($ourProc | ForEach-Object { $_.Id }) -join ", " + Write-Host "Status: Запущен (PID: $pids)" -ForegroundColor Green +} else { + Write-Host "Status: Остановлен" -ForegroundColor Yellow +} +if ($foreignProc) { + $fpid = ($foreignProc | Select-Object -First 1).Id + $fpath = try { ($foreignProc | Select-Object -First 1).Path } catch { "?" } + Write-Host "[WARN] Обнаружен сторонний Apache (PID: $fpid, $fpath)" -ForegroundColor Yellow +} + +Write-Host "Path: $ApachePath" + +# --- Parse httpd.conf --- +$confFile = Join-Path (Join-Path $ApachePath "conf") "httpd.conf" +if (-not (Test-Path $confFile)) { + Write-Host "Config: httpd.conf не найден" -ForegroundColor Red + exit 0 +} + +$confContent = [System.IO.File]::ReadAllText($confFile) + +# Extract port from global block +$port = "—" +if ($confContent -match '(?m)^Listen\s+(\d+)') { + $port = $Matches[1] +} +Write-Host "Port: $port" + +# Extract wsap24 path +if ($confContent -match 'LoadModule\s+_1cws_module\s+"([^"]+)"') { + Write-Host "Module: $($Matches[1])" +} + +# --- Publications --- +Write-Host "" +Write-Host "=== Опубликованные базы ===" -ForegroundColor Cyan + +$pubPattern = '# --- 1C Publication: (.+?) ---' +$pubMatches = [regex]::Matches($confContent, $pubPattern) + +if ($pubMatches.Count -eq 0) { + Write-Host "(нет публикаций)" -ForegroundColor Yellow +} else { + foreach ($match in $pubMatches) { + $appName = $match.Groups[1].Value + + # Read default.vrd for this publication + $vrdPath = Join-Path (Join-Path (Join-Path $ApachePath "publish") $appName) "default.vrd" + $ibInfo = "—" + if (Test-Path $vrdPath) { + $vrdContent = [System.IO.File]::ReadAllText($vrdPath) + if ($vrdContent -match 'ib="([^"]*)"') { + $ibInfo = $Matches[1] -replace '"','"' + } + } + + # Detect published services + $svcTags = @() + if (Test-Path $vrdPath) { + if ($vrdContent -match ' +``` + +### Параметры скрипта + +| Параметр | Обязательный | Описание | +|----------|:------------:|----------| +| `-V8Path <путь>` | нет | Каталог bin платформы (для wsap24.dll) | +| `-InfoBasePath <путь>` | * | Файловая база | +| `-InfoBaseServer <сервер>` | * | Сервер 1С (для серверной базы) | +| `-InfoBaseRef <имя>` | * | Имя базы на сервере | +| `-UserName <имя>` | нет | Имя пользователя | +| `-Password <пароль>` | нет | Пароль | +| `-AppName <имя>` | нет | Имя публикации (по умолчанию из имени каталога базы) | +| `-ApachePath <путь>` | нет | Корень Apache (по умолчанию `tools/apache24`) | +| `-Port <порт>` | нет | Порт (по умолчанию `8081`) | +| `-Manual` | нет | Не скачивать — только проверить и дать инструкцию | + +> `*` — нужен либо `-InfoBasePath`, либо пара `-InfoBaseServer` + `-InfoBaseRef` + +## Несколько пользователей одной базы + +Повторный вызов с тем же AppName **заменяет** публикацию (идемпотентность). Это используется для: +- смены пользователя: «опубликуй под Ивановым» → тот же AppName, новый `-UserName` +- перезапуска после `/web-stop`: тот же вызов поднимает Apache обратно + +Если пользователь просит **параллельную** публикацию под другим пользователем (для тестирования разных наборов прав), добавь суффикс к AppName: +- база `bpdemo`, пользователь `Иванов` → `-AppName bpdemo-ivanov` +- база `bpdemo`, пользователь `Admin` → `-AppName bpdemo-admin` (или просто `bpdemo`) + +Ключевые слова: «ещё одну публикацию», «дополнительно», «параллельно», «под другим пользователем не убирая текущую». + +## После выполнения + +1. Сообщи URL-ы: + - Веб-клиент: `http://localhost:{Port}/{AppName}` + - OData: `http://localhost:{Port}/{AppName}/odata/standard.odata` + - HTTP-сервисы: `http://localhost:{Port}/{AppName}/hs//...` + - Web-сервисы: `http://localhost:{Port}/{AppName}/ws/<Имя>?wsdl` +2. Предложи открыть в браузере +3. Если нужно протестировать сервис — помоги составить запрос +4. Если база не зарегистрирована — предложи `/db-list add` + +## Примеры + +```powershell +# Файловая база +powershell.exe -NoProfile -File .claude/skills/web-publish/scripts/web-publish.ps1 -InfoBasePath "C:\Bases\MyDB" -UserName "Admin" + +# С явным именем публикации и портом +powershell.exe -NoProfile -File .claude/skills/web-publish/scripts/web-publish.ps1 -InfoBasePath "C:\Bases\MyDB" -AppName "mydb" -Port 9090 + +# Серверная база +powershell.exe -NoProfile -File .claude/skills/web-publish/scripts/web-publish.ps1 -InfoBaseServer "srv01" -InfoBaseRef "MyDB" -UserName "Admin" -Password "secret" + +# Ручной режим (только инструкция) +powershell.exe -NoProfile -File .claude/skills/web-publish/scripts/web-publish.ps1 -InfoBasePath "C:\Bases\MyDB" -Manual +``` diff --git a/.claude/skills/web-publish/scripts/web-publish.ps1 b/.claude/skills/web-publish/scripts/web-publish.ps1 new file mode 100644 index 00000000..e3300a0a --- /dev/null +++ b/.claude/skills/web-publish/scripts/web-publish.ps1 @@ -0,0 +1,373 @@ +# web-publish v1.0 — Publish 1C infobase via Apache +# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills +<# +.SYNOPSIS + Публикация информационной базы 1С через Apache HTTP Server + +.DESCRIPTION + Генерирует default.vrd и настраивает httpd.conf для веб-доступа + к информационной базе 1С. При необходимости скачивает portable Apache. + Идемпотентный — повторный вызов обновляет конфигурацию. + +.PARAMETER V8Path + Путь к каталогу bin платформы (для wsap24.dll) + +.PARAMETER InfoBasePath + Путь к файловой информационной базе + +.PARAMETER InfoBaseServer + Сервер 1С (для серверной базы) + +.PARAMETER InfoBaseRef + Имя базы на сервере + +.PARAMETER UserName + Имя пользователя 1С + +.PARAMETER Password + Пароль пользователя + +.PARAMETER AppName + Имя публикации (по умолчанию из имени каталога базы) + +.PARAMETER ApachePath + Корень Apache (по умолчанию tools\apache24) + +.PARAMETER Port + Порт (по умолчанию 8081) + +.PARAMETER Manual + Не скачивать Apache — только проверить и дать инструкцию + +.EXAMPLE + .\web-publish.ps1 -InfoBasePath "C:\Bases\MyDB" + +.EXAMPLE + .\web-publish.ps1 -InfoBasePath "C:\Bases\MyDB" -AppName "mydb" -Port 9090 + +.EXAMPLE + .\web-publish.ps1 -InfoBaseServer "srv01" -InfoBaseRef "MyDB" -Manual +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory=$false)] + [string]$V8Path, + + [Parameter(Mandatory=$false)] + [string]$InfoBasePath, + + [Parameter(Mandatory=$false)] + [string]$InfoBaseServer, + + [Parameter(Mandatory=$false)] + [string]$InfoBaseRef, + + [Parameter(Mandatory=$false)] + [string]$UserName, + + [Parameter(Mandatory=$false)] + [string]$Password, + + [Parameter(Mandatory=$false)] + [string]$AppName, + + [Parameter(Mandatory=$false)] + [string]$ApachePath, + + [Parameter(Mandatory=$false)] + [int]$Port = 8081, + + [Parameter(Mandatory=$false)] + [switch]$Manual +) + +# --- Encoding --- +$OutputEncoding = [System.Text.Encoding]::UTF8 +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 + +# --- Resolve V8Path --- +if (-not $V8Path) { + $found = Get-ChildItem "C:\Program Files\1cv8\*\bin\1cv8.exe" -ErrorAction SilentlyContinue | Sort-Object FullName -Descending | Select-Object -First 1 + if ($found) { + $V8Path = Split-Path $found.FullName -Parent + } else { + Write-Host "Error: платформа 1С не найдена. Укажите -V8Path" -ForegroundColor Red + exit 1 + } +} elseif (Test-Path $V8Path -PathType Leaf) { + $V8Path = Split-Path $V8Path -Parent +} + +# Validate wsap24.dll +$wsapDll = Join-Path $V8Path "wsap24.dll" +if (-not (Test-Path $wsapDll)) { + Write-Host "Error: wsap24.dll не найден в $V8Path" -ForegroundColor Red + exit 1 +} + +# --- Validate connection --- +if (-not $InfoBasePath -and (-not $InfoBaseServer -or -not $InfoBaseRef)) { + Write-Host "Error: укажите -InfoBasePath или -InfoBaseServer + -InfoBaseRef" -ForegroundColor Red + exit 1 +} + +# --- Resolve ApachePath --- +if (-not $ApachePath) { + $projectRoot = (Get-Item $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + $ApachePath = Join-Path $projectRoot "tools\apache24" +} + +# --- Check / Install Apache --- +$httpdExe = Join-Path (Join-Path $ApachePath "bin") "httpd.exe" + +if (-not (Test-Path $httpdExe)) { + if ($Manual) { + Write-Host "Apache не найден: $ApachePath" -ForegroundColor Yellow + Write-Host "" + Write-Host "Установите Apache вручную:" -ForegroundColor Cyan + Write-Host " 1. Скачайте Apache Lounge (x64) с https://www.apachelounge.com/download/" + Write-Host " 2. Распакуйте содержимое Apache24\ в: $ApachePath" + Write-Host " 3. Запустите скрипт повторно" + exit 1 + } + + Write-Host "Apache не найден. Скачиваю..." -ForegroundColor Cyan + $zipUrl = "https://www.apachelounge.com/download/VS18/binaries/httpd-2.4.66-260131-Win64-VS18.zip" + $tmpZip = Join-Path $env:TEMP "apache24.zip" + $tmpDir = Join-Path $env:TEMP "apache24_extract" + + try { + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + # WebClient works better than Invoke-WebRequest in PS 5.1 (handles redirects) + $wc = New-Object System.Net.WebClient + $wc.DownloadFile($zipUrl, $tmpZip) + } catch { + Write-Host "Error: не удалось скачать Apache: $_" -ForegroundColor Red + Write-Host "Скачайте вручную: https://www.apachelounge.com/download/" -ForegroundColor Yellow + exit 1 + } + + Write-Host "Распаковка..." + if (Test-Path $tmpDir) { Remove-Item $tmpDir -Recurse -Force } + Expand-Archive -Path $tmpZip -DestinationPath $tmpDir -Force + + # Move Apache24 contents up to ApachePath + $innerDir = Join-Path $tmpDir "Apache24" + if (-not (Test-Path $innerDir)) { + # Try to find Apache24 in nested folder + $innerDir = Get-ChildItem $tmpDir -Directory -Recurse -Filter "Apache24" | Select-Object -First 1 + if ($innerDir) { $innerDir = $innerDir.FullName } else { + Write-Host "Error: каталог Apache24 не найден в архиве" -ForegroundColor Red + exit 1 + } + } + + if (-not (Test-Path $ApachePath)) { + New-Item -ItemType Directory -Path $ApachePath -Force | Out-Null + } + Copy-Item -Path (Join-Path $innerDir "*") -Destination $ApachePath -Recurse -Force + + # Cleanup + Remove-Item $tmpZip -Force -ErrorAction SilentlyContinue + Remove-Item $tmpDir -Recurse -Force -ErrorAction SilentlyContinue + + # Patch ServerRoot in httpd.conf + $confFile = Join-Path (Join-Path $ApachePath "conf") "httpd.conf" + if (Test-Path $confFile) { + $apachePathFwd = $ApachePath -replace '\\','/' + $confContent = [System.IO.File]::ReadAllText($confFile) + $confContent = $confContent -replace '(?m)^Define SRVROOT .*$', "Define SRVROOT `"$apachePathFwd`"" + [System.IO.File]::WriteAllText($confFile, $confContent) + Write-Host "ServerRoot обновлён: $apachePathFwd" -ForegroundColor Green + } + + Write-Host "Apache установлен: $ApachePath" -ForegroundColor Green +} + +# --- Derive AppName --- +if (-not $AppName) { + if ($InfoBasePath) { + $AppName = (Split-Path $InfoBasePath -Leaf) -replace '[^\w]','' + } else { + $AppName = $InfoBaseRef -replace '[^\w]','' + } + $AppName = $AppName.ToLower() +} +$AppName = $AppName.ToLower() + +if (-not $AppName) { + Write-Host "Error: не удалось определить имя публикации. Укажите -AppName" -ForegroundColor Red + exit 1 +} + +Write-Host "Публикация: $AppName" -ForegroundColor Cyan + +# --- Create publish directory --- +$publishDir = Join-Path (Join-Path $ApachePath "publish") $AppName +if (-not (Test-Path $publishDir)) { + New-Item -ItemType Directory -Path $publishDir -Force | Out-Null +} + +# --- Generate default.vrd --- +$vrdPath = Join-Path $publishDir "default.vrd" + +$ibParts = @() +if ($InfoBaseServer -and $InfoBaseRef) { + $ibParts += "Srvr="$InfoBaseServer"" + $ibParts += "Ref="$InfoBaseRef"" +} else { + $ibParts += "File="$InfoBasePath"" +} +if ($UserName) { $ibParts += "Usr="$UserName"" } +if ($Password) { $ibParts += "Pwd="$Password"" } +$ibString = ($ibParts -join ";") + ";" + +$vrdContent = @" + + + + + +"@ + +$utf8Bom = New-Object System.Text.UTF8Encoding($true) +[System.IO.File]::WriteAllText($vrdPath, $vrdContent, $utf8Bom) +Write-Host "default.vrd: $vrdPath" -ForegroundColor Green + +# --- Update httpd.conf --- +$confFile = Join-Path (Join-Path $ApachePath "conf") "httpd.conf" +if (-not (Test-Path $confFile)) { + Write-Host "Error: httpd.conf не найден: $confFile" -ForegroundColor Red + exit 1 +} + +$confContent = [System.IO.File]::ReadAllText($confFile) +$apachePathFwd = $ApachePath -replace '\\','/' +$wsapDllFwd = $wsapDll -replace '\\','/' +$publishDirFwd = $publishDir -replace '\\','/' +$vrdPathFwd = $vrdPath -replace '\\','/' + +# --- Global block (Listen + LoadModule) --- +$globalMarkerStart = "# --- 1C: global ---" +$globalMarkerEnd = "# --- End: global ---" +$globalBlock = @" +$globalMarkerStart +Listen $Port +LoadModule _1cws_module "$wsapDllFwd" +$globalMarkerEnd +"@ + +if ($confContent -match [regex]::Escape($globalMarkerStart)) { + # Replace existing global block + $pattern = [regex]::Escape($globalMarkerStart) + '[\s\S]*?' + [regex]::Escape($globalMarkerEnd) + $confContent = [regex]::Replace($confContent, $pattern, $globalBlock) +} else { + # Comment out default Listen to avoid port conflict + $confContent = $confContent -replace '(?m)^(Listen\s+\d+)', '#$1 # commented by web-publish' + # Append global block + $confContent = $confContent.TrimEnd() + "`n`n" + $globalBlock + "`n" +} + +# --- Publication block --- +$pubMarkerStart = "# --- 1C Publication: $AppName ---" +$pubMarkerEnd = "# --- End: $AppName ---" +$pubBlock = @" +$pubMarkerStart +Alias "/$AppName" "$publishDirFwd" + + AllowOverride All + Require all granted + SetHandler 1c-application + ManagedApplicationDescriptor "$vrdPathFwd" + +$pubMarkerEnd +"@ + +if ($confContent -match [regex]::Escape($pubMarkerStart)) { + # Replace existing publication block + $pattern = [regex]::Escape($pubMarkerStart) + '[\s\S]*?' + [regex]::Escape($pubMarkerEnd) + $confContent = [regex]::Replace($confContent, $pattern, $pubBlock) +} else { + # Append publication block + $confContent = $confContent.TrimEnd() + "`n`n" + $pubBlock + "`n" +} + +[System.IO.File]::WriteAllText($confFile, $confContent) +Write-Host "httpd.conf обновлён" -ForegroundColor Green + +# --- Helper: filter httpd processes by our ApachePath --- +function Get-OurHttpd { + $httpdExeNorm = (Resolve-Path $httpdExe -ErrorAction SilentlyContinue).Path + Get-Process httpd -ErrorAction SilentlyContinue | Where-Object { + try { $_.Path -eq $httpdExeNorm } catch { $false } + } +} + +# --- Check port availability --- +$portCheck = Get-NetTCPConnection -LocalPort $Port -ErrorAction SilentlyContinue | Select-Object -First 1 +if ($portCheck) { + $ourProc = Get-OurHttpd + if ($ourProc) { + # Our Apache holds the port — will restart + } else { + $holder = Get-Process -Id $portCheck.OwningProcess -ErrorAction SilentlyContinue + $holderName = if ($holder) { "$($holder.ProcessName) (PID: $($holder.Id))" } else { "PID $($portCheck.OwningProcess)" } + Write-Host "Error: порт $Port занят процессом $holderName" -ForegroundColor Red + Write-Host "Укажите другой порт: -Port 9090" -ForegroundColor Yellow + exit 1 + } +} + +# --- Start Apache if not running --- +$httpdProc = Get-OurHttpd +if ($httpdProc) { + Write-Host "Apache уже запущен (PID: $(($httpdProc | Select-Object -First 1).Id))" -ForegroundColor Yellow + Write-Host "Перезапуск для применения конфигурации..." + $httpdProc | Stop-Process -Force -ErrorAction SilentlyContinue + Start-Sleep -Seconds 1 +} else { + # Check if a foreign httpd holds the port + $foreignHttpd = Get-Process httpd -ErrorAction SilentlyContinue + if ($foreignHttpd) { + Write-Host "[WARN] Обнаружен сторонний Apache (PID: $(($foreignHttpd | Select-Object -First 1).Id))" -ForegroundColor Yellow + Write-Host " Наш Apache: $httpdExe" -ForegroundColor Yellow + } +} + +Write-Host "Запуск Apache..." +Start-Process -FilePath $httpdExe -WorkingDirectory $ApachePath -WindowStyle Hidden + +Start-Sleep -Seconds 2 + +$httpdCheck = Get-OurHttpd +if ($httpdCheck) { + Write-Host "Apache запущен (PID: $(($httpdCheck | Select-Object -First 1).Id))" -ForegroundColor Green +} else { + Write-Host "Apache не удалось запустить" -ForegroundColor Red + # Run config test for diagnostics + $testResult = & $httpdExe -t 2>&1 + if ($testResult) { + Write-Host "--- httpd -t ---" -ForegroundColor Yellow + $testResult | ForEach-Object { Write-Host " $_" } + } + $errorLog = Join-Path (Join-Path $ApachePath "logs") "error.log" + if (Test-Path $errorLog) { + Write-Host "--- error.log (последние 10 строк) ---" -ForegroundColor Yellow + Get-Content $errorLog -Tail 10 + } + exit 1 +} + +# --- Result --- +Write-Host "" +Write-Host "=== Публикация готова ===" -ForegroundColor Green +Write-Host "URL: http://localhost:$Port/$AppName" -ForegroundColor Cyan +Write-Host "OData: http://localhost:$Port/$AppName/odata/standard.odata" -ForegroundColor Cyan +Write-Host "HTTP-сервисы: http://localhost:$Port/$AppName/hs//..." -ForegroundColor Cyan +Write-Host "Web-сервисы: http://localhost:$Port/$AppName/ws/<Имя>?wsdl" -ForegroundColor Cyan diff --git a/.claude/skills/web-stop/SKILL.md b/.claude/skills/web-stop/SKILL.md new file mode 100644 index 00000000..73d3ca6c --- /dev/null +++ b/.claude/skills/web-stop/SKILL.md @@ -0,0 +1,52 @@ +--- +name: web-stop +description: Остановка Apache HTTP Server. Используй когда пользователь просит остановить веб-сервер, Apache, прекратить веб-публикацию +argument-hint: "" +allowed-tools: + - Bash + - Read + - Glob +--- + +# /web-stop — Остановка Apache + +Останавливает Apache HTTP Server. Публикации сохраняются — при следующем `/web-publish` сервер запустится снова. + +## Usage + +``` +/web-stop +``` + +## Параметры подключения + +Прочитай `.v8-project.json` из корня проекта. Если задан `webPath` — используй как `-ApachePath`. +По умолчанию `tools/apache24` от корня проекта. + +## Команда + +```powershell +powershell.exe -NoProfile -File .claude/skills/web-stop/scripts/web-stop.ps1 <параметры> +``` + +### Параметры скрипта + +| Параметр | Обязательный | Описание | +|----------|:------------:|----------| +| `-ApachePath <путь>` | нет | Корень Apache (по умолчанию `tools/apache24`) | + +## После выполнения + +Предложи пользователю: +- **Перезапуск** — `/web-publish <база>` (повторный вызов поднимет Apache с существующими публикациями) +- **Удаление публикаций** — `/web-unpublish <имя>` или `/web-unpublish --all` + +## Примеры + +```powershell +# Остановить Apache +powershell.exe -NoProfile -File .claude/skills/web-stop/scripts/web-stop.ps1 + +# С указанием пути +powershell.exe -NoProfile -File .claude/skills/web-stop/scripts/web-stop.ps1 -ApachePath "C:\tools\apache24" +``` diff --git a/.claude/skills/web-stop/scripts/web-stop.ps1 b/.claude/skills/web-stop/scripts/web-stop.ps1 new file mode 100644 index 00000000..f8be0efd --- /dev/null +++ b/.claude/skills/web-stop/scripts/web-stop.ps1 @@ -0,0 +1,92 @@ +# web-stop v1.0 — Stop Apache HTTP Server +# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills +<# +.SYNOPSIS + Остановка Apache HTTP Server + +.DESCRIPTION + Останавливает Apache HTTP Server. Сначала пытается graceful shutdown, + при неудаче — принудительная остановка. + +.PARAMETER ApachePath + Корень Apache (по умолчанию tools\apache24) + +.EXAMPLE + .\web-stop.ps1 + +.EXAMPLE + .\web-stop.ps1 -ApachePath "C:\tools\apache24" +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory=$false)] + [string]$ApachePath +) + +$OutputEncoding = [System.Text.Encoding]::UTF8 +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 + +# --- Resolve ApachePath --- +if (-not $ApachePath) { + $projectRoot = (Get-Item $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + $ApachePath = Join-Path $projectRoot "tools\apache24" +} + +# --- Helper: filter httpd processes by our ApachePath --- +$httpdExe = Join-Path (Join-Path $ApachePath "bin") "httpd.exe" +$httpdExeNorm = (Resolve-Path $httpdExe -ErrorAction SilentlyContinue).Path +function Get-OurHttpd { + Get-Process httpd -ErrorAction SilentlyContinue | Where-Object { + try { $_.Path -eq $httpdExeNorm } catch { $false } + } +} + +# --- Check process (only our Apache) --- +$httpdProc = Get-OurHttpd +if (-not $httpdProc) { + $foreign = Get-Process httpd -ErrorAction SilentlyContinue + if ($foreign) { + Write-Host "Наш Apache не запущен" -ForegroundColor Yellow + Write-Host "[WARN] Обнаружен сторонний Apache (PID: $(($foreign | Select-Object -First 1).Id))" -ForegroundColor Yellow + } else { + Write-Host "Apache не запущен" -ForegroundColor Yellow + } + exit 0 +} + +$pids = ($httpdProc | ForEach-Object { $_.Id }) -join ", " +Write-Host "Останавливаю Apache (PID: $pids)..." + +# --- Stop our processes --- +$httpdProc | Stop-Process -Force -ErrorAction SilentlyContinue + +# --- Wait for shutdown --- +$maxWait = 5 +$elapsed = 0 +while ($elapsed -lt $maxWait) { + Start-Sleep -Seconds 1 + $elapsed++ + $check = Get-OurHttpd + if (-not $check) { + Write-Host "Apache остановлен" -ForegroundColor Green + Write-Host "Публикации сохранены. Перезапуск: /web-publish <база> Удаление: /web-unpublish --all" -ForegroundColor Gray + exit 0 + } +} + +# --- Fallback: force kill --- +$remaining = Get-OurHttpd +if ($remaining) { + Write-Host "Принудительная остановка..." -ForegroundColor Yellow + $remaining | Stop-Process -Force -ErrorAction SilentlyContinue + Start-Sleep -Seconds 1 + $final = Get-OurHttpd + if ($final) { + Write-Host "Error: не удалось остановить Apache" -ForegroundColor Red + exit 1 + } +} + +Write-Host "Apache остановлен" -ForegroundColor Green +Write-Host "Публикации сохранены. Перезапуск: /web-publish <база> Удаление: /web-unpublish --all" -ForegroundColor Gray diff --git a/.claude/skills/web-unpublish/SKILL.md b/.claude/skills/web-unpublish/SKILL.md new file mode 100644 index 00000000..89aebdbe --- /dev/null +++ b/.claude/skills/web-unpublish/SKILL.md @@ -0,0 +1,62 @@ +--- +name: web-unpublish +description: Удаление веб-публикации 1С из Apache. Используй когда пользователь просит убрать публикацию, удалить веб-доступ к базе +argument-hint: "" +allowed-tools: + - Bash + - Read + - Glob + - AskUserQuestion +--- + +# /web-unpublish — Удаление веб-публикации 1С из Apache + +Удаляет блок публикации из `httpd.conf` и каталог `publish/{appname}` внутри Apache. Если других публикаций не осталось — удаляет глобальный блок 1C и останавливает Apache. С флагом `--all` удаляет все публикации разом. + +> **Внимание:** этот навык управляет только веб-публикациями в Apache (блоки в `httpd.conf` + каталог `publish/`). Он **НЕ** удаляет каталоги проекта, `upload/`, базы данных или исходники. + +## Usage + +``` +/web-unpublish +/web-unpublish bpdemo +/web-unpublish --all +``` + +## Параметры подключения + +Прочитай `.v8-project.json` из корня проекта. Если задан `webPath` — используй как `-ApachePath`. +По умолчанию `tools/apache24` от корня проекта. + +Если пользователь не указал `appname` и не указал `--all`, выполни `/web-info` чтобы показать список публикаций и спроси какую удалить. + +Если пользователь просит удалить **все** публикации — используй `-All`. + +## Команда + +```powershell +powershell.exe -NoProfile -File .claude/skills/web-unpublish/scripts/web-unpublish.ps1 <параметры> +``` + +### Параметры скрипта + +| Параметр | Обязательный | Описание | +|----------|:------------:|----------| +| `-AppName <имя>` | * | Имя публикации | +| `-All` | * | Удалить все публикации | +| `-ApachePath <путь>` | нет | Корень Apache (по умолчанию `tools/apache24`) | + +> `*` — нужен либо `-AppName`, либо `-All` + +## Примеры + +```powershell +# Удалить одну публикацию +powershell.exe -NoProfile -File .claude/skills/web-unpublish/scripts/web-unpublish.ps1 -AppName "bpdemo" + +# Удалить все публикации +powershell.exe -NoProfile -File .claude/skills/web-unpublish/scripts/web-unpublish.ps1 -All + +# С указанием пути +powershell.exe -NoProfile -File .claude/skills/web-unpublish/scripts/web-unpublish.ps1 -AppName "mydb" -ApachePath "C:\tools\apache24" +``` diff --git a/.claude/skills/web-unpublish/scripts/web-unpublish.ps1 b/.claude/skills/web-unpublish/scripts/web-unpublish.ps1 new file mode 100644 index 00000000..479ec642 --- /dev/null +++ b/.claude/skills/web-unpublish/scripts/web-unpublish.ps1 @@ -0,0 +1,159 @@ +# web-unpublish v1.0 — Remove 1C web publication +# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills +<# +.SYNOPSIS + Удаление веб-публикации 1С из Apache + +.DESCRIPTION + Удаляет маркерный блок из httpd.conf и каталог публикации. + Если Apache запущен — перезапускает для применения. + С флагом -All удаляет все публикации и останавливает Apache. + +.PARAMETER AppName + Имя публикации (обязательный, если не указан -All) + +.PARAMETER ApachePath + Корень Apache (по умолчанию tools\apache24) + +.PARAMETER All + Удалить все публикации + +.EXAMPLE + .\web-unpublish.ps1 -AppName "mydb" + +.EXAMPLE + .\web-unpublish.ps1 -All + +.EXAMPLE + .\web-unpublish.ps1 -AppName "bpdemo" -ApachePath "C:\tools\apache24" +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory=$false)] + [string]$AppName, + + [Parameter(Mandatory=$false)] + [string]$ApachePath, + + [Parameter(Mandatory=$false)] + [switch]$All +) + +$OutputEncoding = [System.Text.Encoding]::UTF8 +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 + +# --- Resolve ApachePath --- +if (-not $ApachePath) { + $projectRoot = (Get-Item $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + $ApachePath = Join-Path $projectRoot "tools\apache24" +} + +# --- Validate params --- +if (-not $All -and -not $AppName) { + Write-Host "Error: укажите -AppName или -All" -ForegroundColor Red + exit 1 +} + +# --- Read httpd.conf --- +$confFile = Join-Path (Join-Path $ApachePath "conf") "httpd.conf" +if (-not (Test-Path $confFile)) { + Write-Host "Error: httpd.conf не найден: $confFile" -ForegroundColor Red + exit 1 +} + +$confContent = [System.IO.File]::ReadAllText($confFile) + +# --- Helper: our httpd process --- +$httpdExe = Join-Path (Join-Path $ApachePath "bin") "httpd.exe" +$httpdExeNorm = (Resolve-Path $httpdExe -ErrorAction SilentlyContinue).Path +function Get-OurHttpd { + Get-Process httpd -ErrorAction SilentlyContinue | Where-Object { + try { $_.Path -eq $httpdExeNorm } catch { $false } + } +} + +# --- Collect app names to remove --- +if ($All) { + $pubPattern = '# --- 1C Publication: (.+?) ---' + $pubMatches = [regex]::Matches($confContent, $pubPattern) + if ($pubMatches.Count -eq 0) { + Write-Host "Нет публикаций для удаления" -ForegroundColor Yellow + exit 0 + } + $appNames = @() + foreach ($m in $pubMatches) { $appNames += $m.Groups[1].Value } + Write-Host "Удаление всех публикаций: $($appNames -join ', ')" -ForegroundColor Cyan +} else { + $appNames = @($AppName) +} + +# --- Remove marker blocks --- +foreach ($name in $appNames) { + $pubMarkerStart = "# --- 1C Publication: $name ---" + $pubMarkerEnd = "# --- End: $name ---" + + if ($confContent -match [regex]::Escape($pubMarkerStart)) { + $pattern = '\r?\n?' + [regex]::Escape($pubMarkerStart) + '[\s\S]*?' + [regex]::Escape($pubMarkerEnd) + '\r?\n?' + $confContent = [regex]::Replace($confContent, $pattern, "`n") + Write-Host "httpd.conf: блок публикации '$name' удалён" -ForegroundColor Green + } else { + Write-Host "Публикация '$name' не найдена в httpd.conf" -ForegroundColor Yellow + } +} + +# --- Check if any publications remain; if not, remove global block --- +$remainingPubs = [regex]::Matches($confContent, '# --- 1C Publication: .+? ---') +if ($remainingPubs.Count -eq 0) { + $globalMarkerStart = "# --- 1C: global ---" + $globalMarkerEnd = "# --- End: global ---" + if ($confContent -match [regex]::Escape($globalMarkerStart)) { + $globalPattern = '\r?\n?' + [regex]::Escape($globalMarkerStart) + '[\s\S]*?' + [regex]::Escape($globalMarkerEnd) + '\r?\n?' + $confContent = [regex]::Replace($confContent, $globalPattern, "`n") + Write-Host "httpd.conf: глобальный блок 1C удалён (нет публикаций)" -ForegroundColor Green + } +} + +[System.IO.File]::WriteAllText($confFile, $confContent) + +# --- Remove publish directories --- +foreach ($name in $appNames) { + $publishDir = Join-Path (Join-Path $ApachePath "publish") $name + if (Test-Path $publishDir) { + Remove-Item $publishDir -Recurse -Force + Write-Host "Каталог удалён: $publishDir" -ForegroundColor Green + } else { + Write-Host "Каталог не найден: $publishDir" -ForegroundColor Yellow + } +} + +# --- Restart/Stop Apache if running (only our instance) --- +$httpdProc = Get-OurHttpd +if ($httpdProc) { + if ($remainingPubs.Count -gt 0) { + Write-Host "Перезапуск Apache..." + $httpdProc | Stop-Process -Force -ErrorAction SilentlyContinue + Start-Sleep -Seconds 1 + Start-Process -FilePath $httpdExe -WorkingDirectory $ApachePath -WindowStyle Hidden + Start-Sleep -Seconds 2 + $check = Get-OurHttpd + if ($check) { + Write-Host "Apache перезапущен" -ForegroundColor Green + } else { + Write-Host "Error: Apache не удалось перезапустить" -ForegroundColor Red + exit 1 + } + } else { + Write-Host "Публикаций не осталось — останавливаю Apache..." + $httpdProc | Stop-Process -Force -ErrorAction SilentlyContinue + Start-Sleep -Seconds 1 + Write-Host "Apache остановлен" -ForegroundColor Green + } +} + +Write-Host "" +if ($All) { + Write-Host "Все публикации удалены ($($appNames.Count) шт.)" -ForegroundColor Green +} else { + Write-Host "Публикация '$AppName' удалена" -ForegroundColor Green +} diff --git a/.gitignore b/.gitignore index 2d82609f..79321f31 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,8 @@ test-tmp/ # Локальные настройки Claude Code .claude/settings.local.json +# Инструменты (portable Apache и т.д.) +tools/ + # Локальный реестр баз данных 1С .v8-project.json diff --git a/README.md b/README.md index 6f4f372f..300b7298 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ | Подсистемы (Subsystem) | 4 навыка `/subsystem-*` | Анализ, создание, редактирование, валидация подсистем конфигурации | [Подробнее](docs/subsystem-guide.md) | | Командный интерфейс (CI) | 2 навыка `/interface-*` | Редактирование и валидация CommandInterface.xml подсистем | [Подробнее](docs/subsystem-guide.md) | | Базы данных (DB) | 9 навыков `/db-*` | Создание баз, загрузка/выгрузка конфигураций, обновление БД, загрузка из Git | [Подробнее](docs/db-guide.md) | +| Веб-публикация (Web) | 4 навыка `/web-*` | Публикация баз через Apache, статус, остановка, удаление публикаций | [Подробнее](docs/web-guide.md) | | Утилиты | `/img-grid` | Наложение сетки на изображение для определения пропорций колонок | — | ## Требования @@ -61,6 +62,7 @@ - [Подсистемы и командный интерфейс](docs/1c-subsystem-spec.md) — XML-формат подсистем, CommandInterface.xml, секции видимости/размещения/порядка - [Корневая конфигурация](docs/1c-configuration-spec.md) — XML-формат Configuration.xml, ConfigDumpInfo.xml, Languages/, 44 типа ChildObjects - [Расширения конфигурации (CFE)](docs/1c-extension-spec.md) — XML-формат выгрузки расширений конфигурации +- [Веб-публикация 1С](docs/web-spec.md) — VRD, httpd.conf, wsap24.dll, portable Apache ## Структура репозитория @@ -127,6 +129,10 @@ ├── db-update/ # Обновление конфигурации БД ├── db-run/ # Запуск 1С:Предприятие ├── db-load-git/ # Загрузка изменений из Git +├── web-publish/ # Публикация базы через Apache +├── web-info/ # Статус Apache и публикаций +├── web-stop/ # Остановка Apache +├── web-unpublish/ # Удаление публикации └── img-grid/ # Сетка для анализа изображений docs/ ├── epf-guide.md # Гайд: внешние обработки и отчёты @@ -139,6 +145,7 @@ docs/ ├── cfe-guide.md # Гайд: расширения конфигурации (CFE) ├── subsystem-guide.md # Гайд: подсистемы и командный интерфейс ├── db-guide.md # Гайд: базы данных 1С +├── web-guide.md # Гайд: веб-публикация через Apache ├── 1c-epf-spec.md # Спецификация XML-формата (EPF) ├── 1c-erf-spec.md # Спецификация XML-формата (ERF) ├── 1c-form-spec.md # Спецификация управляемых форм @@ -154,5 +161,6 @@ docs/ ├── skd-dsl-spec.md # Спецификация SKD DSL ├── role-dsl-spec.md # Спецификация Role DSL ├── 1c-extension-spec.md # Спецификация расширений конфигурации (CFE) -└── 1c-subsystem-spec.md # Спецификация подсистем и командного интерфейса +├── 1c-subsystem-spec.md # Спецификация подсистем и командного интерфейса +└── web-spec.md # Спецификация веб-публикации (VRD, httpd.conf, Apache) ``` diff --git a/docs/web-guide.md b/docs/web-guide.md new file mode 100644 index 00000000..49d2e83d --- /dev/null +++ b/docs/web-guide.md @@ -0,0 +1,164 @@ +# Веб-публикация 1С + +Навыки группы `/web-*` позволяют публиковать информационные базы 1С через Apache HTTP Server для доступа из браузера (веб-клиент). Это замыкает цикл разработки: правка исходников → загрузка → обновление БД → **открытие в браузере**. + +## Навыки + +| Навык | Скрипт | Описание | +|-------|:------:|----------| +| `/web-publish` | `.ps1` | Публикация базы (VRD + httpd.conf + запуск Apache) | +| `/web-info` | `.ps1` | Статус Apache и список публикаций | +| `/web-stop` | `.ps1` | Остановка Apache | +| `/web-unpublish` | `.ps1` | Удаление публикации (одной или всех `--all`) | + +## Рабочий цикл + +``` +правки в исходниках → /db-load-xml → /db-update → /web-publish → браузер + ↑ + /web-info ← статус | + /web-stop ← остановка| + /web-unpublish ← удаление +``` + +### Типичный цикл разработки + +1. **Настройка** — `/db-list add` зарегистрировать базу в `.v8-project.json` +2. **Загрузка** — `/db-load-xml` загрузить конфигурацию +3. **Обновление** — `/db-update` применить к БД +4. **Публикация** — `/web-publish` опубликовать базу (при первом запуске скачает Apache) +5. **Проверка** — открыть `http://localhost:8081/{appname}` в браузере +6. **Правка** — изменить исходники +7. **Синхронизация** — `/db-load-git` + `/db-update` +8. **Проверка** — обновить страницу в браузере +9. **Завершение** — `/web-stop` остановить Apache + +## Конфигурация `webPath` в `.v8-project.json` + +Поле `webPath` указывает путь к каталогу Apache. Если не задано, используется `tools/apache24` от корня проекта. + +```json +{ + "v8path": "C:\\Program Files\\1cv8\\8.3.24.1691\\bin", + "webPath": "C:\\tools\\apache24", + "databases": [ + { + "id": "dev", + "name": "Разработка", + "type": "file", + "path": "C:\\Bases\\MyApp_Dev", + "user": "Admin" + } + ], + "default": "dev" +} +``` + +> `webPath` — опционально. Если не задан, все навыки `/web-*` ищут Apache в `tools/apache24`. + +## Сценарии использования + +### Опубликовать базу + +``` +> Опубликуй базу bp-demo +``` + +Claude вызовет `/web-publish bp-demo` → скачает Apache (если нет) → сгенерирует VRD → настроит httpd.conf → запустит Apache → выдаст URL. + +### Проверить статус + +``` +> Что с веб-сервером? +``` + +Claude вызовет `/web-info` → покажет состояние Apache, порт, список публикаций, последние ошибки. + +### Опубликовать на другом порту + +``` +> Опубликуй ERP на порту 9090 +``` + +Claude вызовет `/web-publish erp --port 9090`. + +### Сменить пользователя публикации + +``` +> Переключи публикацию bp-demo на пользователя Иванов +``` + +Claude вызовет `/web-publish bp-demo` с `-UserName "Иванов"` — перезапишет VRD с новым пользователем. + +### Параллельные публикации под разными пользователями + +``` +> Добавь ещё одну публикацию bp-demo под Ивановым, Admin оставь +``` + +Claude вызовет `/web-publish bp-demo --appname bpdemo-ivanov --user Иванов` — создаст вторую публикацию. Обе будут доступны одновременно по разным URL. + +### Остановить сервер + +``` +> Останови Apache +``` + +Claude вызовет `/web-stop`. + +### Удалить публикацию + +``` +> Убери публикацию bpdemo +``` + +Claude вызовет `/web-unpublish bpdemo` → удалит блок из httpd.conf → удалит каталог → перезапустит Apache (если есть другие публикации). + +### Удалить все публикации + +``` +> Удали все веб-публикации +``` + +Claude вызовет `/web-unpublish --all` → удалит все блоки из httpd.conf → удалит все каталоги → остановит Apache. + +## Работа с сервисами + +### Протестировать HTTP-сервис +``` +> Вызови HTTP-сервис ОбменДанными метод ПолучитьСтатус +``` + +Claude составит curl/Invoke-WebRequest к `http://localhost:8081/appname/hs/exchange/status` + +### Проверить OData +``` +> Покажи список справочников через OData +``` + +Claude вызовет `http://localhost:8081/appname/odata/standard.odata/$metadata` + +### Получить WSDL web-сервиса +``` +> Покажи WSDL сервиса ОбменСВнешнимиСистемами +``` + +Claude откроет `http://localhost:8081/appname/ws/ExchangeWithExternalSystems?wsdl` + +## Ручная установка Apache + +Если автоматическая загрузка невозможна (прокси, firewall), используйте флаг `--manual`: + +``` +> Опубликуй базу --manual +``` + +Скрипт выдаст инструкцию: +1. Скачайте Apache Lounge (x64 VS17) с https://www.apachelounge.com/download/ +2. Распакуйте содержимое `Apache24/` в `tools/apache24` (или путь из `webPath`) +3. Запустите команду повторно + +## Спецификации + +- [web-spec.md](web-spec.md) — техническая спецификация (VRD, httpd.conf, wsap24.dll, portable Apache) +- [build-spec.md](build-spec.md) — пакетный режим конфигуратора 1С diff --git a/docs/web-spec.md b/docs/web-spec.md new file mode 100644 index 00000000..4f22ef7a --- /dev/null +++ b/docs/web-spec.md @@ -0,0 +1,201 @@ +# Веб-публикация 1С — техническая спецификация + +Описание артефактов, необходимых для публикации информационной базы 1С через Apache HTTP Server. + +## default.vrd + +Дескриптор виртуального ресурса. XML-файл, описывающий подключение к информационной базе. + +### Формат + +```xml + + + + + +``` + +### Атрибут `base` + +URL-путь публикации. Должен начинаться с `/`, совпадает с `Alias` в httpd.conf. + +### Атрибут `ib` + +Строка подключения к информационной базе. + +**Файловая база:** +``` +File="C:\Bases\MyDB"; +``` + +**Серверная база:** +``` +Srvr="server01";Ref="MyDB"; +``` + +**С авторизацией:** +``` +File="C:\Bases\MyDB";Usr="Admin";Pwd="123"; +``` + +> Кавычки внутри значения `ib` экранируются как `"` (XML-сущность). + +### Дочерние элементы + +#### `enableStandardOdata` (атрибут ``) +Стандартный OData-интерфейс платформы. `enableStandardOdata="true"` открывает REST-доступ ко всем объектам. +URL: `/{AppName}/odata/standard.odata` + +#### `` +Публикация SOAP web-сервисов. `pointEnableCommon="true"` публикует все сервисы из конфигурации. +URL: `/{AppName}/ws/{WebServiceName}?wsdl` + +#### `` +Публикация HTTP-сервисов. `publishByDefault="true"` публикует все сервисы из конфигурации. +URL: `/{AppName}/hs/{RootUrl}/...` + +### Расположение + +`{ApachePath}/publish/{AppName}/default.vrd` + +## httpd.conf для 1С + +### LoadModule + +Apache загружает модуль расширения 1С: + +```apache +LoadModule _1cws_module "C:/Program Files/1cv8/8.3.24.1691/bin/wsap24.dll" +``` + +- Модуль `wsap24.dll` — 64-разрядный, требует x64-версию Apache +- Путь использует forward slashes + +### Listen + +```apache +Listen 8081 +``` + +Порт для веб-клиента. По умолчанию `8081` (стандартный `80` может быть занят). + +### Alias + Directory + +Для каждой публикации добавляется блок: + +```apache +Alias "/appname" "C:/path/to/apache/publish/appname" + + AllowOverride All + Require all granted + SetHandler 1c-application + ManagedApplicationDescriptor "C:/path/to/apache/publish/appname/default.vrd" + +``` + +- `Alias` — URL-путь → физический каталог +- `SetHandler 1c-application` — делегирование обработки запросов модулю wsap24 +- `ManagedApplicationDescriptor` — путь к default.vrd + +### Маркерный подход + +Скрипты используют маркерные комментарии для идемпотентного управления блоками: + +```apache +# --- 1C: global --- +Listen 8081 +LoadModule _1cws_module "C:/Program Files/1cv8/8.3.24.1691/bin/wsap24.dll" +# --- End: global --- + +# --- 1C Publication: mydb --- +Alias "/mydb" "C:/tools/apache24/publish/mydb" + + AllowOverride All + Require all granted + SetHandler 1c-application + ManagedApplicationDescriptor "C:/tools/apache24/publish/mydb/default.vrd" + +# --- End: mydb --- +``` + +При повторном запуске блок между маркерами заменяется целиком. + +## wsap24.dll + +Модуль расширения Apache для 1С:Предприятие 8.3. + +- Расположение: `{V8Path}/wsap24.dll` (в каталоге `bin` платформы) +- Архитектура: x64 (Apache тоже должен быть x64) +- Имя модуля: `_1cws_module` + +## Portable Apache + +### Дистрибутив + +Apache Lounge — Windows-сборка Apache HTTP Server (x64): +- Сайт: `https://www.apachelounge.com/download/` +- Прямая ссылка (2.4.62, VS17): `https://www.apachelounge.com/download/VS17/binaries/httpd-2.4.62-240904-win64-VS17.zip` +- Внутри ZIP: каталог `Apache24/` с полной структурой + +### Структура после установки + +``` +tools/apache24/ +├── bin/ +│ ├── httpd.exe +│ └── ... +├── conf/ +│ ├── httpd.conf +│ └── ... +├── logs/ +│ ├── error.log +│ └── access.log +├── modules/ +│ └── ... +└── publish/ + └── {appname}/ + └── default.vrd +``` + +### Пост-распаковка + +1. `Expand-Archive` распаковывает ZIP во временный каталог +2. Содержимое `Apache24/` перемещается в `{ApachePath}` +3. В `httpd.conf` патчится `ServerRoot`: + +```apache +Define SRVROOT "C:/path/to/apache24" +ServerRoot "${SRVROOT}" +``` + +Путь `SRVROOT` — абсолютный, с forward slashes. + +### Запуск + +``` +httpd.exe # foreground (для отладки) +httpd.exe -k start # фоновый запуск (не работает без установки сервиса) +``` + +> Portable Apache запускается напрямую через `Start-Process httpd.exe` без установки Windows-сервиса. + +### Остановка + +``` +httpd.exe -k stop # graceful shutdown (требует сервис) +Stop-Process -Name httpd # принудительная остановка (portable) +``` + +### Перезагрузка + +``` +httpd.exe -k restart # graceful restart (требует сервис) +``` + +Для portable варианта: остановка + запуск.