2026-06-04 14:30:53 -07:00
|
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
|
set -e
|
|
|
|
|
|
ROOT="$(cd "$(dirname "$0")" && pwd)"
|
|
|
|
|
|
|
2026-06-08 15:13:59 +08:00
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-04 14:30:53 -07:00
|
|
|
|
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() {
|
2026-06-08 15:13:59 +08:00
|
|
|
|
stop_services backend
|
2026-06-04 14:30:53 -07:00
|
|
|
|
ensure_backend_venv
|
2026-06-08 15:13:59 +08:00
|
|
|
|
exec uvicorn main:app --reload --host 0.0.0.0 --port "$BACKEND_PORT"
|
2026-06-04 14:30:53 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
run_frontend() {
|
2026-06-08 15:13:59 +08:00
|
|
|
|
stop_services frontend
|
2026-06-04 14:30:53 -07:00
|
|
|
|
cd "$ROOT/frontend"
|
|
|
|
|
|
[ -d node_modules ] || npm install
|
2026-06-08 15:13:59 +08:00
|
|
|
|
exec npm run dev -- --host 0.0.0.0 --port "$FRONTEND_PORT"
|
2026-06-04 14:30:53 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
run_all() {
|
2026-06-08 15:13:59 +08:00
|
|
|
|
stop_services all
|
2026-06-04 14:30:53 -07:00
|
|
|
|
ensure_backend_venv
|
|
|
|
|
|
cd "$ROOT/frontend"
|
|
|
|
|
|
[ -d node_modules ] || npm install
|
|
|
|
|
|
|
|
|
|
|
|
trap 'kill $(jobs -p) 2>/dev/null' EXIT INT TERM
|
|
|
|
|
|
|
2026-06-08 15:13:59 +08:00
|
|
|
|
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") &
|
2026-06-04 14:30:53 -07:00
|
|
|
|
|
2026-06-08 15:13:59 +08:00
|
|
|
|
echo "启动前端 http://localhost:${FRONTEND_PORT}"
|
|
|
|
|
|
(cd "$ROOT/frontend" && npm run dev -- --host 0.0.0.0 --port "$FRONTEND_PORT") &
|
2026-06-04 14:30:53 -07:00
|
|
|
|
|
|
|
|
|
|
echo "按 Ctrl+C 停止前后端"
|
|
|
|
|
|
wait
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case "${1:-all}" in
|
2026-06-08 15:13:59 +08:00
|
|
|
|
stop) stop_services all ;;
|
2026-06-04 14:30:53 -07:00
|
|
|
|
backend) run_backend ;;
|
|
|
|
|
|
frontend) run_frontend ;;
|
|
|
|
|
|
all) run_all ;;
|
|
|
|
|
|
*)
|
2026-06-08 15:13:59 +08:00
|
|
|
|
echo "用法: ./start.sh [all|backend|frontend|stop]"
|
2026-06-04 14:30:53 -07:00
|
|
|
|
echo ""
|
2026-06-08 15:13:59 +08:00
|
|
|
|
echo " ./start.sh # 先停旧进程,再同一终端启动前后端"
|
2026-06-04 14:30:53 -07:00
|
|
|
|
echo " ./start.sh all # 同上"
|
2026-06-08 15:13:59 +08:00
|
|
|
|
echo " ./start.sh backend # 先停旧后端,再仅启动后端"
|
|
|
|
|
|
echo " ./start.sh frontend # 先停旧前端,再仅启动前端"
|
|
|
|
|
|
echo " ./start.sh stop # 仅停止前后端,不启动"
|
2026-06-04 14:30:53 -07:00
|
|
|
|
echo ""
|
|
|
|
|
|
echo "或按 README 在两个终端分别执行 backend / frontend 下的命令。"
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
;;
|
|
|
|
|
|
esac
|