Auto-build: codeassistant (python) from fc2d460

This commit is contained in:
github-actions[bot]
2026-08-08 12:48:45 +00:00
commit f1633a9e3d
322 changed files with 166457 additions and 0 deletions
@@ -0,0 +1,197 @@
# web-info v1.3 — 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]
}
# Проверяем именно TCP-порт: запрос к публикации поднял бы сеанс 1С и занял лицензию
$portState = ""
if ($port -ne "") {
$client = New-Object System.Net.Sockets.TcpClient
try {
$async = $client.BeginConnect("127.0.0.1", [int]$port, $null, $null)
if ($async.AsyncWaitHandle.WaitOne(1000) -and $client.Connected) {
$portState = " (слушается)"
} else {
$portState = " (не отвечает)"
}
} catch {
$portState = " (не отвечает)"
} finally {
$client.Close()
}
}
Write-Host "Port: $port$portState"
# 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 '&quot;','"'
}
}
# Detect published services
$svcTags = @()
if (Test-Path $vrdPath) {
# "+ext" — publishExtensionsByDefault: сервисы расширений тоже опубликованы
if ($vrdContent -match '<ws\s[^>]*>') {
$wsTag = "WS"
if ($Matches[0] -match 'publishExtensionsByDefault\s*=\s*"true"') { $wsTag += "+ext" }
$svcTags += $wsTag
}
if ($vrdContent -match '<httpServices\s[^>]*>') {
$hsTag = "HTTP"
if ($Matches[0] -match 'publishExtensionsByDefault\s*=\s*"true"') { $hsTag += "+ext" }
$svcTags += $hsTag
}
# Актуальная форма — <standardOdata enable="true"/>; enableStandardOdata — до 8.3.9
if ($vrdContent -match '<standardOdata\s[^>]*enable\s*=\s*"true"' -or
$vrdContent -match 'enableStandardOdata\s*=\s*"true"') { $svcTags += "OData" }
}
$svcLabel = if ($svcTags.Count -gt 0) { " [" + ($svcTags -join " ") + "]" } else { "" }
$url = "http://localhost:$port/$appName"
Write-Host " $appName" -ForegroundColor White -NoNewline
Write-Host " $url" -ForegroundColor Gray -NoNewline
Write-Host " $ibInfo" -ForegroundColor DarkGray -NoNewline
Write-Host $svcLabel -ForegroundColor DarkCyan
}
}
# --- Error log ---
Write-Host ""
Write-Host "=== Последние ошибки ===" -ForegroundColor Cyan
# Имя файла берём из httpd.conf: сборки Apache расходятся (error.log / error_log)
$errorLog = $null
if ($confContent -match '(?m)^\s*ErrorLog\s+"?([^"\r\n]+)"?') {
$logPath = $Matches[1].Trim()
if ([System.IO.Path]::IsPathRooted($logPath)) {
$errorLog = $logPath
} else {
$errorLog = Join-Path $ApachePath ($logPath -replace '/','\')
}
}
if (-not $errorLog -or -not (Test-Path $errorLog)) {
$errorLog = @("error_log", "error.log") |
ForEach-Object { Join-Path (Join-Path $ApachePath "logs") $_ } |
Where-Object { Test-Path $_ } |
Select-Object -First 1
}
if ($errorLog -and (Test-Path $errorLog)) {
Write-Host "Журнал: $errorLog" -ForegroundColor DarkGray
# Только error и выше: warn забит штатным шумом winnt_accept, notice — строки старта.
# AH02538 — след нашего же рестарта (родитель убит), пишется как crit при каждой публикации
$lines = Get-Content $errorLog -ErrorAction SilentlyContinue |
Where-Object { $_ -match '\[[a-z_]+:(error|crit|alert|emerg)\]' -and $_ -notmatch 'AH02538' } |
Select-Object -Last 5
if ($lines) {
foreach ($line in $lines) {
Write-Host " $line" -ForegroundColor DarkGray
}
} else {
Write-Host "(ошибок нет)" -ForegroundColor Green
}
} else {
Write-Host "(нет файла)" -ForegroundColor Green
}
@@ -0,0 +1,192 @@
#!/usr/bin/env python3
# web-info v1.3 — Apache & 1C publication status
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
"""
Статус Apache HTTP Server и публикаций 1С.
Показывает состояние Apache, список опубликованных баз
и последние ошибки из error.log.
"""
import argparse
import os
import re
import socket
import sys
import psutil
def get_httpd_by_exe(httpd_exe_norm):
"""Get httpd processes matching our exe path."""
ours = []
foreign = []
for p in psutil.process_iter(['pid', 'name', 'exe']):
try:
if p.info['name'] and 'httpd' in p.info['name'].lower():
if p.info['exe'] and os.path.normcase(os.path.normpath(p.info['exe'])) == httpd_exe_norm:
ours.append(p)
else:
foreign.append(p)
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
return ours, foreign
def main():
sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")
parser = argparse.ArgumentParser(description='Apache & 1C publication status', allow_abbrev=False)
parser.add_argument('-ApachePath', type=str, default='', help='Apache root (default: tools\\apache24)')
args = parser.parse_args()
# --- Resolve ApachePath ---
apache_path = args.ApachePath
if not apache_path:
script_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(script_dir))))
apache_path = os.path.join(project_root, 'tools', 'apache24')
# --- Check Apache installation ---
httpd_exe = os.path.join(apache_path, 'bin', 'httpd.exe')
print('=== Apache Web Server ===')
if not os.path.exists(httpd_exe):
print('Status: Не установлен')
print(f'Path: {apache_path} (не найден)')
print('')
print('Используйте /web-publish для установки Apache.')
sys.exit(0)
# --- Check process (only our Apache) ---
httpd_exe_norm = os.path.normcase(os.path.normpath(os.path.realpath(httpd_exe)))
our_proc, foreign_proc = get_httpd_by_exe(httpd_exe_norm)
if our_proc:
pids = ', '.join(str(p.pid) for p in our_proc)
print(f'Status: Запущен (PID: {pids})')
else:
print('Status: Остановлен')
if foreign_proc:
fp = foreign_proc[0]
try:
fpath = fp.info['exe'] or '?'
except Exception:
fpath = '?'
print(f'[WARN] Обнаружен сторонний Apache (PID: {fp.pid}, {fpath})')
print(f'Path: {apache_path}')
# --- Parse httpd.conf ---
conf_file = os.path.join(apache_path, 'conf', 'httpd.conf')
if not os.path.exists(conf_file):
print('Config: httpd.conf не найден')
sys.exit(0)
with open(conf_file, 'r', encoding='utf-8-sig') as f:
conf_content = f.read()
# Extract port from global block
port = '\u2014'
m = re.search(r'(?m)^Listen\s+(\d+)', conf_content)
if m:
port = m.group(1)
# Проверяем именно TCP-порт: запрос к публикации поднял бы сеанс 1С и занял лицензию
port_state = ''
if port != '':
try:
with socket.create_connection(('127.0.0.1', int(port)), timeout=1):
port_state = ' (слушается)'
except OSError:
port_state = ' (не отвечает)'
print(f'Port: {port}{port_state}')
# Extract wsap24 path
m = re.search(r'LoadModule\s+_1cws_module\s+"([^"]+)"', conf_content)
if m:
print(f'Module: {m.group(1)}')
# --- Publications ---
print('')
print('=== Опубликованные базы ===')
pub_pattern = r'# --- 1C Publication: (.+?) ---'
pub_matches = re.findall(pub_pattern, conf_content)
if not pub_matches:
print('(нет публикаций)')
else:
for app_name in pub_matches:
# Read default.vrd for this publication
vrd_path = os.path.join(apache_path, 'publish', app_name, 'default.vrd')
ib_info = '\u2014'
vrd_content = ''
if os.path.exists(vrd_path):
with open(vrd_path, 'r', encoding='utf-8-sig') as f:
vrd_content = f.read()
m = re.search(r'ib="([^"]*)"', vrd_content)
if m:
ib_info = m.group(1).replace('&quot;', '"')
# Detect published services
svc_tags = []
if vrd_content:
# "+ext" — publishExtensionsByDefault: сервисы расширений тоже опубликованы
m_ws = re.search(r'<ws\s[^>]*>', vrd_content)
if m_ws:
svc_tags.append('WS+ext' if re.search(
r'publishExtensionsByDefault\s*=\s*"true"', m_ws.group(0)) else 'WS')
m_hs = re.search(r'<httpServices\s[^>]*>', vrd_content)
if m_hs:
svc_tags.append('HTTP+ext' if re.search(
r'publishExtensionsByDefault\s*=\s*"true"', m_hs.group(0)) else 'HTTP')
# Актуальная форма — <standardOdata enable="true"/>; enableStandardOdata — до 8.3.9
if (re.search(r'<standardOdata\s[^>]*enable\s*=\s*"true"', vrd_content)
or re.search(r'enableStandardOdata\s*=\s*"true"', vrd_content)):
svc_tags.append('OData')
svc_label = ' [' + ' '.join(svc_tags) + ']' if svc_tags else ''
url = f'http://localhost:{port}/{app_name}'
print(f' {app_name} {url} {ib_info}{svc_label}')
# --- Error log ---
print('')
print('=== Последние ошибки ===')
# Имя файла берём из httpd.conf: сборки Apache расходятся (error.log / error_log)
error_log = None
m_log = re.search(r'(?m)^\s*ErrorLog\s+"?([^"\r\n]+)"?', conf_content)
if m_log:
log_path = m_log.group(1).strip()
error_log = os.path.normpath(
log_path if os.path.isabs(log_path) else os.path.join(apache_path, log_path))
if not error_log or not os.path.exists(error_log):
error_log = next((p for p in (os.path.join(apache_path, 'logs', n)
for n in ('error_log', 'error.log'))
if os.path.exists(p)), None)
if error_log and os.path.exists(error_log):
print(f'Журнал: {error_log}')
try:
with open(error_log, 'r', encoding='utf-8-sig', errors='replace') as f:
all_lines = f.readlines()
# Только error и выше: warn забит штатным шумом winnt_accept, notice — строки старта.
# AH02538 — след нашего же рестарта (родитель убит), пишется как crit при каждой публикации
err_lines = [ln for ln in all_lines
if re.search(r'\[[a-z_]+:(error|crit|alert|emerg)\]', ln)
and 'AH02538' not in ln]
if err_lines:
for line in err_lines[-5:]:
print(f' {line.rstrip()}')
else:
print('(ошибок нет)')
except Exception:
print('(ошибка чтения)')
else:
print('(нет файла)')
if __name__ == '__main__':
main()