start.sh 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. #!/bin/bash
  2. # ============================================
  3. # 陇耘鲜达 Backend 启动脚本
  4. # ============================================
  5. set -e
  6. # 颜色定义
  7. RED='\033[0;31m'
  8. GREEN='\033[0;32m'
  9. YELLOW='\033[1;33m'
  10. BLUE='\033[0;34m'
  11. NC='\033[0m' # No Color
  12. # 默认配置
  13. HOST="0.0.0.0"
  14. PORT=8000
  15. WORKERS=1
  16. RELOAD=false
  17. LOG_LEVEL="info"
  18. # 脚本目录
  19. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  20. BACKEND_DIR="$SCRIPT_DIR"
  21. # 虚拟环境目录
  22. VENV_DIR="$BACKEND_DIR/venv"
  23. PIP="$VENV_DIR/bin/pip"
  24. PYTHON="$VENV_DIR/bin/python"
  25. UVICORN="$VENV_DIR/bin/uvicorn"
  26. # 日志文件
  27. LOG_FILE="$BACKEND_DIR/logs/app.log"
  28. PID_FILE="$BACKEND_DIR/app.pid"
  29. # ============================================
  30. # 函数定义
  31. # ============================================
  32. log_info() {
  33. echo -e "${GREEN}[INFO]${NC} $1"
  34. }
  35. log_warn() {
  36. echo -e "${YELLOW}[WARN]${NC} $1"
  37. }
  38. log_error() {
  39. echo -e "${RED}[ERROR]${NC} $1"
  40. }
  41. log_success() {
  42. echo -e "${GREEN}[SUCCESS]${NC} $1"
  43. }
  44. # 显示帮助信息
  45. show_help() {
  46. cat << EOF
  47. 陇耘鲜达 Backend 启动脚本
  48. 用法: $0 [选项]
  49. 选项:
  50. -h, --host <ip> 监听地址 (默认: 0.0.0.0)
  51. -p, --port <port> 监听端口 (默认: 8000)
  52. -w, --workers <num> 工作进程数 (默认: 1)
  53. -r, --reload 开发模式, 自动重载
  54. -l, --log-level 日志级别: debug/info/warning/error (默认: info)
  55. --install 安装依赖
  56. --init 初始化环境
  57. --stop 停止服务
  58. --restart 重启服务
  59. --status 查看服务状态
  60. --logs 查看日志
  61. --help 显示帮助信息
  62. 示例:
  63. $0 --port 8080 --reload # 开发模式, 端口 8080
  64. $0 --install # 安装依赖
  65. $0 --stop # 停止服务
  66. $0 --logs # 查看日志
  67. EOF
  68. }
  69. # 检查 Python 是否安装
  70. check_python() {
  71. if ! command -v python3 &> /dev/null; then
  72. log_error "Python3 未安装, 请先安装 Python 3.10+"
  73. exit 1
  74. fi
  75. PYTHON_VERSION=$(python3 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))')
  76. log_info "Python 版本: $PYTHON_VERSION"
  77. }
  78. # 创建虚拟环境
  79. create_venv() {
  80. log_info "创建虚拟环境..."
  81. python3 -m venv "$VENV_DIR"
  82. log_success "虚拟环境创建完成: $VENV_DIR"
  83. }
  84. # 安装依赖
  85. install_dependencies() {
  86. log_info "安装依赖..."
  87. # 检查虚拟环境
  88. if [ ! -d "$VENV_DIR" ]; then
  89. create_venv
  90. fi
  91. # 激活虚拟环境
  92. source "$VENV_DIR/bin/activate"
  93. # 升级 pip
  94. pip install --upgrade pip
  95. # 安装依赖
  96. pip install -r "$BACKEND_DIR/requirements.txt"
  97. log_success "依赖安装完成"
  98. }
  99. # 检查环境
  100. check_env() {
  101. # 检查虚拟环境
  102. if [ ! -d "$VENV_DIR" ]; then
  103. log_warn "虚拟环境不存在, 正在创建..."
  104. create_venv
  105. install_dependencies
  106. fi
  107. # 检查依赖
  108. if ! "$PYTHON" -c "import fastapi" &> /dev/null; then
  109. log_warn "依赖未安装, 正在安装..."
  110. install_dependencies
  111. fi
  112. # 检查 .env 文件
  113. if [ ! -f "$BACKEND_DIR/.env" ]; then
  114. if [ -f "$BACKEND_DIR/.env.example" ]; then
  115. log_warn ".env 文件不存在, 复制 .env.example 作为模板"
  116. cp "$BACKEND_DIR/.env.example" "$BACKEND_DIR/.env"
  117. log_warn "请编辑 $BACKEND_DIR/.env 配置文件"
  118. else
  119. log_error ".env 文件不存在且无 .env.example 模板"
  120. fi
  121. fi
  122. }
  123. # 创建日志目录
  124. setup_logs() {
  125. if [ ! -d "$BACKEND_DIR/logs" ]; then
  126. mkdir -p "$BACKEND_DIR/logs"
  127. log_info "创建日志目录: $BACKEND_DIR/logs"
  128. fi
  129. }
  130. # 启动服务
  131. start_server() {
  132. # 检查服务是否已运行
  133. if [ -f "$PID_FILE" ]; then
  134. PID=$(cat "$PID_FILE")
  135. if ps -p "$PID" &> /dev/null; then
  136. log_error "服务已在运行 (PID: $PID), 请先停止后再启动"
  137. exit 1
  138. else
  139. log_warn "发现旧的 PID 文件, 已清理"
  140. rm -f "$PID_FILE"
  141. fi
  142. fi
  143. # 激活虚拟环境
  144. source "$VENV_DIR/bin/activate"
  145. # 构建 uvicorn 命令
  146. UVICORN_CMD="uvicorn app.main:app"
  147. UVICORN_CMD="$UVICORN_CMD --host $HOST"
  148. UVICORN_CMD="$UVICORN_CMD --port $PORT"
  149. UVICORN_CMD="$UVICORN_CMD --log-level $LOG_LEVEL"
  150. if [ "$RELOAD" = true ]; then
  151. UVICORN_CMD="$UVICORN_CMD --reload"
  152. log_info "启动模式: 开发模式 (自动重载)"
  153. else
  154. UVICORN_CMD="$UVICORN_CMD --workers $WORKERS"
  155. log_info "启动模式: 生产模式 (Workers: $WORKERS)"
  156. fi
  157. # 创建日志目录
  158. setup_logs
  159. # 切换到后端目录
  160. cd "$BACKEND_DIR"
  161. # 记录启动信息
  162. echo "========================================" >> "$LOG_FILE"
  163. echo "启动时间: $(date '+%Y-%m-%d %H:%M:%S')" >> "$LOG_FILE"
  164. echo "启动参数: $UVICORN_CMD" >> "$LOG_FILE"
  165. echo "========================================" >> "$LOG_FILE"
  166. # 启动服务
  167. log_info "=========================================="
  168. log_info " 陇耘鲜达 Backend 启动中..."
  169. log_info "=========================================="
  170. log_info " 监听地址: $HOST:$PORT"
  171. log_info " 日志级别: $LOG_LEVEL"
  172. log_info " 日志文件: $LOG_FILE"
  173. log_info " 文档地址: http://$HOST:$PORT/docs"
  174. log_info "=========================================="
  175. # 后台运行并保存 PID
  176. nohup $UVICORN_CMD >> "$LOG_FILE" 2>&1 &
  177. echo $! > "$PID_FILE"
  178. # 等待服务启动
  179. sleep 2
  180. # 检查服务是否启动成功
  181. if ps -p $(cat "$PID_FILE") &> /dev/null; then
  182. log_success "服务启动成功 (PID: $(cat "$PID_FILE"))"
  183. log_info "访问地址: http://localhost:$PORT"
  184. log_info "API 文档: http://localhost:$PORT/docs"
  185. else
  186. log_error "服务启动失败, 请查看日志: $LOG_FILE"
  187. rm -f "$PID_FILE"
  188. exit 1
  189. fi
  190. }
  191. # 停止服务
  192. stop_server() {
  193. if [ ! -f "$PID_FILE" ]; then
  194. log_error "PID 文件不存在, 服务可能未运行"
  195. exit 1
  196. fi
  197. PID=$(cat "$PID_FILE")
  198. if ! ps -p "$PID" &> /dev/null; then
  199. log_warn "服务未在运行 (PID: $PID)"
  200. rm -f "$PID_FILE"
  201. exit 0
  202. fi
  203. log_info "正在停止服务 (PID: $PID)..."
  204. # 尝试优雅关闭
  205. kill -TERM "$PID" 2>/dev/null
  206. # 等待进程退出
  207. for i in {1..10}; do
  208. if ! ps -p "$PID" &> /dev/null; then
  209. break
  210. fi
  211. sleep 1
  212. done
  213. # 强制关闭
  214. if ps -p "$PID" &> /dev/null; then
  215. log_warn "优雅关闭超时, 强制关闭..."
  216. kill -9 "$PID" 2>/dev/null
  217. fi
  218. rm -f "$PID_FILE"
  219. log_success "服务已停止"
  220. }
  221. # 查看状态
  222. show_status() {
  223. if [ ! -f "$PID_FILE" ]; then
  224. echo -e "${YELLOW}[WARN]${NC} 服务未运行 (无 PID 文件)"
  225. exit 0
  226. fi
  227. PID=$(cat "$PID_FILE")
  228. if ps -p "$PID" &> /dev/null; then
  229. echo -e "${GREEN}[RUNNING]${NC} 服务运行中 (PID: $PID)"
  230. # 获取端口信息
  231. if command -v lsof &> /dev/null; then
  232. PORT_INFO=$(lsof -i :$PORT 2>/dev/null | grep LISTEN | head -1)
  233. if [ -n "$PORT_INFO" ]; then
  234. echo ""
  235. echo "端口信息:"
  236. lsof -i :$PORT 2>/dev/null | grep LISTEN | head -5
  237. fi
  238. fi
  239. else
  240. echo -e "${RED}[STOPPED]${NC} 服务已停止"
  241. rm -f "$PID_FILE"
  242. fi
  243. }
  244. # 查看日志
  245. show_logs() {
  246. if [ ! -f "$LOG_FILE" ]; then
  247. log_error "日志文件不存在: $LOG_FILE"
  248. exit 1
  249. fi
  250. if [ "$1" = "-f" ] || [ "$1" = "--follow" ]; then
  251. log_info "实时查看日志 (Ctrl+C 退出)..."
  252. tail -f "$LOG_FILE"
  253. else
  254. echo -e "${BLUE}=== 最近 100 行日志 ===${NC}"
  255. tail -100 "$LOG_FILE"
  256. fi
  257. }
  258. # 重启服务
  259. restart_server() {
  260. log_info "重启服务..."
  261. stop_server
  262. sleep 2
  263. start_server
  264. }
  265. # 初始化环境
  266. init_env() {
  267. check_python
  268. check_env
  269. log_success "环境初始化完成"
  270. }
  271. # ============================================
  272. # 命令行参数解析
  273. # ============================================
  274. while [[ $# -gt 0 ]]; do
  275. case $1 in
  276. -h|--host)
  277. HOST="$2"
  278. shift 2
  279. ;;
  280. -p|--port)
  281. PORT="$2"
  282. shift 2
  283. ;;
  284. -w|--workers)
  285. WORKERS="$2"
  286. shift 2
  287. ;;
  288. -r|--reload)
  289. RELOAD=true
  290. shift
  291. ;;
  292. -l|--log-level)
  293. LOG_LEVEL="$2"
  294. shift 2
  295. ;;
  296. --install)
  297. check_python
  298. install_dependencies
  299. exit 0
  300. ;;
  301. --init)
  302. init_env
  303. exit 0
  304. ;;
  305. --stop)
  306. stop_server
  307. exit 0
  308. ;;
  309. --restart)
  310. restart_server
  311. exit 0
  312. ;;
  313. --status)
  314. show_status
  315. exit 0
  316. ;;
  317. --logs)
  318. show_logs "${2:-}"
  319. exit 0
  320. ;;
  321. --help)
  322. show_help
  323. exit 0
  324. ;;
  325. *)
  326. log_error "未知参数: $1"
  327. show_help
  328. exit 1
  329. ;;
  330. esac
  331. done
  332. # 默认启动
  333. check_python
  334. check_env
  335. start_server