#!/bin/bash # ============================================ # 山峡化工 shanxia2.0 Backend 启动脚本 # ============================================ set -e # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # 默认配置 HOST="0.0.0.0" PORT=8000 WORKERS=1 RELOAD=false LOG_LEVEL="info" # 脚本目录 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" BACKEND_DIR="$SCRIPT_DIR" # 虚拟环境目录 VENV_DIR="$BACKEND_DIR/venv" PIP="$VENV_DIR/bin/pip" PYTHON="$VENV_DIR/bin/python" UVICORN="$VENV_DIR/bin/uvicorn" # 日志文件 LOG_FILE="$BACKEND_DIR/logs/app.log" PID_FILE="$BACKEND_DIR/app.pid" # ============================================ # 函数定义 # ============================================ log_info() { echo -e "${GREEN}[INFO]${NC} $1" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } # 显示帮助信息 show_help() { cat << EOF 山峡化工 shanxia2.0 Backend 启动脚本 用法: $0 [选项] 选项: -h, --host 监听地址 (默认: 0.0.0.0) -p, --port 监听端口 (默认: 8000) -w, --workers 工作进程数 (默认: 1) -r, --reload 开发模式, 自动重载 -l, --log-level 日志级别: debug/info/warning/error (默认: info) --install 安装依赖 --init 初始化环境 --stop 停止服务 --restart 重启服务 --status 查看服务状态 --logs 查看日志 --help 显示帮助信息 示例: $0 --port 8080 --reload # 开发模式, 端口 8080 $0 --install # 安装依赖 $0 --stop # 停止服务 $0 --logs # 查看日志 EOF } # 检查 Python 是否安装 check_python() { if ! command -v python3 &> /dev/null; then log_error "Python3 未安装, 请先安装 Python 3.10+" exit 1 fi PYTHON_VERSION=$(python3 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') log_info "Python 版本: $PYTHON_VERSION" } # 创建虚拟环境 create_venv() { log_info "创建虚拟环境..." python3 -m venv "$VENV_DIR" log_success "虚拟环境创建完成: $VENV_DIR" } # 安装依赖 install_dependencies() { log_info "安装依赖..." # 检查虚拟环境 if [ ! -d "$VENV_DIR" ]; then create_venv fi # 激活虚拟环境 source "$VENV_DIR/bin/activate" # 升级 pip pip install --upgrade pip # 安装依赖 pip install -r "$BACKEND_DIR/requirements.txt" log_success "依赖安装完成" } # 检查环境 check_env() { # 检查虚拟环境 if [ ! -d "$VENV_DIR" ]; then log_warn "虚拟环境不存在, 正在创建..." create_venv install_dependencies fi # 检查依赖 if ! "$PYTHON" -c "import fastapi" &> /dev/null; then log_warn "依赖未安装, 正在安装..." install_dependencies fi # 检查 .env 文件 if [ ! -f "$BACKEND_DIR/.env" ]; then if [ -f "$BACKEND_DIR/.env.example" ]; then log_warn ".env 文件不存在, 复制 .env.example 作为模板" cp "$BACKEND_DIR/.env.example" "$BACKEND_DIR/.env" log_warn "请编辑 $BACKEND_DIR/.env 配置文件" else log_error ".env 文件不存在且无 .env.example 模板" fi fi } # 创建日志目录 setup_logs() { if [ ! -d "$BACKEND_DIR/logs" ]; then mkdir -p "$BACKEND_DIR/logs" log_info "创建日志目录: $BACKEND_DIR/logs" fi } # 启动服务 start_server() { # 检查服务是否已运行 if [ -f "$PID_FILE" ]; then PID=$(cat "$PID_FILE") if ps -p "$PID" &> /dev/null; then log_error "服务已在运行 (PID: $PID), 请先停止后再启动" exit 1 else log_warn "发现旧的 PID 文件, 已清理" rm -f "$PID_FILE" fi fi # 激活虚拟环境 source "$VENV_DIR/bin/activate" # 构建 uvicorn 命令 UVICORN_CMD="uvicorn app.main:app" UVICORN_CMD="$UVICORN_CMD --host $HOST" UVICORN_CMD="$UVICORN_CMD --port $PORT" UVICORN_CMD="$UVICORN_CMD --log-level $LOG_LEVEL" if [ "$RELOAD" = true ]; then UVICORN_CMD="$UVICORN_CMD --reload" log_info "启动模式: 开发模式 (自动重载)" else UVICORN_CMD="$UVICORN_CMD --workers $WORKERS" log_info "启动模式: 生产模式 (Workers: $WORKERS)" fi # 创建日志目录 setup_logs # 切换到后端目录 cd "$BACKEND_DIR" # 记录启动信息 echo "========================================" >> "$LOG_FILE" echo "启动时间: $(date '+%Y-%m-%d %H:%M:%S')" >> "$LOG_FILE" echo "启动参数: $UVICORN_CMD" >> "$LOG_FILE" echo "========================================" >> "$LOG_FILE" # 启动服务 log_info "==========================================" log_info " 山峡化工 shanxia2.0 Backend 启动中..." log_info "==========================================" log_info " 监听地址: $HOST:$PORT" log_info " 日志级别: $LOG_LEVEL" log_info " 日志文件: $LOG_FILE" log_info " 文档地址: http://$HOST:$PORT/docs" log_info "==========================================" # 后台运行并保存 PID nohup $UVICORN_CMD >> "$LOG_FILE" 2>&1 & echo $! > "$PID_FILE" # 等待服务启动 sleep 2 # 检查服务是否启动成功 if ps -p $(cat "$PID_FILE") &> /dev/null; then log_success "服务启动成功 (PID: $(cat "$PID_FILE"))" log_info "访问地址: http://localhost:$PORT" log_info "API 文档: http://localhost:$PORT/docs" else log_error "服务启动失败, 请查看日志: $LOG_FILE" rm -f "$PID_FILE" exit 1 fi } # 停止服务 stop_server() { if [ ! -f "$PID_FILE" ]; then log_error "PID 文件不存在, 服务可能未运行" exit 1 fi PID=$(cat "$PID_FILE") if ! ps -p "$PID" &> /dev/null; then log_warn "服务未在运行 (PID: $PID)" rm -f "$PID_FILE" exit 0 fi log_info "正在停止服务 (PID: $PID)..." # 尝试优雅关闭 kill -TERM "$PID" 2>/dev/null # 等待进程退出 for i in {1..10}; do if ! ps -p "$PID" &> /dev/null; then break fi sleep 1 done # 强制关闭 if ps -p "$PID" &> /dev/null; then log_warn "优雅关闭超时, 强制关闭..." kill -9 "$PID" 2>/dev/null fi rm -f "$PID_FILE" log_success "服务已停止" } # 查看状态 show_status() { if [ ! -f "$PID_FILE" ]; then echo -e "${YELLOW}[WARN]${NC} 服务未运行 (无 PID 文件)" exit 0 fi PID=$(cat "$PID_FILE") if ps -p "$PID" &> /dev/null; then echo -e "${GREEN}[RUNNING]${NC} 服务运行中 (PID: $PID)" # 获取端口信息 if command -v lsof &> /dev/null; then PORT_INFO=$(lsof -i :$PORT 2>/dev/null | grep LISTEN | head -1) if [ -n "$PORT_INFO" ]; then echo "" echo "端口信息:" lsof -i :$PORT 2>/dev/null | grep LISTEN | head -5 fi fi else echo -e "${RED}[STOPPED]${NC} 服务已停止" rm -f "$PID_FILE" fi } # 查看日志 show_logs() { if [ ! -f "$LOG_FILE" ]; then log_error "日志文件不存在: $LOG_FILE" exit 1 fi if [ "$1" = "-f" ] || [ "$1" = "--follow" ]; then log_info "实时查看日志 (Ctrl+C 退出)..." tail -f "$LOG_FILE" else echo -e "${BLUE}=== 最近 100 行日志 ===${NC}" tail -100 "$LOG_FILE" fi } # 重启服务 restart_server() { log_info "重启服务..." stop_server sleep 2 start_server } # 初始化环境 init_env() { check_python check_env log_success "环境初始化完成" } # ============================================ # 命令行参数解析 # ============================================ while [[ $# -gt 0 ]]; do case $1 in -h|--host) HOST="$2" shift 2 ;; -p|--port) PORT="$2" shift 2 ;; -w|--workers) WORKERS="$2" shift 2 ;; -r|--reload) RELOAD=true shift ;; -l|--log-level) LOG_LEVEL="$2" shift 2 ;; --install) check_python install_dependencies exit 0 ;; --init) init_env exit 0 ;; --stop) stop_server exit 0 ;; --restart) restart_server exit 0 ;; --status) show_status exit 0 ;; --logs) show_logs "${2:-}" exit 0 ;; --help) show_help exit 0 ;; *) log_error "未知参数: $1" show_help exit 1 ;; esac done # 默认启动 check_python check_env start_server