|
|
|
@ -1,54 +1,60 @@ |
|
|
|
#!/usr/bin/env bash |
|
|
|
#!/usr/bin/env bash |
|
|
|
# MiroSociety 启动脚本 (开发模式) |
|
|
|
# MiroSociety 后台启动 (开发模式) |
|
|
|
# 前置: 请先执行 ./deploy.sh 安装依赖 |
|
|
|
# 前置: 请先执行 ./deploy.sh 安装依赖 |
|
|
|
# 前端: http://localhost:31400 后端: http://localhost:31401 |
|
|
|
# 前端: http://localhost:31400 后端: http://localhost:31401 |
|
|
|
|
|
|
|
# |
|
|
|
|
|
|
|
# 直接 ./start.sh 即可启动;已在跑会自动重启。 |
|
|
|
|
|
|
|
|
|
|
|
set -euo pipefail |
|
|
|
set -euo pipefail |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" |
|
|
|
FRONTEND_PORT=31400 |
|
|
|
FRONTEND_PORT=31400 |
|
|
|
BACKEND_PORT=31401 |
|
|
|
BACKEND_PORT=31401 |
|
|
|
# 解析到脚本所在目录,并使用绝对路径避免子 shell cd 后路径重复 |
|
|
|
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" |
|
|
|
|
|
|
|
VENV_DIR="$SCRIPT_DIR/backend/.venv" |
|
|
|
VENV_DIR="$SCRIPT_DIR/backend/.venv" |
|
|
|
BACKEND_DIR="$SCRIPT_DIR/backend" |
|
|
|
BACKEND_DIR="$SCRIPT_DIR/backend" |
|
|
|
FRONTEND_DIR="$SCRIPT_DIR/frontend" |
|
|
|
FRONTEND_DIR="$SCRIPT_DIR/frontend" |
|
|
|
|
|
|
|
RUN_DIR="$SCRIPT_DIR/.run" |
|
|
|
|
|
|
|
BACKEND_PID_FILE="$RUN_DIR/backend.pid" |
|
|
|
|
|
|
|
FRONTEND_PID_FILE="$RUN_DIR/frontend.pid" |
|
|
|
|
|
|
|
BACKEND_LOG="$SCRIPT_DIR/backend.log" |
|
|
|
|
|
|
|
FRONTEND_LOG="$SCRIPT_DIR/frontend.log" |
|
|
|
|
|
|
|
|
|
|
|
# 切到脚本所在目录,确保相对路径正确 |
|
|
|
cd "$SCRIPT_DIR" |
|
|
|
cd "$(dirname "$0")" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# --- 颜色 --- |
|
|
|
# --- 颜色 --- |
|
|
|
RED='\033[0;31m' |
|
|
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m' |
|
|
|
GREEN='\033[0;32m' |
|
|
|
|
|
|
|
YELLOW='\033[1;33m' |
|
|
|
|
|
|
|
NC='\033[0m' |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log() { echo -e "${GREEN}[start.sh]${NC} $*"; } |
|
|
|
log() { echo -e "${GREEN}[start.sh]${NC} $*"; } |
|
|
|
warn() { echo -e "${YELLOW}[start.sh]${NC} $*"; } |
|
|
|
warn() { echo -e "${YELLOW}[start.sh]${NC} $*"; } |
|
|
|
err() { echo -e "${RED}[start.sh]${NC} $*" >&2; } |
|
|
|
err() { echo -e "${RED}[start.sh]${NC} $*" >&2; } |
|
|
|
|
|
|
|
|
|
|
|
# --- 检查命令依赖 --- |
|
|
|
# --- 检查依赖 --- |
|
|
|
for cmd in lsof npm node curl; do |
|
|
|
for cmd in lsof npm node curl; do |
|
|
|
if ! command -v "$cmd" >/dev/null 2>&1; then |
|
|
|
command -v "$cmd" >/dev/null 2>&1 || { err "缺少依赖命令: $cmd"; exit 1; } |
|
|
|
err "缺少依赖命令: $cmd" |
|
|
|
|
|
|
|
exit 1 |
|
|
|
|
|
|
|
fi |
|
|
|
|
|
|
|
done |
|
|
|
done |
|
|
|
|
|
|
|
[[ -x "$VENV_DIR/bin/python3" ]] || { err "未检测到 $VENV_DIR/bin/python3,请先执行 ./deploy.sh"; exit 1; } |
|
|
|
|
|
|
|
"$VENV_DIR/bin/python3" -c "import uvicorn" 2>/dev/null || { err "venv 缺少 uvicorn,请先执行 ./deploy.sh"; exit 1; } |
|
|
|
|
|
|
|
[[ -d "$FRONTEND_DIR/node_modules" ]] || { err "未检测到 frontend/node_modules,请先执行 ./deploy.sh"; exit 1; } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mkdir -p "$RUN_DIR" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# --- 已在跑 → 先杀 --- |
|
|
|
|
|
|
|
read_pid() { [[ -f "$1" ]] && cat "$1" || echo ""; } |
|
|
|
|
|
|
|
kill_pid_file() { |
|
|
|
|
|
|
|
local pid_file="$1" label="$2" |
|
|
|
|
|
|
|
local pid |
|
|
|
|
|
|
|
pid=$(read_pid "$pid_file") |
|
|
|
|
|
|
|
if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then |
|
|
|
|
|
|
|
kill "$pid" 2>/dev/null || true |
|
|
|
|
|
|
|
for _ in $(seq 1 10); do |
|
|
|
|
|
|
|
kill -0 "$pid" 2>/dev/null || break |
|
|
|
|
|
|
|
sleep 0.3 |
|
|
|
|
|
|
|
done |
|
|
|
|
|
|
|
kill -9 "$pid" 2>/dev/null || true |
|
|
|
|
|
|
|
log "$label 已停止 (pid=$pid)" |
|
|
|
|
|
|
|
fi |
|
|
|
|
|
|
|
rm -f "$pid_file" |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
# --- 检查依赖是否已部署 --- |
|
|
|
|
|
|
|
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() { |
|
|
|
free_port() { |
|
|
|
local port="$1" |
|
|
|
local port="$1" |
|
|
|
local pids |
|
|
|
local pids |
|
|
|
@ -61,39 +67,35 @@ free_port() { |
|
|
|
fi |
|
|
|
fi |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
free_port "$FRONTEND_PORT" |
|
|
|
# --- 启动后端 --- |
|
|
|
free_port "$BACKEND_PORT" |
|
|
|
free_port "$BACKEND_PORT" |
|
|
|
|
|
|
|
kill_pid_file "$BACKEND_PID_FILE" "后端" |
|
|
|
# --- 清理函数: 退出时杀掉所有子进程 --- |
|
|
|
log "启动后端 FastAPI @ :$BACKEND_PORT (后台)" |
|
|
|
cleanup() { |
|
|
|
: >"$BACKEND_LOG" |
|
|
|
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" |
|
|
|
cd "$BACKEND_DIR" |
|
|
|
HOST=0.0.0.0 PORT="$BACKEND_PORT" \ |
|
|
|
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" |
|
|
|
nohup "$VENV_DIR/bin/python3" -m uvicorn app.main:app \ |
|
|
|
) >"$SCRIPT_DIR/backend.log" 2>&1 & |
|
|
|
--host 0.0.0.0 --port "$BACKEND_PORT" \ |
|
|
|
BACKEND_PID=$! |
|
|
|
>>"$BACKEND_LOG" 2>&1 & |
|
|
|
|
|
|
|
echo $! >"$BACKEND_PID_FILE" |
|
|
|
# --- 启动前端 (后台, 端口固定为 31400) --- |
|
|
|
disown || true |
|
|
|
log "启动前端 Vite @ :$FRONTEND_PORT" |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# --- 启动前端 --- |
|
|
|
|
|
|
|
free_port "$FRONTEND_PORT" |
|
|
|
|
|
|
|
kill_pid_file "$FRONTEND_PID_FILE" "前端" |
|
|
|
|
|
|
|
log "启动前端 Vite @ :$FRONTEND_PORT (后台)" |
|
|
|
|
|
|
|
: >"$FRONTEND_LOG" |
|
|
|
( |
|
|
|
( |
|
|
|
cd "$FRONTEND_DIR" |
|
|
|
cd "$FRONTEND_DIR" |
|
|
|
npx vite --port "$FRONTEND_PORT" --strictPort |
|
|
|
nohup npx vite >>"$FRONTEND_LOG" 2>&1 & |
|
|
|
) >"$SCRIPT_DIR/frontend.log" 2>&1 & |
|
|
|
echo $! >"$FRONTEND_PID_FILE" |
|
|
|
FRONTEND_PID=$! |
|
|
|
disown || true |
|
|
|
|
|
|
|
) |
|
|
|
log "后端 pid=$BACKEND_PID 前端 pid=$FRONTEND_PID" |
|
|
|
|
|
|
|
log "等待服务就绪..." |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# --- 健康检查 --- |
|
|
|
# --- 健康检查 --- |
|
|
|
|
|
|
|
log "等待服务就绪..." |
|
|
|
wait_for() { |
|
|
|
wait_for() { |
|
|
|
local url="$1" name="$2" |
|
|
|
local url="$1" name="$2" |
|
|
|
for _ in $(seq 1 30); do |
|
|
|
for _ in $(seq 1 30); do |
|
|
|
@ -107,16 +109,11 @@ wait_for() { |
|
|
|
return 1 |
|
|
|
return 1 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
wait_for "http://localhost:$BACKEND_PORT/health" "后端" |
|
|
|
wait_for "http://localhost:$BACKEND_PORT/health" "后端" || true |
|
|
|
wait_for "http://localhost:$FRONTEND_PORT" "前端" |
|
|
|
wait_for "http://localhost:$FRONTEND_PORT" "前端" || true |
|
|
|
|
|
|
|
|
|
|
|
echo |
|
|
|
echo |
|
|
|
log "全部就绪!" |
|
|
|
log "全部就绪 ✓" |
|
|
|
echo -e " ${GREEN}前端:${NC} http://localhost:$FRONTEND_PORT" |
|
|
|
echo -e " ${GREEN}前端:${NC} http://localhost:$FRONTEND_PORT" |
|
|
|
echo -e " ${GREEN}后端:${NC} http://localhost:$BACKEND_PORT (健康检查: /health)" |
|
|
|
echo -e " ${GREEN}后端:${NC} http://localhost:$BACKEND_PORT (健康检查: /health)" |
|
|
|
echo -e " ${GREEN}日志:${NC} backend.log / frontend.log" |
|
|
|
echo -e " ${GREEN}日志:${NC} backend.log / frontend.log" |
|
|
|
echo |
|
|
|
|
|
|
|
warn "按 Ctrl+C 终止所有服务" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# --- 保持前台,跟随日志 --- |
|
|
|
|
|
|
|
tail -F "$SCRIPT_DIR/backend.log" "$SCRIPT_DIR/frontend.log" |
|
|
|
|