#!/usr/bin/env bash set -eo pipefail ROOT_DIR="$(cd "$(dirname "$0")" && pwd)" cd "$ROOT_DIR" PYTHON="$ROOT_DIR/.venv/bin/python" FRONTEND_DIR="$ROOT_DIR/web/frontend" BACKEND_PORT=8000 FRONTEND_PORT=5173 HEALTH_TIMEOUT=30 # 后端健康检查最长等待秒数 # ── 工具函数 ────────────────────────────────────────────── log() { echo "[$(date '+%H:%M:%S')] $*"; } warn() { echo "[$(date '+%H:%M:%S')] ⚠️ $*" >&2; } die() { echo "[$(date '+%H:%M:%S')] ❌ $*" >&2; exit 1; } check_port() { if lsof -iTCP:"$1" -sTCP:LISTEN -P &>/dev/null; then die "端口 $1 已被占用,请先释放或更改端口号(lsof -i :$1)" fi } # ── 预检查 ──────────────────────────────────────────────── check_port "$BACKEND_PORT" check_port "$FRONTEND_PORT" if [ ! -f "$PYTHON" ]; then log "未找到 .venv,正在创建..." uv venv .venv uv pip install -e . fi if [ ! -d "$FRONTEND_DIR/node_modules" ]; then log "前端依赖未安装,正在安装..." (cd "$FRONTEND_DIR" && npm install) fi # ── 启动信息 ────────────────────────────────────────────── echo "" echo "==============================" echo " 斗鱼批量登录 - Web 后台" echo "==============================" echo " 后端: http://localhost:$BACKEND_PORT" echo " 前端: http://localhost:$FRONTEND_PORT" echo " 默认账号见环境变量 ADMIN_USERNAME/ADMIN_PASSWORD(未设置则为 admin/admin123)" echo " 按 Ctrl+C 停止所有服务" echo "==============================" echo "" # ── 清理子进程 ──────────────────────────────────────────── PIDS=() cleanup() { echo "" log "正在停止服务..." for pid in "${PIDS[@]}"; do if kill -0 "$pid" 2>/dev/null; then kill "$pid" 2>/dev/null fi done for pid in "${PIDS[@]}"; do wait "$pid" 2>/dev/null done log "已停止" } trap cleanup EXIT INT TERM # ── 启动后端 ────────────────────────────────────────────── log "启动后端 (FastAPI :$BACKEND_PORT)..." RELOAD_FLAG="" if [ "${UVICORN_RELOAD:-true}" = "true" ]; then RELOAD_FLAG="--reload" fi "$PYTHON" -m uvicorn web.backend.main:app --host 0.0.0.0 --port "$BACKEND_PORT" $RELOAD_FLAG & PIDS+=($!) # 等后端就绪(健康检查循环替代固定 sleep) log "等待后端就绪(最多 ${HEALTH_TIMEOUT}s)..." elapsed=0 while [ "$elapsed" -lt "$HEALTH_TIMEOUT" ]; do if curl -sf "http://localhost:$BACKEND_PORT/docs" -o /dev/null 2>/dev/null; then log "后端已就绪 ✓" break fi sleep 1 elapsed=$((elapsed + 1)) done if [ "$elapsed" -ge "$HEALTH_TIMEOUT" ]; then warn "后端未在 ${HEALTH_TIMEOUT}s 内就绪,前端可能无法正常连接" fi # ── 启动前端 ────────────────────────────────────────────── log "启动前端 (Vite :$FRONTEND_PORT)..." cd "$FRONTEND_DIR" npm run dev -- --port "$FRONTEND_PORT" --host 0.0.0.0 & PIDS+=($!) cd "$ROOT_DIR" # ── 等待子进程 ──────────────────────────────────────────── wait