Files
2026-10-10 13:57:15 +05:30

205 lines
7.1 KiB
PowerShell

#requires -Version 5.1
<#
.SYNOPSIS
Set up, start and stop the entire TravelHub stack on Docker. A new developer
needs only Docker Desktop -- no local Go, Node, or PostgreSQL.
.DESCRIPTION
Wraps docker compose for the three services (PostgreSQL + Go API + Angular
via nginx). Database credentials are read from backend/.env, so there is one
source of truth and no secret is duplicated.
URLs once up:
App -> http://localhost:4200 (nginx serves the SPA and proxies /api)
API -> http://localhost:8080
Demo sign-in: any seeded account, password demo1234
(e.g. agent@skyline.test, agency "skyline").
.PARAMETER Action
setup Build images and bring everything up, waiting until healthy. The
database is created and seeded on this first run. One command to
onboard.
start Bring the stack up (images already built).
stop Stop and remove the containers; the database volume is kept.
restart stop then start.
reset Stop and DELETE the database volume, then start fresh (re-seeds).
rebuild Rebuild images (use after pulling code changes) and start.
status Show container state.
logs Tail logs (-Follow to stream; -Service to pick one).
.PARAMETER Service
logs: which service (postgres | api | web). Default: all.
.PARAMETER Follow
logs: stream instead of printing the tail once.
.EXAMPLE
.\docker.ps1 setup
.EXAMPLE
.\docker.ps1 logs -Service api -Follow
.EXAMPLE
.\docker.ps1 reset # wipe data and re-seed with fresh flight dates
#>
[CmdletBinding()]
param(
[Parameter(Position = 0)]
[ValidateSet('setup', 'start', 'stop', 'restart', 'reset', 'rebuild', 'status', 'logs', 'help')]
[string]$Action = 'help',
[ValidateSet('postgres', 'api', 'web')]
[string]$Service,
[switch]$Follow
)
$ErrorActionPreference = 'Stop'
$Root = $PSScriptRoot
# Refresh PATH from the registry so `docker` resolves even in a terminal opened
# before Docker Desktop was installed (a common first-run gotcha).
$env:Path = (@(
[Environment]::GetEnvironmentVariable('Path', 'Machine'),
[Environment]::GetEnvironmentVariable('Path', 'User')
) | Where-Object { $_ }) -join ';'
function Write-Step($m) { Write-Host "==> $m" -ForegroundColor Cyan }
function Write-Ok($m) { Write-Host " + $m" -ForegroundColor Green }
function Write-Warn2($m){ Write-Host " ! $m" -ForegroundColor Yellow }
# --- docker availability ---------------------------------------------------
function Require-Docker {
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
throw "Docker not found. Install Docker Desktop and ensure it is running: https://www.docker.com/products/docker-desktop/"
}
# `docker info` fails fast if the engine/daemon is not running.
docker info *> $null
if ($LASTEXITCODE -ne 0) {
throw 'Docker is installed but the engine is not running. Start Docker Desktop and retry.'
}
# Compose v2 (the `docker compose` subcommand) is required.
docker compose version *> $null
if ($LASTEXITCODE -ne 0) {
throw "This script needs Docker Compose v2 (the 'docker compose' subcommand). Update Docker Desktop."
}
}
# --- credentials from backend/.env -----------------------------------------
# Load DB_USER / DB_PASSWORD / DB_NAME from backend/.env into this process so
# docker compose can substitute them. Nothing is printed or copied to disk.
function Import-DbEnv {
$envFile = Join-Path $Root 'backend\.env'
if (-not (Test-Path $envFile)) {
throw "backend\.env not found. Copy backend\.env.example to backend\.env and set DB_PASSWORD first."
}
foreach ($line in Get-Content $envFile) {
$t = $line.Trim()
if ($t -eq '' -or $t.StartsWith('#')) { continue }
$i = $t.IndexOf('=')
if ($i -lt 1) { continue }
$key = $t.Substring(0, $i).Trim()
$val = $t.Substring($i + 1).Trim().Trim('"').Trim("'")
if ($key -in @('DB_USER', 'DB_PASSWORD', 'DB_NAME')) {
Set-Item -Path "Env:$key" -Value $val
}
}
if (-not $env:DB_PASSWORD) {
throw 'DB_PASSWORD is not set in backend\.env.'
}
if (-not $env:DB_USER) { $env:DB_USER = 'postgres' }
if (-not $env:DB_NAME) { $env:DB_NAME = 'travelhub' }
}
# --- compose helpers -------------------------------------------------------
function Compose {
param([Parameter(ValueFromRemainingArguments = $true)] $ComposeArgs)
Push-Location $Root
try { & docker compose @ComposeArgs } finally { Pop-Location }
}
function Wait-ForUrl($url, $timeoutSec) {
$deadline = (Get-Date).AddSeconds($timeoutSec)
while ((Get-Date) -lt $deadline) {
try { Invoke-WebRequest $url -UseBasicParsing -TimeoutSec 3 | Out-Null; return $true }
catch { Start-Sleep -Seconds 2 }
}
return $false
}
function Show-Ready {
Write-Host ''
if (Wait-ForUrl 'http://localhost:8080/api/health' 90) { Write-Ok 'API healthy -> http://localhost:8080' }
else { Write-Warn2 'API not healthy yet -- check: .\docker.ps1 logs -Service api' }
if (Wait-ForUrl 'http://localhost:4200/' 60) { Write-Ok 'App ready -> http://localhost:4200' }
else { Write-Warn2 'Web not ready yet -- check: .\docker.ps1 logs -Service web' }
Write-Host ''
Write-Host 'Sign in with any demo account, password demo1234 (e.g. agent@skyline.test / skyline).'
Write-Host 'Note: stop any native .\app.ps1 first -- it uses the same ports.' -ForegroundColor DarkGray
}
# --- actions ---------------------------------------------------------------
switch ($Action) {
'setup' {
Require-Docker; Import-DbEnv
Write-Step 'Building images (first run downloads base images)'
Compose build
Write-Step 'Starting stack and seeding the database'
Compose up -d
Show-Ready
Write-Ok 'Setup complete.'
}
'start' {
Require-Docker; Import-DbEnv
Write-Step 'Starting stack'
Compose up -d
Show-Ready
}
'stop' {
Require-Docker; Import-DbEnv
Write-Step 'Stopping stack (database volume kept)'
Compose down
Write-Ok 'Stopped.'
}
'restart' {
Require-Docker; Import-DbEnv
Compose down
Compose up -d
Show-Ready
}
'reset' {
Require-Docker; Import-DbEnv
Write-Warn2 'This DELETES the database volume. All data will be lost.'
Write-Step 'Removing containers and the data volume'
Compose down -v
Write-Step 'Starting fresh (re-seeds with current flight dates)'
Compose up -d
Show-Ready
Write-Ok 'Reset complete.'
}
'rebuild' {
Require-Docker; Import-DbEnv
Write-Step 'Rebuilding images'
Compose build --no-cache
Compose up -d
Show-Ready
}
'status' {
Require-Docker; Import-DbEnv
Compose ps
}
'logs' {
Require-Docker; Import-DbEnv
$logArgs = @('logs')
if ($Follow) { $logArgs += '-f' } else { $logArgs += @('--tail', '80') }
if ($Service) { $logArgs += $Service }
Compose @logArgs
}
default {
Get-Help $PSCommandPath -Detailed
}
}