Spaces:
Sleeping
Sleeping
| param( | |
| [string]$BackendHost = "127.0.0.1", | |
| [int]$BackendPort = 8008, | |
| [int]$FrontendPort = 5173, | |
| [int]$HealthTimeoutSeconds = 90, | |
| [string]$PythonExe = "", | |
| [ValidateSet("web", "electron")][string]$FrontendMode = "web", | |
| [switch]$SkipFrontend, | |
| [switch]$RequireA1111 | |
| ) | |
| $ErrorActionPreference = "Stop" | |
| $projectRoot = Split-Path -Parent $PSScriptRoot | |
| Set-Location $projectRoot | |
| function Resolve-PythonExe { | |
| param([string]$Override) | |
| if ($Override -and (Test-Path $Override)) { | |
| return (Resolve-Path $Override).Path | |
| } | |
| $candidates = @( | |
| (Join-Path $projectRoot ".venv\Scripts\python.exe"), | |
| (Join-Path (Split-Path -Parent $projectRoot) ".venv\Scripts\python.exe") | |
| ) | |
| foreach ($candidate in $candidates) { | |
| if (Test-Path $candidate) { | |
| return (Resolve-Path $candidate).Path | |
| } | |
| } | |
| throw "Python executable not found. Use -PythonExe <path-to-python.exe>." | |
| } | |
| function Stop-PortListeners { | |
| param([int[]]$Ports) | |
| foreach ($port in $Ports) { | |
| $listeners = Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue | |
| foreach ($listener in $listeners) { | |
| try { | |
| Stop-Process -Id $listener.OwningProcess -Force -ErrorAction Stop | |
| Write-Host "Stopped PID $($listener.OwningProcess) on port $port" | |
| } | |
| catch { | |
| Write-Host "Could not stop PID $($listener.OwningProcess) on port $port" | |
| } | |
| } | |
| } | |
| } | |
| function Wait-Http { | |
| param( | |
| [Parameter(Mandatory = $true)][string]$Url, | |
| [Parameter(Mandatory = $true)][string]$Name, | |
| [Parameter(Mandatory = $true)][int]$TimeoutSeconds | |
| ) | |
| $deadline = (Get-Date).AddSeconds($TimeoutSeconds) | |
| do { | |
| try { | |
| $response = Invoke-WebRequest -Uri $Url -UseBasicParsing -TimeoutSec 5 | |
| if ($response.StatusCode -ge 200 -and $response.StatusCode -lt 500) { | |
| Write-Host "OK $Name -> $Url" | |
| return | |
| } | |
| } | |
| catch { | |
| Start-Sleep -Milliseconds 800 | |
| } | |
| } while ((Get-Date) -lt $deadline) | |
| throw "$Name not reachable within ${TimeoutSeconds}s: $Url" | |
| } | |
| $python = Resolve-PythonExe -Override $PythonExe | |
| $logsDir = Join-Path $projectRoot "logs" | |
| New-Item -ItemType Directory -Force -Path $logsDir | Out-Null | |
| $backendLog = Join-Path $logsDir "backend.log" | |
| $backendErrLog = Join-Path $logsDir "backend.err.log" | |
| $frontendLog = Join-Path $logsDir "frontend.log" | |
| $frontendErrLog = Join-Path $logsDir "frontend.err.log" | |
| Stop-PortListeners -Ports @($BackendPort, $FrontendPort) | |
| $backendCmd = @( | |
| "Set-Location '$projectRoot'", | |
| "`$env:IMAGEFORGE_HOST='$BackendHost'", | |
| "`$env:IMAGEFORGE_PORT='$BackendPort'", | |
| "`$env:IMAGEFORGE_CORS_ORIGINS='http://localhost:$FrontendPort,http://127.0.0.1:$FrontendPort'", | |
| "& '$python' -m backend.app.main" | |
| ) -join "; " | |
| $backendProc = Start-Process -FilePath "powershell.exe" -ArgumentList @( | |
| "-NoProfile", | |
| "-ExecutionPolicy", "Bypass", | |
| "-Command", $backendCmd | |
| ) -RedirectStandardOutput $backendLog -RedirectStandardError $backendErrLog -PassThru | |
| Write-Host "Backend started (PID $($backendProc.Id))." | |
| $frontendProc = $null | |
| if (-not $SkipFrontend.IsPresent) { | |
| $frontendScript = if ($FrontendMode -eq "electron") { "dev" } else { "dev:web" } | |
| $frontendCheckUrl = if ($FrontendMode -eq "electron") { "http://localhost:$FrontendPort" } else { "http://127.0.0.1:$FrontendPort" } | |
| $frontendCmd = @( | |
| "Set-Location '$projectRoot'", | |
| "npm --prefix frontend run $frontendScript" | |
| ) -join "; " | |
| $frontendProc = Start-Process -FilePath "powershell.exe" -ArgumentList @( | |
| "-NoProfile", | |
| "-ExecutionPolicy", "Bypass", | |
| "-Command", $frontendCmd | |
| ) -RedirectStandardOutput $frontendLog -RedirectStandardError $frontendErrLog -PassThru | |
| Write-Host "Frontend started (PID $($frontendProc.Id))." | |
| Wait-Http -Url $frontendCheckUrl -Name "Frontend" -TimeoutSeconds $HealthTimeoutSeconds | |
| } | |
| $healthArgs = @( | |
| "-BackendUrl", "http://$BackendHost`:$BackendPort", | |
| "-TimeoutSeconds", "$HealthTimeoutSeconds" | |
| ) | |
| if ($RequireA1111.IsPresent) { | |
| $healthArgs += "-RequireA1111" | |
| } | |
| & (Join-Path $PSScriptRoot "healthcheck-stack.ps1") @healthArgs | |
| Write-Host "" | |
| Write-Host "Ready:" | |
| Write-Host "- Backend: http://$BackendHost`:$BackendPort" | |
| if (-not $SkipFrontend.IsPresent) { | |
| if ($FrontendMode -eq "electron") { | |
| Write-Host "- Frontend mode: electron (with embedded Vite on $FrontendPort)" | |
| } | |
| else { | |
| Write-Host "- Frontend (Web): http://localhost:$FrontendPort" | |
| } | |
| } | |
| Write-Host "- Backend log: $backendLog" | |
| Write-Host "- Backend error log: $backendErrLog" | |
| if (-not $SkipFrontend.IsPresent) { | |
| Write-Host "- Frontend log: $frontendLog" | |
| Write-Host "- Frontend error log: $frontendErrLog" | |
| } |