#!/usr/bin/env bash set -e ROOT="$(cd "$(dirname "$0")" && pwd)" BACKEND_PORT=18004 FRONTEND_PORT=18003 stop_port() { local port=$1 local label=$2 local pids pids=$(lsof -ti tcp:"$port" 2>/dev/null || true) if [ -z "$pids" ]; then return 0 fi echo "停止 ${label}(端口 ${port})..." echo "$pids" | xargs kill -TERM 2>/dev/null || true sleep 1 pids=$(lsof -ti tcp:"$port" 2>/dev/null || true) if [ -n "$pids" ]; then echo "$pids" | xargs kill -KILL 2>/dev/null || true sleep 0.5 fi } stop_services() { local target="${1:-all}" case "$target" in backend) stop_port "$BACKEND_PORT" "后端" ;; frontend) stop_port "$FRONTEND_PORT" "前端" ;; all) stop_port "$BACKEND_PORT" "后端" stop_port "$FRONTEND_PORT" "前端" ;; esac } ensure_backend_venv() { cd "$ROOT/backend" if [ ! -d venv ]; then python3 -m venv venv source venv/bin/activate pip install -r requirements.txt -q else source venv/bin/activate fi } run_backend() { stop_services backend ensure_backend_venv exec uvicorn main:app --reload --host 0.0.0.0 --port "$BACKEND_PORT" } run_frontend() { stop_services frontend cd "$ROOT/frontend" [ -d node_modules ] || npm install exec npm run dev -- --host 0.0.0.0 --port "$FRONTEND_PORT" } run_all() { stop_services all ensure_backend_venv cd "$ROOT/frontend" [ -d node_modules ] || npm install trap 'kill $(jobs -p) 2>/dev/null' EXIT INT TERM echo "启动后端 http://localhost:${BACKEND_PORT} (API 文档 /docs)" (cd "$ROOT/backend" && source venv/bin/activate && uvicorn main:app --reload --host 0.0.0.0 --port "$BACKEND_PORT") & echo "启动前端 http://localhost:${FRONTEND_PORT}" (cd "$ROOT/frontend" && npm run dev -- --host 0.0.0.0 --port "$FRONTEND_PORT") & echo "按 Ctrl+C 停止前后端" wait } case "${1:-all}" in stop) stop_services all ;; backend) run_backend ;; frontend) run_frontend ;; all) run_all ;; *) echo "用法: ./start.sh [all|backend|frontend|stop]" echo "" echo " ./start.sh # 先停旧进程,再同一终端启动前后端" echo " ./start.sh all # 同上" echo " ./start.sh backend # 先停旧后端,再仅启动后端" echo " ./start.sh frontend # 先停旧前端,再仅启动前端" echo " ./start.sh stop # 仅停止前后端,不启动" echo "" echo "或按 README 在两个终端分别执行 backend / frontend 下的命令。" exit 1 ;; esac