#!/usr/bin/env bash # MiroSociety 启动脚本 (开发模式) # 前置: 请先执行 ./deploy.sh 安装依赖 # 前端: http://localhost:31400 后端: http://localhost:31401 set -euo pipefail FRONTEND_PORT=31400 BACKEND_PORT=31401 # 解析到脚本所在目录,并使用绝对路径避免子 shell cd 后路径重复 SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" VENV_DIR="$SCRIPT_DIR/backend/.venv" BACKEND_DIR="$SCRIPT_DIR/backend" FRONTEND_DIR="$SCRIPT_DIR/frontend" # 切到脚本所在目录,确保相对路径正确 cd "$(dirname "$0")" # --- 颜色 --- RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' log() { echo -e "${GREEN}[start.sh]${NC} $*"; } warn() { echo -e "${YELLOW}[start.sh]${NC} $*"; } err() { echo -e "${RED}[start.sh]${NC} $*" >&2; } # --- 检查命令依赖 --- for cmd in lsof npm node curl; do if ! command -v "$cmd" >/dev/null 2>&1; then err "缺少依赖命令: $cmd" exit 1 fi done # --- 检查依赖是否已部署 --- if [[ ! -x "$VENV_DIR/bin/python3" ]]; then err "未检测到 $VENV_DIR/bin/python3,请先执行 ./deploy.sh" exit 1 fi if ! "$VENV_DIR/bin/python3" -c "import uvicorn" 2>/dev/null; then err "venv 缺少 uvicorn,请先执行 ./deploy.sh" exit 1 fi if [[ ! -d frontend/node_modules ]]; then err "未检测到 frontend/node_modules,请先执行 ./deploy.sh" exit 1 fi # --- 释放端口: kill 占用进程 --- free_port() { local port="$1" local pids pids=$(lsof -ti tcp:"$port" 2>/dev/null || true) if [[ -n "$pids" ]]; then warn "端口 $port 被占用 (pid: $pids),正在终止..." # shellcheck disable=SC2086 kill -9 $pids 2>/dev/null || true sleep 1 fi } free_port "$FRONTEND_PORT" free_port "$BACKEND_PORT" # --- 清理函数: 退出时杀掉所有子进程 --- cleanup() { log "正在关闭子进程..." if [[ -n "${BACKEND_PID:-}" ]]; then kill -9 "$BACKEND_PID" 2>/dev/null || true; fi if [[ -n "${FRONTEND_PID:-}" ]]; then kill -9 "$FRONTEND_PID" 2>/dev/null || true; fi exit 0 } trap cleanup INT TERM EXIT # --- 启动后端 (后台, 使用 venv 内的 uvicorn) --- log "启动后端 FastAPI @ :$BACKEND_PORT" ( cd "$BACKEND_DIR" HOST=0.0.0.0 PORT="$BACKEND_PORT" \ exec "$VENV_DIR/bin/python3" -m uvicorn app.main:app --host 0.0.0.0 --port "$BACKEND_PORT" ) >"$SCRIPT_DIR/backend.log" 2>&1 & BACKEND_PID=$! # --- 启动前端 (后台, 端口固定为 31400) --- log "启动前端 Vite @ :$FRONTEND_PORT" ( cd "$FRONTEND_DIR" npx vite --port "$FRONTEND_PORT" --strictPort ) >"$SCRIPT_DIR/frontend.log" 2>&1 & FRONTEND_PID=$! log "后端 pid=$BACKEND_PID 前端 pid=$FRONTEND_PID" log "等待服务就绪..." # --- 健康检查 --- wait_for() { local url="$1" name="$2" for _ in $(seq 1 30); do if curl -sf "$url" >/dev/null 2>&1; then log "$name 已启动 ✓ ($url)" return 0 fi sleep 1 done err "$name 启动超时,请查看 ${name%% *}.log" return 1 } wait_for "http://localhost:$BACKEND_PORT/health" "后端" wait_for "http://localhost:$FRONTEND_PORT" "前端" echo log "全部就绪!" echo -e " ${GREEN}前端:${NC} http://localhost:$FRONTEND_PORT" echo -e " ${GREEN}后端:${NC} http://localhost:$BACKEND_PORT (健康检查: /health)" echo -e " ${GREEN}日志:${NC} backend.log / frontend.log" echo warn "按 Ctrl+C 终止所有服务" # --- 保持前台,跟随日志 --- tail -F "$SCRIPT_DIR/backend.log" "$SCRIPT_DIR/frontend.log"