mirror of
https://github.com/hansjone/netx.git
synced 2026-10-15 03:40:51 +08:00
- Start: WinSW's NetX.out.log/err.log in data\runtime are the same files as start_netx.ps1's netx.out.log (case-insensitive) and stay open, so NetX could not truncate its log and every start failed. WinSW logs now go to data\runtime\service. - Stop: nothing told the service_run.ps1 supervisor to exit, so it kept restarting NetX and WinSW waited forever. stop_netx_app.ps1 writes the service.stop flag (-KeepSupervisor for the supervisor's own restarts), service_run checks it every second, and runtime_ctl / service_lifecycle write it before stopping the service. - WinSW launched powershell.exe as stop hook never reached the script (stuck on inherited std handles); run it via cmd with NUL stdin and output to data\runtime\service\stopexec.log. stop_netx_app.ps1 keeps a timestamped trace there. Offline Windows Sandbox: service install -> healthy in 28s, stop in 20s. Existing service installs need "install service" re-run to pick this up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
109 lines
3.6 KiB
PowerShell
109 lines
3.6 KiB
PowerShell
param(
|
|
[string]$ProgramRoot = "",
|
|
[string]$DataRoot = "",
|
|
[int]$HealthFailLimit = 6,
|
|
[int]$HealthIntervalSec = 10
|
|
)
|
|
|
|
# Long-running supervisor for Windows Service / Task Scheduler.
|
|
# Starts NetX, probes /health; repeated failures trigger restart (or exit for WinSW).
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
. "$PSScriptRoot\_common.ps1"
|
|
|
|
$prog = Get-NetxProgramRoot -Override $ProgramRoot
|
|
$data = Get-NetxDataRoot -ProgramRoot $prog -Override $DataRoot
|
|
$logDir = Join-Path $data "data\runtime"
|
|
if (-not (Test-Path $logDir)) {
|
|
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
|
|
}
|
|
$logFile = Join-Path $logDir "service_run.log"
|
|
$stopFlag = Join-Path $logDir "service.stop"
|
|
|
|
function Write-SvcLog([string]$Msg) {
|
|
$line = "{0} {1}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Msg
|
|
Add-Content -Path $logFile -Value $line -Encoding utf8
|
|
Write-Host $line
|
|
}
|
|
|
|
function Get-BindInfo {
|
|
$envPath = Join-Path $data ".env"
|
|
$hostBind = "127.0.0.1"
|
|
$port = 8890
|
|
if (Test-Path $envPath) {
|
|
$map = Read-DotEnv -Path $envPath
|
|
if ($map["NETX_HOST"]) { $hostBind = $map["NETX_HOST"] }
|
|
if ($map["NETX_PORT"]) { try { $port = [int]$map["NETX_PORT"] } catch {} }
|
|
}
|
|
return @{ Host = $hostBind; Port = $port }
|
|
}
|
|
|
|
Remove-Item -Force $stopFlag -ErrorAction SilentlyContinue
|
|
Write-SvcLog "service_run starting program=$prog data=$data"
|
|
|
|
$startPs1 = Join-Path $PSScriptRoot "start_netx_app.ps1"
|
|
$stopPs1 = Join-Path $PSScriptRoot "stop_netx_app.ps1"
|
|
$bind = Get-BindInfo
|
|
$failStreak = 0
|
|
$restartCount = 0
|
|
|
|
function Start-NetxChildren {
|
|
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $startPs1 `
|
|
-ProgramRoot $prog -DataRoot $data -SkipBrowser -Force
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "start_netx_app_failed exit=$LASTEXITCODE"
|
|
}
|
|
}
|
|
|
|
function Stop-NetxChildren {
|
|
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $stopPs1 `
|
|
-ProgramRoot $prog -DataRoot $data -KeepSupervisor
|
|
}
|
|
|
|
function Wait-NetxStopFlag([int]$Seconds) {
|
|
# Sleep in 1s steps so a service stop (stop flag) is noticed quickly.
|
|
for ($i = 0; $i -lt $Seconds; $i++) {
|
|
if (Test-Path $stopFlag) { return $true }
|
|
Start-Sleep -Seconds 1
|
|
}
|
|
return (Test-Path $stopFlag)
|
|
}
|
|
|
|
try {
|
|
Start-NetxChildren
|
|
Write-SvcLog "NetX started; health watch host=$($bind.Host) port=$($bind.Port)"
|
|
|
|
while ($true) {
|
|
if (Test-Path $stopFlag) {
|
|
Write-SvcLog "stop flag detected"
|
|
break
|
|
}
|
|
|
|
if (Test-NetxApiHealthy -HostName $bind.Host -Port $bind.Port -TimeoutSec 3) {
|
|
$failStreak = 0
|
|
} else {
|
|
$failStreak++
|
|
Write-SvcLog "health fail streak=$failStreak/$HealthFailLimit"
|
|
if ($failStreak -ge $HealthFailLimit) {
|
|
$restartCount++
|
|
Write-SvcLog "restarting NetX (attempt=$restartCount)"
|
|
try { Stop-NetxChildren } catch { Write-SvcLog "stop warning: $($_.Exception.Message)" }
|
|
Start-Sleep -Seconds 3
|
|
Start-NetxChildren
|
|
$failStreak = 0
|
|
# Give API time to come up before counting failures again.
|
|
$null = Wait-NetxStopFlag ([Math]::Max(15, $HealthIntervalSec))
|
|
continue
|
|
}
|
|
}
|
|
$null = Wait-NetxStopFlag $HealthIntervalSec
|
|
}
|
|
} catch {
|
|
Write-SvcLog "ERROR: $($_.Exception.Message)"
|
|
throw
|
|
} finally {
|
|
Write-SvcLog "stopping NetX"
|
|
try { Stop-NetxChildren } catch { Write-SvcLog "stop warning: $($_.Exception.Message)" }
|
|
Remove-Item -Force $stopFlag -ErrorAction SilentlyContinue
|
|
Write-SvcLog "service_run exited"
|
|
}
|