diff --git a/.claude/skills/web-info/scripts/web-info.ps1 b/.claude/skills/web-info/scripts/web-info.ps1 index 2cb1d0f2f..9230ea8fd 100644 --- a/.claude/skills/web-info/scripts/web-info.ps1 +++ b/.claude/skills/web-info/scripts/web-info.ps1 @@ -1,4 +1,4 @@ -# web-info v1.5 — Apache & 1C publication status +# web-info v1.6 — Apache & 1C publication status # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills <# .SYNOPSIS @@ -77,17 +77,34 @@ if (-not (Test-Path $confFile)) { $confContent = [System.IO.File]::ReadAllText($confFile) -# Extract port from global block +# Extract Listen from global block (fallback — whole file): "Listen [host:]port [proto]" $port = "—" -if ($confContent -match '(?m)^Listen\s+(\d+)') { - $port = $Matches[1] +$listenHost = "" +$listenScope = $confContent +if ($confContent -match '(?s)# --- 1C: global ---(.*?)# --- End: global ---') { + $listenScope = $Matches[1] } +if ($listenScope -match '(?m)^[ \t]*Listen[ \t]+(?:(\[[^\]]+\]|[^\s:\[\]]+):)?(\d+)\b') { + $port = $Matches[2] + if ($Matches[1] -and @('0.0.0.0', '*', '[::]') -notcontains $Matches[1]) { + $listenHost = $Matches[1] + } +} +# Адрес привязки явный — по нему и URL, и проба; иначе все интерфейсы → localhost +$urlHost = if ($listenHost) { $listenHost } else { "localhost" } +$probeHost = if ($listenHost) { $listenHost.Trim('[', ']') } else { "127.0.0.1" } # Проверяем именно TCP-порт: запрос к публикации поднял бы сеанс 1С и занял лицензию $portState = "" if ($port -ne "—") { - $client = New-Object System.Net.Sockets.TcpClient + # Конструктор по умолчанию — только IPv4: для IPv6-литерала нужно семейство адреса + $probeAddr = $null + if ([System.Net.IPAddress]::TryParse($probeHost, [ref]$probeAddr)) { + $client = New-Object System.Net.Sockets.TcpClient($probeAddr.AddressFamily) + } else { + $client = New-Object System.Net.Sockets.TcpClient + } try { - $async = $client.BeginConnect("127.0.0.1", [int]$port, $null, $null) + $async = $client.BeginConnect($probeHost, [int]$port, $null, $null) if ($async.AsyncWaitHandle.WaitOne(1000) -and $client.Connected) { $portState = " (слушается)" } else { @@ -99,7 +116,8 @@ if ($port -ne "—") { $client.Close() } } -Write-Host "Port: $port$portState" +$portLabel = if ($listenHost) { "${listenHost}:$port" } else { $port } +Write-Host "Port: $portLabel$portState" # Extract wsap24 path if ($confContent -match 'LoadModule\s+_1cws_module\s+"([^"]+)"') { @@ -149,7 +167,7 @@ if ($pubMatches.Count -eq 0) { } $svcLabel = if ($svcTags.Count -gt 0) { " [" + ($svcTags -join " ") + "]" } else { "" } - $url = "http://localhost:$port/$appName" + $url = "http://${urlHost}:$port/$appName" Write-Host " $appName" -ForegroundColor White -NoNewline Write-Host " $url" -ForegroundColor Gray -NoNewline Write-Host " $ibInfo" -ForegroundColor DarkGray -NoNewline diff --git a/.claude/skills/web-info/scripts/web-info.py b/.claude/skills/web-info/scripts/web-info.py index 0e5e42d5f..1b66a8ed5 100644 --- a/.claude/skills/web-info/scripts/web-info.py +++ b/.claude/skills/web-info/scripts/web-info.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# web-info v1.5 — Apache & 1C publication status +# web-info v1.6 — Apache & 1C publication status # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills """ @@ -110,20 +110,31 @@ def main(): with open(conf_file, 'r', encoding='utf-8-sig') as f: conf_content = f.read() - # Extract port from global block + # Extract Listen from global block (fallback — whole file): "Listen [host:]port [proto]" port = '\u2014' - m = re.search(r'(?m)^Listen\s+(\d+)', conf_content) + listen_host = '' + listen_scope = conf_content + m = re.search(r'(?s)# --- 1C: global ---(.*?)# --- End: global ---', conf_content) if m: - port = m.group(1) + listen_scope = m.group(1) + m = re.search(r'(?mi)^[ \t]*Listen[ \t]+(?:(\[[^\]]+\]|[^\s:\[\]]+):)?(\d+)\b', listen_scope) + if m: + port = m.group(2) + if m.group(1) and m.group(1) not in ('0.0.0.0', '*', '[::]'): + listen_host = m.group(1) + # Адрес привязки явный — по нему и URL, и проба; иначе все интерфейсы → localhost + url_host = listen_host if listen_host else 'localhost' + probe_host = listen_host.strip('[]') if listen_host else '127.0.0.1' # Проверяем именно TCP-порт: запрос к публикации поднял бы сеанс 1С и занял лицензию port_state = '' if port != '—': try: - with socket.create_connection(('127.0.0.1', int(port)), timeout=1): + with socket.create_connection((probe_host, int(port)), timeout=1): port_state = ' (слушается)' except OSError: port_state = ' (не отвечает)' - print(f'Port: {port}{port_state}') + port_label = f'{listen_host}:{port}' if listen_host else port + print(f'Port: {port_label}{port_state}') # Extract wsap24 path m = re.search(r'LoadModule\s+_1cws_module\s+"([^"]+)"', conf_content) @@ -170,7 +181,7 @@ def main(): svc_tags.append('OData') svc_label = ' [' + ' '.join(svc_tags) + ']' if svc_tags else '' - url = f'http://localhost:{port}/{app_name}' + url = f'http://{url_host}:{port}/{app_name}' print(f' {app_name} {url} {ib_info}{svc_label}') # --- Error log --- diff --git a/.claude/skills/web-publish/scripts/web-publish.ps1 b/.claude/skills/web-publish/scripts/web-publish.ps1 index fb2a26c75..26b11c7ab 100644 --- a/.claude/skills/web-publish/scripts/web-publish.ps1 +++ b/.claude/skills/web-publish/scripts/web-publish.ps1 @@ -1,4 +1,4 @@ -# web-publish v1.11 — Publish 1C infobase via Apache (+_version_dir/_version_key: общий эталон db-семейства) +# web-publish v1.12 — Publish 1C infobase via Apache (+_version_dir/_version_key: общий эталон db-семейства) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills <# .SYNOPSIS @@ -349,9 +349,20 @@ $vrdPathFwd = $vrdPath -replace '\\','/' # --- Global block (Listen + LoadModule) --- $globalMarkerStart = "# --- 1C: global ---" $globalMarkerEnd = "# --- End: global ---" +# Адрес привязки, вписанный в наш блок руками (Listen 127.0.0.1:port), при перезаписи сохраняем +$listenHost = "" +$globalPattern = [regex]::Escape($globalMarkerStart) + '([\s\S]*?)' + [regex]::Escape($globalMarkerEnd) +if ($confContent -match $globalPattern -and + $Matches[1] -match '(?m)^[ \t]*Listen[ \t]+(?:(\[[^\]]+\]|[^\s:\[\]]+):)?(\d+)\b') { + if ($Matches[1] -and @('0.0.0.0', '*', '[::]') -notcontains $Matches[1]) { + $listenHost = $Matches[1] + } +} +$listenArg = if ($listenHost) { "${listenHost}:$Port" } else { "$Port" } +$urlHost = if ($listenHost) { $listenHost } else { "localhost" } $globalBlock = @" $globalMarkerStart -Listen $Port +Listen $listenArg LoadModule _1cws_module "$wsapDllFwd" $globalMarkerEnd "@ @@ -362,7 +373,7 @@ if ($confContent -match [regex]::Escape($globalMarkerStart)) { $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' + $confContent = $confContent -replace '(?m)^([ \t]*Listen\b[^\r\n]*)', '#$1 # commented by web-publish' # Append global block $confContent = $confContent.TrimEnd() + "`n`n" + $globalBlock + "`n" } @@ -460,7 +471,7 @@ if ($httpdCheck) { # --- 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 +Write-Host "URL: http://${urlHost}:$Port/$AppName" -ForegroundColor Cyan +Write-Host "OData: http://${urlHost}:$Port/$AppName/odata/standard.odata" -ForegroundColor Cyan +Write-Host "HTTP-сервисы: http://${urlHost}:$Port/$AppName/hs//..." -ForegroundColor Cyan +Write-Host "Web-сервисы: http://${urlHost}:$Port/$AppName/ws/<Имя>?wsdl" -ForegroundColor Cyan diff --git a/.claude/skills/web-publish/scripts/web-publish.py b/.claude/skills/web-publish/scripts/web-publish.py index e93418346..95af432d5 100644 --- a/.claude/skills/web-publish/scripts/web-publish.py +++ b/.claude/skills/web-publish/scripts/web-publish.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# web-publish v1.11 — Publish 1C infobase via Apache (+_version_dir/_version_key: общий эталон db-семейства) +# web-publish v1.12 — Publish 1C infobase via Apache (+_version_dir/_version_key: общий эталон db-семейства) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills """ @@ -408,9 +408,19 @@ def main(): # --- Global block (Listen + LoadModule) --- global_marker_start = '# --- 1C: global ---' global_marker_end = '# --- End: global ---' + # Адрес привязки, вписанный в наш блок руками (Listen 127.0.0.1:port), при перезаписи сохраняем + listen_host = '' + global_pattern = re.escape(global_marker_start) + r'([\s\S]*?)' + re.escape(global_marker_end) + m = re.search(global_pattern, conf_content) + if m: + m = re.search(r'(?mi)^[ \t]*Listen[ \t]+(?:(\[[^\]]+\]|[^\s:\[\]]+):)?(\d+)\b', m.group(1)) + if m and m.group(1) and m.group(1) not in ('0.0.0.0', '*', '[::]'): + listen_host = m.group(1) + listen_arg = f'{listen_host}:{port}' if listen_host else f'{port}' + url_host = listen_host if listen_host else 'localhost' global_block = ( f'{global_marker_start}\n' - f'Listen {port}\n' + f'Listen {listen_arg}\n' f'LoadModule _1cws_module "{wsap_dll_fwd}"\n' f'{global_marker_end}' ) @@ -421,7 +431,7 @@ def main(): conf_content = re.sub(pattern, global_block, conf_content) else: # Comment out default Listen to avoid port conflict - conf_content = re.sub(r'(?m)^(Listen\s+\d+)', r'#\1 # commented by web-publish', conf_content) + conf_content = re.sub(r'(?mi)^([ \t]*Listen\b[^\r\n]*)', r'#\1 # commented by web-publish', conf_content) # Append global block conf_content = conf_content.rstrip() + '\n\n' + global_block + '\n' @@ -536,10 +546,10 @@ def main(): # --- Result --- print('') print('=== Публикация готова ===') - print(f'URL: http://localhost:{port}/{app_name}') - print(f'OData: http://localhost:{port}/{app_name}/odata/standard.odata') - print(f'HTTP-сервисы: http://localhost:{port}/{app_name}/hs//...') - print(f'Web-сервисы: http://localhost:{port}/{app_name}/ws/<Имя>?wsdl') + print(f'URL: http://{url_host}:{port}/{app_name}') + print(f'OData: http://{url_host}:{port}/{app_name}/odata/standard.odata') + print(f'HTTP-сервисы: http://{url_host}:{port}/{app_name}/hs//...') + print(f'Web-сервисы: http://{url_host}:{port}/{app_name}/ws/<Имя>?wsdl') if __name__ == '__main__': diff --git a/docs/web-spec.md b/docs/web-spec.md index 0ee25ec81..01a407ec8 100644 --- a/docs/web-spec.md +++ b/docs/web-spec.md @@ -103,6 +103,8 @@ Listen 8081 Порт для веб-клиента. По умолчанию `8081` (стандартный `80` может быть занят). +Адрес привязки, вписанный в глобальный блок вручную (`Listen 127.0.0.1:8081`, `Listen [::1]:8081`), при повторной публикации сохраняется — меняется только порт; URL в выводе `web-publish` и `web-info` строятся по этому адресу. `0.0.0.0`, `*`, `[::]` считаются «все интерфейсы» → `localhost`. + ### Alias + Directory Для каждой публикации добавляется блок: