fix(web-info, web-publish): Listen с адресом привязки

web-info брал первое число строки Listen: при `Listen 127.0.0.1:19081`
порт выходил 127, URL и проверка TCP были ложными. Теперь разбирается
`Listen [host:]port [proto]` (включая IPv6 в скобках), сначала в своём
глобальном блоке; явный адрес идёт в URL и в пробу порта.

web-publish при перезаписи глобального блока сохраняет адрес привязки,
вписанный вручную, — повторная публикация больше не открывает loopback
на все интерфейсы; URL в выводе строятся по нему. Стандартный Listen
комментируется целой строкой (раньше `Listen 0.0.0.0:80` превращался
в мусор, а `Listen [::]:80` оставался активным).

Refs #107

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-09-26 14:27:18 +03:00
co-authored by Claude Opus 5.5
parent 614206bc5c
commit 933414f2e6
5 changed files with 81 additions and 29 deletions
+26 -8
View File
@@ -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
+18 -7
View File
@@ -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 ---