#!/usr/bin/env bash # 全栈部署冒烟:经 nginx 验证 SPA 静态、/api 反代、登录、SW/manifest。 # 前提:.env 里有 JWT_SECRET/ADMIN_USER/ADMIN_PASSWORD;宿主 :8080 空闲。 set -euo pipefail BASE=${BASE:-http://localhost:8080} [ -f .env ] && set -a && . ./.env && set +a : "${JWT_SECRET:?JWT_SECRET 未设置}"; : "${ADMIN_USER:?ADMIN_USER 未设置}"; : "${ADMIN_PASSWORD:?ADMIN_PASSWORD 未设置}" say(){ echo "smoke-web: $1"; } die(){ echo "SMOKE-WEB FAIL: $1"; exit 1; } say "build web + api images" docker compose build web api say "up --wait" # docker compose 支持 --wait;podman-compose 不支持 → 回退 up -d + 轮询健康 if docker compose up -d --wait postgres redis api web 2>/dev/null; then : else say "no --wait support: up -d + poll healthz" docker compose up -d postgres redis api web for _ in $(seq 1 60); do curl -fsS --max-time 3 "$BASE/api/healthz" >/dev/null 2>&1 && break sleep 2 done curl -fsS --max-time 5 "$BASE/api/healthz" >/dev/null || die "api healthz 未就绪" fi say "SPA index served by nginx" INDEX=$(curl -fsS "$BASE/") echo "$INDEX" | grep -q 'id="root"' || die "index 缺 #root" ASSET=$(echo "$INDEX" | sed -nE 's#.*src="(/assets/[^"]+\.js)".*#\1#p' | head -1) [ -n "$ASSET" ] || die "index 未引用 /assets/*.js" curl -fsS -o /dev/null "$BASE$ASSET" || die "asset $ASSET 拉取失败" say "SPA deep link falls back to index" curl -fsS "$BASE/admin/users" | grep -q 'id="root"' || die "try_files 回退失败" say "api via /api/ proxy" curl -fsS "$BASE/api/healthz" >/dev/null || die "healthz via nginx" TOK=$(curl -fsS "$BASE/api/auth/login" -H 'content-type: application/json' \ -d "{\"username\":\"$ADMIN_USER\",\"password\":\"$ADMIN_PASSWORD\"}" \ | sed -E 's/.*"token":"([^"]+)".*/\1/') [ -n "$TOK" ] || die "nginx 反代登录失败" curl -fsS "$BASE/api/auth/me" -H "authorization: Bearer $TOK" | grep -q "$ADMIN_USER" || die "me via nginx" say "pwa artifacts" curl -fsS "$BASE/sw.js" >/dev/null || die "sw.js 缺失" curl -fsS "$BASE/manifest.webmanifest" >/dev/null || die "manifest 缺失" say "ALL WEB SMOKE PASSED"