docs: update production workflow design and runtime changes
This commit is contained in:
+207
-1
@@ -29,6 +29,14 @@ $CondaEnvName = ""
|
||||
$NginxExe = "C:/nginx-1.29.4/nginx.exe"
|
||||
$ServerHost = ""
|
||||
$ServerPort = 18000
|
||||
$NginxAllowedClientIps = ""
|
||||
$BackendReadyTimeoutSeconds = 120
|
||||
$TileServerAutoStart = $false
|
||||
$TileServerAutoStop = $true
|
||||
$TileServerRoot = ""
|
||||
$TileServerStartScript = "start-all.bat"
|
||||
$TileServerStopScript = "stop-all.bat"
|
||||
$TileServerUrl = ""
|
||||
|
||||
$envLines = Get-Content -LiteralPath "$EnvPath"
|
||||
foreach ($line in $envLines) {
|
||||
@@ -43,7 +51,18 @@ foreach ($line in $envLines) {
|
||||
if ($key -eq "CONDA_EXE") { if ($val) { $CondaExe = $val } }
|
||||
if ($key -eq "CONDA_ENV_NAME") { if ($val) { $CondaEnvName = $val } }
|
||||
if ($key -eq "NGINX_PATH") { if ($val) { $NginxExe = $val } }
|
||||
if ($key -eq "NGINX_ALLOWED_CLIENT_IPS") { $NginxAllowedClientIps = $val }
|
||||
if ($key -eq "BACKEND_BIND_HOST") { if ($val) { $ServerHost = $val } }
|
||||
if ($key -eq "BACKEND_READY_TIMEOUT_SECONDS") {
|
||||
$parsed = 0
|
||||
if ([int]::TryParse($val, [ref]$parsed) -and $parsed -gt 0) { $BackendReadyTimeoutSeconds = $parsed }
|
||||
}
|
||||
if ($key -eq "TILE_SERVER_AUTO_START") { $TileServerAutoStart = $val -match '^(?i)(true|1|yes|on)$' }
|
||||
if ($key -eq "TILE_SERVER_AUTO_STOP") { $TileServerAutoStop = -not ($val -match '^(?i)(false|0|no|off)$') }
|
||||
if ($key -eq "TILE_SERVER_ROOT") { if ($val) { $TileServerRoot = $val } }
|
||||
if ($key -eq "TILE_SERVER_START_SCRIPT") { if ($val) { $TileServerStartScript = $val } }
|
||||
if ($key -eq "TILE_SERVER_STOP_SCRIPT") { if ($val) { $TileServerStopScript = $val } }
|
||||
if ($key -eq "VITE_TILE_SERVER_URL") { if ($val) { $TileServerUrl = $val.TrimEnd("/") } }
|
||||
if ($key -eq "PORT") {
|
||||
$parsed = 0
|
||||
if ([int]::TryParse($val, [ref]$parsed)) { $ServerPort = $parsed }
|
||||
@@ -315,11 +334,140 @@ function Test-PortAvailable {
|
||||
}
|
||||
}
|
||||
|
||||
function Write-NginxClientAllowFile {
|
||||
param([string]$Path)
|
||||
|
||||
$entries = @()
|
||||
$raw = "$NginxAllowedClientIps".Trim()
|
||||
if ($raw) {
|
||||
$entries = @($raw -split '[;,\s]+' | ForEach-Object { "$_".Trim() } | Where-Object { $_ })
|
||||
}
|
||||
|
||||
$lines = @(
|
||||
"# Generated by scripts/start_app.ps1.",
|
||||
"# Configure NGINX_ALLOWED_CLIENT_IPS in .env. Empty means allow all clients."
|
||||
)
|
||||
|
||||
if ($entries.Count -gt 0) {
|
||||
$allowed = @("127.0.0.1", "::1") + $entries
|
||||
$allowed = @($allowed | Sort-Object -Unique)
|
||||
foreach ($item in $allowed) {
|
||||
$lines += " allow $item;"
|
||||
}
|
||||
$lines += " deny all;"
|
||||
}
|
||||
|
||||
$dir = Split-Path -Parent "$Path"
|
||||
if (-not (Test-Path -LiteralPath "$dir")) {
|
||||
New-Item -ItemType Directory -Path "$dir" -Force | Out-Null
|
||||
}
|
||||
$Utf8NoBom = New-Object System.Text.UTF8Encoding $false
|
||||
[System.IO.File]::WriteAllText("$Path", (($lines -join [Environment]::NewLine) + [Environment]::NewLine), $Utf8NoBom)
|
||||
|
||||
if ($entries.Count -gt 0) {
|
||||
Write-Host ">>> Nginx client IP whitelist enabled: $($entries -join ', ')" -ForegroundColor Yellow
|
||||
} else {
|
||||
Write-Host ">>> Nginx client IP whitelist disabled." -ForegroundColor DarkGray
|
||||
}
|
||||
}
|
||||
|
||||
function Test-TileServerReady {
|
||||
if (-not $TileServerUrl) {
|
||||
return $false
|
||||
}
|
||||
try {
|
||||
$response = Invoke-WebRequest -Uri "$TileServerUrl/health" -UseBasicParsing -TimeoutSec 2 -ErrorAction Stop
|
||||
return ($response.StatusCode -ge 200 -and $response.StatusCode -lt 500)
|
||||
} catch {
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
function Get-TileServerScriptPath {
|
||||
param([string]$ScriptName)
|
||||
|
||||
$root = "$TileServerRoot".Trim().Trim('"').Trim("'")
|
||||
if (-not $root) {
|
||||
return $null
|
||||
}
|
||||
$script = "$ScriptName".Trim().Trim('"').Trim("'")
|
||||
if (-not $script) {
|
||||
return $null
|
||||
}
|
||||
if ([System.IO.Path]::IsPathRooted($script)) {
|
||||
return $script
|
||||
}
|
||||
return (Join-Path -Path $root -ChildPath $script)
|
||||
}
|
||||
|
||||
function Stop-TileServer {
|
||||
if (-not $TileServerAutoStop) {
|
||||
return
|
||||
}
|
||||
$stopScript = Get-TileServerScriptPath -ScriptName "$TileServerStopScript"
|
||||
if (-not $stopScript -or -not (Test-Path -LiteralPath "$stopScript")) {
|
||||
return
|
||||
}
|
||||
Write-Host ">>> Stopping tile-server..." -ForegroundColor Yellow
|
||||
$previousNoPause = $env:NO_PAUSE
|
||||
try {
|
||||
$env:NO_PAUSE = "1"
|
||||
& "$stopScript"
|
||||
} catch {
|
||||
Write-Warning "tile-server stop failed: $($_.Exception.Message)"
|
||||
} finally {
|
||||
if ($null -eq $previousNoPause) {
|
||||
Remove-Item Env:\NO_PAUSE -ErrorAction SilentlyContinue
|
||||
} else {
|
||||
$env:NO_PAUSE = $previousNoPause
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Start-TileServer {
|
||||
if (-not $TileServerAutoStart) {
|
||||
return
|
||||
}
|
||||
if (Test-TileServerReady) {
|
||||
Write-Host ">>> tile-server already responding: $TileServerUrl" -ForegroundColor Green
|
||||
return
|
||||
}
|
||||
$startScript = Get-TileServerScriptPath -ScriptName "$TileServerStartScript"
|
||||
if (-not $startScript -or -not (Test-Path -LiteralPath "$startScript")) {
|
||||
Write-Error "tile-server start script not found. TILE_SERVER_ROOT=$TileServerRoot TILE_SERVER_START_SCRIPT=$TileServerStartScript"
|
||||
$global:LASTEXITCODE = 1
|
||||
return
|
||||
}
|
||||
Write-Host ">>> Launching tile-server..." -ForegroundColor Green
|
||||
$previousNoPause = $env:NO_PAUSE
|
||||
try {
|
||||
$env:NO_PAUSE = "1"
|
||||
& "$startScript"
|
||||
} finally {
|
||||
if ($null -eq $previousNoPause) {
|
||||
Remove-Item Env:\NO_PAUSE -ErrorAction SilentlyContinue
|
||||
} else {
|
||||
$env:NO_PAUSE = $previousNoPause
|
||||
}
|
||||
}
|
||||
for ($i = 0; $i -lt 20; $i++) {
|
||||
if (Test-TileServerReady) {
|
||||
Write-Host ">>> tile-server ready: $TileServerUrl" -ForegroundColor Green
|
||||
return
|
||||
}
|
||||
Start-Sleep -Milliseconds 500
|
||||
}
|
||||
Write-Warning "tile-server was started but did not pass health check: $TileServerUrl/health"
|
||||
}
|
||||
|
||||
Stop-Backend-By-Cmdline -MatchText "run_backend.py"
|
||||
Stop-Backend-By-Cmdline -MatchText "run_worker.py"
|
||||
$NginxProcName = Split-Path -Leaf $NginxExe
|
||||
$NginxProcName = $NginxProcName -replace '\.exe$', ''
|
||||
Stop-Process-By-Name -Name $NginxProcName -ExeName $NginxExe
|
||||
if ($TileServerAutoStart -and $TileServerAutoStop) {
|
||||
Stop-TileServer
|
||||
}
|
||||
|
||||
$PortAvailable = Test-PortAvailable -Port $ServerPort
|
||||
if (-not $PortAvailable) {
|
||||
@@ -455,6 +603,38 @@ function Assert-ProcessAlive {
|
||||
return $true
|
||||
}
|
||||
|
||||
function Wait-BackendReady {
|
||||
param(
|
||||
[int]$Port,
|
||||
[int]$TimeoutSeconds
|
||||
)
|
||||
|
||||
$deadline = (Get-Date).AddSeconds([Math]::Max(1, $TimeoutSeconds))
|
||||
# Use the lightweight root route for readiness. /api/health runs a full
|
||||
# operational self-check and can legitimately take longer during startup.
|
||||
$url = "http://127.0.0.1:$Port/"
|
||||
$lastError = $null
|
||||
|
||||
Write-Host ">>> Waiting for backend readiness: $url" -ForegroundColor Yellow
|
||||
while ((Get-Date) -lt $deadline) {
|
||||
try {
|
||||
$response = Invoke-WebRequest -Uri "$url" -UseBasicParsing -TimeoutSec 3 -ErrorAction Stop
|
||||
if ($response.StatusCode -ge 200 -and $response.StatusCode -lt 500) {
|
||||
Write-Host ">>> Backend ready (status=$($response.StatusCode))." -ForegroundColor Green
|
||||
return $true
|
||||
}
|
||||
$lastError = "HTTP $($response.StatusCode)"
|
||||
} catch {
|
||||
$lastError = $_.Exception.Message
|
||||
}
|
||||
Start-Sleep -Milliseconds 800
|
||||
}
|
||||
|
||||
Write-Error "Backend did not become ready within $TimeoutSeconds seconds. Last error: $lastError"
|
||||
$global:LASTEXITCODE = 1
|
||||
return $false
|
||||
}
|
||||
|
||||
Invoke-PythonScript -ScriptPath "$CheckRuntimeScript"
|
||||
if ($LastExitCode -ne 0) {
|
||||
Write-Host "`n[ERROR] Deployment configuration check failed." -ForegroundColor Red
|
||||
@@ -506,16 +686,29 @@ if (Test-Path -LiteralPath "$NginxConfPath") {
|
||||
$ForwardRoot = $ProjectRoot.Replace([char]92, [char]47)
|
||||
$FrontendDist = "$ForwardRoot/frontend/dist"
|
||||
$ImageCache = "$ForwardRoot/backend/image_cache"
|
||||
$NginxClientAllowFile = Join-Path -Path $ProjectRoot -ChildPath "nginx\client_allow.conf"
|
||||
|
||||
if (-not (Test-Path -LiteralPath "$ProjectRoot/backend/image_cache")) {
|
||||
New-Item -ItemType Directory -Path "$ProjectRoot/backend/image_cache" -Force | Out-Null
|
||||
}
|
||||
Write-NginxClientAllowFile -Path "$NginxClientAllowFile"
|
||||
|
||||
$ConfContent = Get-Content -LiteralPath "$NginxConfPath" -Raw
|
||||
$NewConfContent = $ConfContent -replace 'root\s+[^;]+;', "root `"$FrontendDist`";"
|
||||
$NewConfContent = $NewConfContent -replace 'alias\s+[^;]+;', "alias `"$ImageCache/`";"
|
||||
$ClientAllowForwardPath = "$ForwardRoot/nginx/client_allow.conf"
|
||||
$NewConfContent = $NewConfContent -replace 'include\s+"[^"]*client_allow\.conf";', "include `"$ClientAllowForwardPath`";"
|
||||
$BackendProxy = "http://127.0.0.1:$ServerPort"
|
||||
$NewConfContent = $NewConfContent -replace 'proxy_pass\s+http://(127\.0\.0\.1|localhost):\d+;', "proxy_pass $BackendProxy;"
|
||||
$NewConfContent = [regex]::Replace(
|
||||
$NewConfContent,
|
||||
'(location\s+/api/\s*\{[\s\S]*?proxy_pass\s+)http://(127\.0\.0\.1|localhost):\d+(;)',
|
||||
"`${1}$BackendProxy`${3}"
|
||||
)
|
||||
$NewConfContent = [regex]::Replace(
|
||||
$NewConfContent,
|
||||
'(location\s+/api/tasks/active/stream\s*\{[\s\S]*?proxy_pass\s+)http://(127\.0\.0\.1|localhost):\d+(;)',
|
||||
"`${1}$BackendProxy`${3}"
|
||||
)
|
||||
# 使用 UTF8 无 BOM 编码写入
|
||||
$Utf8NoBom = New-Object System.Text.UTF8Encoding $false
|
||||
[System.IO.File]::WriteAllText("$NginxConfPath", $NewConfContent, $Utf8NoBom)
|
||||
@@ -533,6 +726,9 @@ $BackendProc = Start-PythonBackground -ScriptPath "run_backend.py"
|
||||
if (-not (Assert-ProcessAlive -Process $BackendProc -DisplayName "Backend")) {
|
||||
return
|
||||
}
|
||||
if (-not (Wait-BackendReady -Port $ServerPort -TimeoutSeconds $BackendReadyTimeoutSeconds)) {
|
||||
return
|
||||
}
|
||||
|
||||
# 6.5 Start job worker
|
||||
Write-Host ">>> Launching job worker..." -ForegroundColor Green
|
||||
@@ -541,6 +737,12 @@ if (-not (Assert-ProcessAlive -Process $WorkerProc -DisplayName "Worker")) {
|
||||
return
|
||||
}
|
||||
|
||||
# 6.6 Start tile-server
|
||||
Start-TileServer
|
||||
if ($global:LASTEXITCODE -eq 1) {
|
||||
return
|
||||
}
|
||||
|
||||
# 7. Start Nginx
|
||||
if (Test-Path -LiteralPath "$NginxExe") {
|
||||
Write-Host ">>> Launching Nginx..." -ForegroundColor Green
|
||||
@@ -575,6 +777,9 @@ if (Test-Path -LiteralPath "$NginxExe") {
|
||||
Write-StatusLine "Frontend (via Nginx): http://$DisplayHost"
|
||||
Write-StatusLine "Backend (internal): http://127.0.0.1`:$ServerPort"
|
||||
Write-StatusLine "API Docs (internal): http://127.0.0.1`:$ServerPort/docs"
|
||||
if ($TileServerAutoStart -and $TileServerUrl) {
|
||||
Write-StatusLine "Tile Server: $TileServerUrl"
|
||||
}
|
||||
Write-StatusLine "============================================================" Green
|
||||
Write-Host ""
|
||||
Write-StatusLine "System is running. Press Ctrl+C to stop all services." Yellow
|
||||
@@ -599,5 +804,6 @@ try {
|
||||
Stop-Backend-By-Cmdline -MatchText "run_backend.py"
|
||||
Stop-Backend-By-Cmdline -MatchText "run_worker.py"
|
||||
Stop-Process-By-Name -Name $NginxProcName -ExeName $NginxExe
|
||||
Stop-TileServer
|
||||
Write-Host "Done." -ForegroundColor Green
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user