#!/usr/bin/env bash # ============================================================ # 后端服务管理脚本(macOS / Linux 通用) # # 用法: # ./deploy/serve.sh start 启动(默认 127.0.0.1:8099,仅 API 模式) # ./deploy/serve.sh start-dev 启动开发模式(API + 静态,可直接浏览器预览) # ./deploy/serve.sh stop 停止 # ./deploy/serve.sh restart 重启 # ./deploy/serve.sh status 查看状态 # ./deploy/serve.sh log 跟踪日志 # # 说明:生产环境建议用 systemd / launchd 托管;本脚本用于快速启停与排障。 # ============================================================ set -euo pipefail PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$PROJECT_ROOT" PY="${PROJECT_ROOT}/.venv/bin/python" PORT="${HDDIV_PORT:-8099}" HOST="${HDDIV_HOST:-127.0.0.1}" MODE="${HDDIV_MODE:-api-only}" # api-only | full LOG_DIR="${PROJECT_ROOT}/logs" PID_FILE="${LOG_DIR}/web.pid" MODE_FILE="${LOG_DIR}/web.mode" LOG_FILE="${LOG_DIR}/web.log" mkdir -p "$LOG_DIR" is_running() { [[ -f "$PID_FILE" ]] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null } start() { if is_running; then echo "已在运行(PID $(cat "$PID_FILE"))" return 0 fi if [[ ! -x "$PY" ]]; then echo "找不到虚拟环境:$PY" >&2 echo "请先执行:uv venv --python 3.12 .venv && uv pip install --python .venv/bin/python pandas numpy sqlalchemy pymysql pydantic pyyaml jinja2 httpx python-dotenv" >&2 exit 1 fi local args=(--host "$HOST" --port "$PORT") [[ "$MODE" == "api-only" ]] && args+=(--api-only) PYTHONPATH=src nohup "$PY" -m hdiv web "${args[@]}" >> "$LOG_FILE" 2>&1 & echo $! > "$PID_FILE" echo "$MODE" > "$MODE_FILE" sleep 2 if is_running; then echo "已启动(PID $(cat "$PID_FILE")) 模式=${MODE} 监听 http://${HOST}:${PORT}" echo "日志:$LOG_FILE" if [[ "$MODE" == "api-only" ]]; then echo "注意:api-only 模式下静态文件需由 nginx 提供;本机预览请用 start-dev" else echo "本机预览:http://${HOST}:${PORT}/" fi else echo "启动失败,日志尾部:" >&2 tail -20 "$LOG_FILE" >&2 exit 1 fi } stop() { SAVED_MODE="$(cat "$MODE_FILE" 2>/dev/null || echo unknown)" if ! is_running; then echo "未在运行" rm -f "$PID_FILE" "$MODE_FILE" return 0 fi local pid; pid="$(cat "$PID_FILE")" kill "$pid" 2>/dev/null || true for _ in $(seq 1 20); do kill -0 "$pid" 2>/dev/null || break sleep 0.2 done kill -9 "$pid" 2>/dev/null || true rm -f "$PID_FILE" "$MODE_FILE" echo "已停止(PID ${pid})" } status() { SAVED_MODE="$(cat "$MODE_FILE" 2>/dev/null || echo unknown)" if is_running; then local pid; pid="$(cat "$PID_FILE")" echo "运行中 PID=${pid} 模式=${SAVED_MODE} 监听 http://${HOST}:${PORT}" echo -n "健康检查:" curl -s --max-time 3 "http://${HOST}:${PORT}/api/health" || echo "(无响应)" echo else echo "未在运行" fi } case "${1:-}" in start) MODE=api-only start ;; start-dev) MODE=full start ;; stop) stop ;; restart) SAVED_MODE="$(cat "$MODE_FILE" 2>/dev/null || echo api-only)" stop; MODE="${SAVED_MODE}" start ;; status) status ;; log) tail -f "$LOG_FILE" ;; *) sed -n '2,15p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//' exit 2 ;; esac