394 lines
9.3 KiB
Bash
Executable File
394 lines
9.3 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# ============================================
|
|
# 陇耘鲜达 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
|
|
陇耘鲜达 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 " 陇耘鲜达 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
|