Files
mindfulness/server/run.sh
2026-02-09 11:52:11 +08:00

208 lines
5.5 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
set -euo pipefail
# 一键启动 FastAPI 后端:
# - 自动创建/复用虚拟环境(.venv
# - 自动安装 requirements.txt 依赖
# - 自动启动 uvicorn默认开启 --reload
#
# 用法示例:
# ./run.sh # 默认 host=0.0.0.0 port=8000 env=dev reload=on
# ./run.sh --env prod # 使用 .env.prod若存在且可被 source
# ./run.sh --port 9000 # 改端口
# ./run.sh --no-reload # 关闭热更新
# ./run.sh --with-worker --with-beat # 同时启动 Celery Worker + Beat用于定时推送
# ./run.sh --install-only # 只安装依赖,不启动
usage() {
cat <<'EOF'
用法:
./run.sh [--env dev|prod] [--host 0.0.0.0] [--port 8000] [--no-reload] [--with-worker] [--with-beat] [--skip-install] [--install-only]
参数:
--env dev|prod 优先尝试加载 .env.dev 或 .env.prod如果存在
--host <host> uvicorn host默认 0.0.0.0
--port <port> uvicorn port默认 8000
--no-reload 关闭 uvicorn --reload
--with-worker 同时启动 Celery Worker处理异步/ETA 任务)
--with-beat 同时启动 Celery Beat定时调度例如每日生成推送排程
--skip-install 跳过依赖安装(默认会安装/更新 requirements.txt
--install-only 只安装依赖,不启动服务
-h, --help 显示帮助
说明:
- 若你的 .env.* 不是 shell 可 source 的格式(例如包含空格/特殊字符未加引号),建议改成 KEY=value 形式。
- 仅启动 API 并不会生成 `push_send_log`;要测试“定时推送”,需要 Beat 调度 `tasks.push.generate_daily_schedule`,并由 Worker 执行后续 ETA 任务。
- 启动后访问:
/healthz 健康检查
/docs OpenAPI 文档
EOF
}
# 始终从脚本所在目录运行(避免在别处执行导致路径错)
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
ENV_NAME="dev"
HOST="0.0.0.0"
PORT="8000"
RELOAD="1"
SKIP_INSTALL="0"
INSTALL_ONLY="0"
WITH_WORKER="0"
WITH_BEAT="0"
while [[ $# -gt 0 ]]; do
case "$1" in
--env)
ENV_NAME="${2:-}"
shift 2
;;
--host)
HOST="${2:-}"
shift 2
;;
--port)
PORT="${2:-}"
shift 2
;;
--no-reload)
RELOAD="0"
shift 1
;;
--with-worker)
WITH_WORKER="1"
shift 1
;;
--with-beat)
WITH_BEAT="1"
shift 1
;;
--skip-install)
SKIP_INSTALL="1"
shift 1
;;
--install-only)
INSTALL_ONLY="1"
shift 1
;;
-h|--help)
usage
exit 0
;;
*)
echo "未知参数:$1" >&2
echo "" >&2
usage >&2
exit 2
;;
esac
done
if [[ "$ENV_NAME" != "dev" && "$ENV_NAME" != "prod" ]]; then
echo "--env 仅支持 dev 或 prod当前$ENV_NAME" >&2
exit 2
fi
ENV_FILE=".env.${ENV_NAME}"
if [[ -f "$ENV_FILE" ]]; then
# 让 source 进来的变量自动 export供 pydantic-settings/应用读取)
set -a
# shellcheck disable=SC1090
source "$ENV_FILE"
set +a
fi
# 让 API/Celery 统一使用同一个 APP_ENV影响 Redis key 前缀、定时任务配置等)
export APP_ENV="${APP_ENV:-$ENV_NAME}"
# Beat 只负责“投递任务到队列”,真正执行仍需要 Worker这里自动补齐避免误用。
if [[ "$WITH_BEAT" == "1" && "$WITH_WORKER" == "0" ]]; then
echo "提示:已启用 --with-beat自动同时启用 --with-worker否则队列无人执行。"
WITH_WORKER="1"
fi
# 选择 python 命令(优先 python3
PY_BIN=""
if command -v python3 >/dev/null 2>&1; then
PY_BIN="python3"
elif command -v python >/dev/null 2>&1; then
PY_BIN="python"
else
echo "未找到 python/python3请先安装 Python 3.11+。" >&2
exit 1
fi
VENV_DIR=".venv"
if [[ ! -d "$VENV_DIR" ]]; then
echo "创建虚拟环境:$VENV_DIR"
"$PY_BIN" -m venv "$VENV_DIR"
fi
# 激活虚拟环境
# shellcheck disable=SC1091
source "$VENV_DIR/bin/activate"
if [[ "$SKIP_INSTALL" == "0" ]]; then
if [[ -f "requirements.txt" ]]; then
echo "升级 pip 并安装依赖requirements.txt"
python -m pip install -U pip
python -m pip install -r requirements.txt
else
echo "未找到 requirements.txt跳过依赖安装。" >&2
fi
fi
if [[ "$INSTALL_ONLY" == "1" ]]; then
echo "依赖安装完成install-only退出。"
exit 0
fi
UVICORN_ARGS=(app.main:app --host "$HOST" --port "$PORT")
if [[ "$RELOAD" == "1" ]]; then
UVICORN_ARGS+=(--reload)
fi
if [[ "$WITH_WORKER" == "0" && "$WITH_BEAT" == "0" ]]; then
echo "启动服务uvicorn ${UVICORN_ARGS[*]}"
exec uvicorn "${UVICORN_ARGS[@]}"
fi
PIDS=()
cleanup() {
# 避免重复清理导致脚本退出码被覆盖
set +e
if [[ ${#PIDS[@]} -gt 0 ]]; then
echo ""
echo "正在停止后台进程..."
# 先尝试温和退出
for pid in "${PIDS[@]}"; do
kill -TERM "$pid" >/dev/null 2>&1 || true
done
# 等待一点时间,再强制杀掉仍存活的(防止残留)
sleep 1
for pid in "${PIDS[@]}"; do
kill -KILL "$pid" >/dev/null 2>&1 || true
done
fi
}
trap cleanup EXIT INT TERM
if [[ "$WITH_WORKER" == "1" ]]; then
echo "启动 Celery Workercelery -A app.worker:celery_app worker -l info"
celery -A app.worker:celery_app worker -l info &
PIDS+=("$!")
fi
if [[ "$WITH_BEAT" == "1" ]]; then
echo "启动 Celery Beatcelery -A app.worker:celery_app beat -l info"
celery -A app.worker:celery_app beat -l info &
PIDS+=("$!")
fi
echo "启动服务uvicorn ${UVICORN_ARGS[*]}"
uvicorn "${UVICORN_ARGS[@]}"