$ErrorActionPreference = 'Stop' $projectRoot = Split-Path -Parent $PSScriptRoot Set-Location -LiteralPath $projectRoot docker compose config --quiet if ($LASTEXITCODE -ne 0) { throw 'Docker Compose configuration is invalid.' } docker compose up --build -d if ($LASTEXITCODE -ne 0) { throw 'Docker Compose startup failed. Check the error above.' } # A rebuilt API container may have a new address while the web image is cached. docker compose exec -T web nginx -s reload if ($LASTEXITCODE -ne 0) { throw 'Could not refresh the web proxy after startup.' } function Get-LocalServiceURL([string]$Service, [int]$ContainerPort) { $binding = docker compose port $Service $ContainerPort | Select-Object -First 1 if ($LASTEXITCODE -ne 0 -or !$binding -or $binding -notmatch ':(\d+)$') { throw "No published port found for $Service." } return "http://localhost:$($Matches[1])" } $apiURL = Get-LocalServiceURL 'api' 8080 Write-Host 'Waiting for API readiness...' for ($attempt = 1; $attempt -le 60; $attempt++) { try { $response = Invoke-RestMethod -Uri "$apiURL/health/ready" -TimeoutSec 2 if ($response.data.status -eq 'ready') { Write-Host 'Local environment is ready:' Write-Host " Web $(Get-LocalServiceURL 'web' 8080)" Write-Host " API $apiURL" Write-Host " Callbacks $(Get-LocalServiceURL 'callbacks' 8081)" Write-Host " Worker $(Get-LocalServiceURL 'worker' 8082)" Write-Host " MinIO $(Get-LocalServiceURL 'minio' 9001)" Write-Host " Keycloak $(Get-LocalServiceURL 'keycloak' 8080)" exit 0 } } catch { Start-Sleep -Seconds 2 } } docker compose ps throw 'API did not become ready in time. Run docker compose logs api postgres redis migrate for details.'