deploy: one-command scripts to build+run from deploy/.env

This commit is contained in:
cesnimda
2026-06-30 16:16:16 +02:00
parent a387d31f62
commit dcb939e4f2
9 changed files with 264 additions and 0 deletions
+48
View File
@@ -0,0 +1,48 @@
# deploy/
Holds the **live secrets** (`deploy/.env`) and one-command scripts to build and run the stack against them. `deploy/.env` is git-ignored — it is never committed.
## deploy/.env
Required keys (see `../.env.example` for the template):
```
POSTGRES_PASSWORD=...
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
AI_MODE=Disabled # Disabled | LocalOllama | CloudOpenAi
FRONTEND_ORIGIN=http://localhost:8081
```
## Commands
Windows (PowerShell), from the repo root:
```powershell
./deploy/up.ps1 # build + start (detached)
./deploy/up.ps1 -Foreground # stream logs instead of detaching
./deploy/up.ps1 -Proxy # also run the top-level nginx (everything on :80)
./deploy/logs.ps1 api # tail a service's logs
./deploy/down.ps1 # stop
./deploy/down.ps1 -Volumes # stop and wipe the DB + key volumes
```
Linux / Ubuntu production target:
```bash
./deploy/up.sh # build + start (detached)
./deploy/up.sh --foreground
./deploy/up.sh --proxy
```
Both scripts validate that `deploy/.env` exists and that the required keys are non-blank before invoking Docker, so a misconfigured env fails fast with a clear message instead of a half-started stack.
## What it runs
`docker compose --env-file deploy/.env up --build` — Postgres, the API (auto-applies EF migrations on boot), and the frontend. The `--env-file` flag feeds the `${...}` variables in `docker-compose.yml`.
After it's up:
- Frontend — http://localhost:8081
- API / Swagger — http://localhost:8080/swagger
Reminder: the Google OAuth redirect URI for this layout is `http://localhost:8081/signin-google` (see `../docs/GOOGLE_OAUTH_SETUP.md`).
+19
View File
@@ -0,0 +1,19 @@
<#
.SYNOPSIS
Stop the InboxIntel stack. Use -Volumes to also drop the database + key data.
.EXAMPLE
./deploy/down.ps1
./deploy/down.ps1 -Volumes
#>
param([switch]$Volumes)
$ErrorActionPreference = 'Stop'
$root = Split-Path -Parent $PSScriptRoot
$envFile = Join-Path $PSScriptRoot '.env'
$composeArgs = @('compose', '--env-file', $envFile, 'down')
if ($Volumes) { $composeArgs += '--volumes' }
Push-Location $root
try { & docker @composeArgs }
finally { Pop-Location }
+19
View File
@@ -0,0 +1,19 @@
<#
.SYNOPSIS
Tail logs for the stack (or a single service, e.g. api / frontend / postgres).
.EXAMPLE
./deploy/logs.ps1 # all services
./deploy/logs.ps1 api # just the API container
#>
param([string]$Service)
$ErrorActionPreference = 'Stop'
$root = Split-Path -Parent $PSScriptRoot
$envFile = Join-Path $PSScriptRoot '.env'
$composeArgs = @('compose', '--env-file', $envFile, 'logs', '-f', '--tail', '200')
if ($Service) { $composeArgs += $Service }
Push-Location $root
try { & docker @composeArgs }
finally { Pop-Location }
+46
View File
@@ -0,0 +1,46 @@
<#
.SYNOPSIS
Build and start the full InboxIntel stack using deploy/.env for secrets.
.EXAMPLE
./deploy/up.ps1 # build + start detached
./deploy/up.ps1 -Proxy # also start the top-level nginx reverse proxy
./deploy/up.ps1 -Foreground # stream logs instead of detaching
#>
param(
[switch]$Proxy,
[switch]$Foreground
)
$ErrorActionPreference = 'Stop'
$root = Split-Path -Parent $PSScriptRoot # repo root (deploy/ is one level down)
$envFile = Join-Path $PSScriptRoot '.env'
if (-not (Test-Path $envFile)) {
throw "Missing $envFile. Create it from .env.example with your real secrets."
}
# Fail fast if a required key is absent or blank.
$required = 'POSTGRES_PASSWORD','GOOGLE_CLIENT_ID','GOOGLE_CLIENT_SECRET'
$envMap = @{}
Get-Content $envFile | ForEach-Object {
if ($_ -match '^\s*([A-Z_][A-Z0-9_]*)\s*=\s*(.*)$') { $envMap[$Matches[1]] = $Matches[2].Trim() }
}
$missing = $required | Where-Object { [string]::IsNullOrWhiteSpace($envMap[$_]) }
if ($missing) { throw "deploy/.env is missing values for: $($missing -join ', ')" }
$composeArgs = @('compose', '--env-file', $envFile)
if ($Proxy) { $composeArgs += @('--profile', 'proxy') }
$composeArgs += @('up', '--build')
if (-not $Foreground) { $composeArgs += '-d' }
Push-Location $root
try {
Write-Host "Starting InboxIntel via docker compose (env: deploy/.env)..." -ForegroundColor Cyan
& docker @composeArgs
if (-not $Foreground) {
& docker compose --env-file $envFile ps
Write-Host "`nFrontend: http://localhost:8081 API/Swagger: http://localhost:8080/swagger" -ForegroundColor Green
Write-Host "Logs: ./deploy/logs.ps1 Stop: ./deploy/down.ps1" -ForegroundColor DarkGray
}
}
finally { Pop-Location }
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env bash
# Build and start the InboxIntel stack using deploy/.env for secrets.
# Usage: ./deploy/up.sh [--proxy] [--foreground]
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
ENV_FILE="$SCRIPT_DIR/.env"
[ -f "$ENV_FILE" ] || { echo "Missing $ENV_FILE. Create it from .env.example." >&2; exit 1; }
# Fail fast if a required key is blank.
for key in POSTGRES_PASSWORD GOOGLE_CLIENT_ID GOOGLE_CLIENT_SECRET; do
val="$(grep -E "^${key}=" "$ENV_FILE" | head -1 | cut -d= -f2- | tr -d '[:space:]' || true)"
[ -n "$val" ] || { echo "deploy/.env is missing a value for: $key" >&2; exit 1; }
done
PROFILE_ARGS=()
DETACH="-d"
for arg in "$@"; do
case "$arg" in
--proxy) PROFILE_ARGS=(--profile proxy) ;;
--foreground) DETACH="" ;;
*) echo "Unknown arg: $arg" >&2; exit 1 ;;
esac
done
cd "$ROOT"
echo "Starting InboxIntel via docker compose (env: deploy/.env)..."
docker compose --env-file "$ENV_FILE" "${PROFILE_ARGS[@]}" up --build $DETACH
if [ -n "$DETACH" ]; then
docker compose --env-file "$ENV_FILE" ps
echo
echo "Frontend: http://localhost:8081 API/Swagger: http://localhost:8080/swagger"
fi