<# AZA – Clean Local Start (no Docker) Purpose: - Ensure port 8000 is free (kills existing listener on :8000) - Load auth tokens from deploy\.env - Ensure AZA_SECRET_KEY is set (from .env or auto-generated for this run) - Start the backend deterministically: python -m uvicorn workforce_planner.api.app:app --host 127.0.0.1 --port 8000 Run (from project root): powershell -ExecutionPolicy Bypass -File .\deploy\local_reset_and_start.ps1 Then (in a second terminal): cd .\deploy powershell -ExecutionPolicy Bypass -File .\authorized_test.ps1 #> [CmdletBinding()] param( [string]$ProjectRoot = ".", [string]$EnvFile = ".\deploy\.env", [string]$BindHost = "127.0.0.1", [int]$Port = 8000 ) function Load-DotEnv([string]$Path) { if (-not (Test-Path -LiteralPath $Path)) { throw "Missing .env file at: $Path" } $map = @{} Get-Content -LiteralPath $Path | ForEach-Object { $line = $_.Trim() if ($line.Length -eq 0) { return } if ($line.StartsWith("#")) { return } $idx = $line.IndexOf("=") if ($idx -lt 1) { return } $k = $line.Substring(0, $idx).Trim() $v = $line.Substring($idx + 1).Trim() if (($v.StartsWith('"') -and $v.EndsWith('"')) -or ($v.StartsWith("'") -and $v.EndsWith("'"))) { $v = $v.Substring(1, $v.Length - 2) } $map[$k] = $v } return $map } function Kill-PortListener([int]$p) { $pids = @() # Preferred (more reliable): Get-NetTCPConnection try { $conns = Get-NetTCPConnection -LocalPort $p -State Listen -ErrorAction Stop foreach ($c in $conns) { if ($c.OwningProcess -and $c.OwningProcess -is [int]) { $pids += $c.OwningProcess } elseif ($c.OwningProcess -and ($c.OwningProcess.ToString() -match "^\d+$")) { $pids += [int]$c.OwningProcess } } } catch { # Fallback: netstat parsing try { $lines = & netstat -ano | Select-String -Pattern (":$p\s") | ForEach-Object { $_.Line } foreach ($ln in $lines) { $parts = ($ln -split "\s+") | Where-Object { $_ -ne "" } if ($parts.Count -ge 5) { $state = $parts[3] $listenerPid = $parts[4] if ($state -eq "LISTENING" -and $listenerPid -match "^\d+$") { $pids += [int]$listenerPid } } } } catch { } } $pids = $pids | Select-Object -Unique if (-not $pids -or $pids.Count -eq 0) { Write-Host "Port ${p}: no LISTENING pid found." return } foreach ($procId in $pids) { try { Write-Host "Killing PID $procId on port ${p} ..." & taskkill /PID $procId /F | Out-Null } catch { Write-Host "WARNING: failed to kill PID $procId" } } Start-Sleep -Milliseconds 600 # Re-check try { $still = Get-NetTCPConnection -LocalPort $p -State Listen -ErrorAction SilentlyContinue if ($still) { $stillPids = @($still | ForEach-Object { $_.OwningProcess } | Select-Object -Unique) Write-Host "WARNING: Port ${p} still has listener(s): $($stillPids -join ', ')" } else { Write-Host "Port ${p}: listener cleared." } } catch { # ignore } } Set-Location -LiteralPath $ProjectRoot Write-Host "[AZA] Clean Local Start" Write-Host " Root: $(Get-Location)" Write-Host " Env: $EnvFile" Write-Host " Bind: http://$BindHost`:$Port" Write-Host "" # 1) Free the port Kill-PortListener -p $Port Write-Host "" # 2) Load .env try { $envMap = Load-DotEnv $EnvFile } catch { Write-Host "ERROR: $($_.Exception.Message)" exit 1 } # 3) Auth tokens (do NOT print them) if ($envMap.ContainsKey("MEDWORK_API_TOKENS")) { $env:MEDWORK_API_TOKENS = $envMap["MEDWORK_API_TOKENS"] Write-Host " MEDWORK_API_TOKENS: set" # Compatibility: some startup checks still require MEDWORK_API_TOKEN (singular). $first = ($envMap["MEDWORK_API_TOKENS"] -split "[,\r\n]+" | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" } | Select-Object -First 1) if ($first) { $env:MEDWORK_API_TOKEN = $first Write-Host " MEDWORK_API_TOKEN: set (first token, compatibility)" } } elseif ($envMap.ContainsKey("MEDWORK_API_TOKEN")) { $env:MEDWORK_API_TOKEN = $envMap["MEDWORK_API_TOKEN"] Write-Host " MEDWORK_API_TOKEN: set" $first = $envMap["MEDWORK_API_TOKEN"].Trim() } else { Write-Host "ERROR: No MEDWORK_API_TOKENS or MEDWORK_API_TOKEN found in $EnvFile" exit 1 } # Ensure backend_token.txt matches the token used for local runs (some code paths may read this file). try { if ($first -and $first.Trim().Length -gt 0) { $tokenFile = ".\backend_token.txt" $desired = $first.Trim() + "`n" $existing = "" if (Test-Path -LiteralPath $tokenFile) { try { $existing = (Get-Content -LiteralPath $tokenFile -Raw) } catch { $existing = "" } if ($existing -ne $desired) { $bak = ".\backend_token.txt.bak" try { Copy-Item -LiteralPath $tokenFile -Destination $bak -Force | Out-Null } catch { } } } Set-Content -LiteralPath $tokenFile -Value $desired -Encoding ASCII Write-Host " backend_token.txt: synced (local auth consistency)" } } catch { Write-Host "WARN: Could not write backend_token.txt (continuing)." } # 4) Secret key (hard guarantee for local run) if ($envMap.ContainsKey("AZA_SECRET_KEY") -and $envMap["AZA_SECRET_KEY"].Trim().Length -ge 32) { $env:AZA_SECRET_KEY = $envMap["AZA_SECRET_KEY"].Trim() Write-Host " AZA_SECRET_KEY: set (from .env)" } else { $gen = & python -c "import secrets; print(secrets.token_hex(64))" if (-not $gen -or $gen.Trim().Length -lt 32) { Write-Host "ERROR: Could not generate AZA_SECRET_KEY via python." exit 1 } $env:AZA_SECRET_KEY = $gen.Trim() Write-Host " AZA_SECRET_KEY: generated for this local run" } # Optional: build label if present if ($envMap.ContainsKey("AZA_BUILD")) { $env:AZA_BUILD = $envMap["AZA_BUILD"] } Write-Host "" Write-Host "Starting backend:" Write-Host " python -m uvicorn backend_main:app --host $BindHost --port $Port" Write-Host "" Write-Host "NOTE: This window must stay open while you run authorized_test.ps1 in a second terminal." Write-Host "" # 5) Optionally launch Dev Status window $devStatusScript = ".\tools\dev_status_window.py" $devStatusEnabled = $true if ($envMap.ContainsKey("DEV_STATUS_WINDOW") -and $envMap["DEV_STATUS_WINDOW"] -eq "0") { $devStatusEnabled = $false } if ($devStatusEnabled -and (Test-Path -LiteralPath $devStatusScript)) { $Root = (Get-Location).Path Start-Process -FilePath "powershell.exe" -ArgumentList @("-NoExit","-ExecutionPolicy","Bypass","-Command","`$host.ui.RawUI.WindowTitle = 'AZA Dev Status'; cd `"$Root`"; python .\tools\dev_status_window.py") -WindowStyle Normal Write-Host " Dev status window: started" } elseif ($devStatusEnabled) { Write-Host " Dev status window: missing ($devStatusScript not found)" } else { Write-Host " Dev status window: disabled" } # 6) Start AZA backend (foreground) $target = "backend_main:app" Write-Host "Uvicorn target: $target" Write-Host "" & python -m uvicorn $target --host $BindHost --port $Port exit $LASTEXITCODE