Files
2026-05-30 14:03:11 +08:00

394 lines
9.3 KiB
Bash
Executable File

#!/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 <ip> 监听地址 (默认: 0.0.0.0)
-p, --port <port> 监听端口 (默认: 8000)
-w, --workers <num> 工作进程数 (默认: 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