feat: 服务端集成自动打包流程
This commit is contained in:
@@ -33,6 +33,36 @@ info() { echo -e "${GREEN}[INFO]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $*"; }
|
||||
|
||||
# 在 PID 文件丢失或进程 PID 被复用时,仍能安全识别本服务。
|
||||
is_server_process() {
|
||||
local pid="$1"
|
||||
local command
|
||||
command=$(ps -p "$pid" -o command= 2>/dev/null || true)
|
||||
[[ "$command" == *"uvicorn backend.main:app"* ]]
|
||||
}
|
||||
|
||||
get_server_pid() {
|
||||
local pid
|
||||
if [ -f "$PID_FILE" ]; then
|
||||
pid=$(cat "$PID_FILE")
|
||||
if kill -0 "$pid" 2>/dev/null && is_server_process "$pid"; then
|
||||
echo "$pid"
|
||||
return 0
|
||||
fi
|
||||
rm -f "$PID_FILE"
|
||||
fi
|
||||
|
||||
# 仅认领监听当前配置端口、且命令明确属于本项目的 uvicorn 进程。
|
||||
for pid in $(lsof -tiTCP:"$BACKEND_PORT" -sTCP:LISTEN 2>/dev/null); do
|
||||
if is_server_process "$pid"; then
|
||||
echo "$pid" > "$PID_FILE"
|
||||
echo "$pid"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# 日志同时写文件和终端
|
||||
log_to_file() {
|
||||
local level="$1"; shift
|
||||
@@ -52,8 +82,9 @@ check_env() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v node &>/dev/null; then
|
||||
error "未找到 node"
|
||||
# 运行已构建的前端不需要 Node;仅在缺少静态资源时才需要它重新构建。
|
||||
if [ ! -d "$SCRIPT_DIR/backend/static" ] && ! command -v node &>/dev/null; then
|
||||
error "未找到 node,且前端尚未构建"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -65,12 +96,16 @@ check_env() {
|
||||
|
||||
# ========== Python 虚拟环境 ==========
|
||||
setup_venv() {
|
||||
# 项目目录移动后,venv 内 pip 的 shebang 会指向旧路径,需要重建。
|
||||
if [ -d "$VENV_DIR" ] && ! "$VENV_DIR/bin/pip" --version >/dev/null 2>&1; then
|
||||
warn "检测到失效的 Python 虚拟环境,正在重建..."
|
||||
rm -rf "$VENV_DIR"
|
||||
fi
|
||||
if [ ! -d "$VENV_DIR" ]; then
|
||||
info "创建 Python 虚拟环境..."
|
||||
python3 -m venv "$VENV_DIR"
|
||||
fi
|
||||
source "$VENV_DIR/bin/activate"
|
||||
pip install -r requirements.txt -q
|
||||
"$VENV_DIR/bin/python3" -m pip install -r requirements.txt -q
|
||||
}
|
||||
|
||||
# ========== 构建前端 ==========
|
||||
@@ -107,14 +142,11 @@ do_build() {
|
||||
do_start() {
|
||||
check_env
|
||||
|
||||
if [ -f "$PID_FILE" ]; then
|
||||
local pid
|
||||
pid=$(cat "$PID_FILE")
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
warn "服务已在运行 (PID: $pid)"
|
||||
return 0
|
||||
fi
|
||||
rm -f "$PID_FILE"
|
||||
local existing_pid
|
||||
existing_pid=$(get_server_pid || true)
|
||||
if [ -n "$existing_pid" ]; then
|
||||
warn "服务已在运行 (PID: $existing_pid)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 确保前端已构建
|
||||
@@ -128,7 +160,7 @@ do_start() {
|
||||
mkdir -p "$LOG_DIR"
|
||||
|
||||
info "启动后端服务 (端口: $BACKEND_PORT)..."
|
||||
nohup python3 -m uvicorn backend.main:app \
|
||||
nohup "$VENV_DIR/bin/python3" -m uvicorn backend.main:app \
|
||||
--host 0.0.0.0 \
|
||||
--port "$BACKEND_PORT" \
|
||||
--workers 1 \
|
||||
@@ -138,8 +170,11 @@ do_start() {
|
||||
sleep 1
|
||||
|
||||
if kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
|
||||
local access_host
|
||||
access_host=$(ipconfig getifaddr en0 2>/dev/null || true)
|
||||
access_host="${access_host:-localhost}"
|
||||
info "服务已启动 (PID: $(cat "$PID_FILE"))"
|
||||
info "访问地址: http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo 'localhost'):$BACKEND_PORT"
|
||||
info "访问地址: http://$access_host:$BACKEND_PORT"
|
||||
else
|
||||
error "启动失败,请查看日志: $LOG_DIR/server.log"
|
||||
rm -f "$PID_FILE"
|
||||
@@ -149,40 +184,37 @@ do_start() {
|
||||
|
||||
# ========== 停止服务 ==========
|
||||
do_stop() {
|
||||
if [ ! -f "$PID_FILE" ]; then
|
||||
local pid
|
||||
pid=$(get_server_pid || true)
|
||||
if [ -z "$pid" ]; then
|
||||
warn "服务未在运行"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local pid
|
||||
pid=$(cat "$PID_FILE")
|
||||
info "停止服务 (PID: $pid)..."
|
||||
kill "$pid"
|
||||
# 等待进程退出
|
||||
for i in $(seq 1 10); do
|
||||
kill -0 "$pid" 2>/dev/null || break
|
||||
sleep 0.5
|
||||
done
|
||||
# 还没退出就强杀
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
info "停止服务 (PID: $pid)..."
|
||||
kill "$pid"
|
||||
# 等待进程退出
|
||||
for i in $(seq 1 10); do
|
||||
kill -0 "$pid" 2>/dev/null || break
|
||||
sleep 0.5
|
||||
done
|
||||
# 还没退出就强杀
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
warn "进程未响应,强制终止..."
|
||||
kill -9 "$pid"
|
||||
fi
|
||||
info "服务已停止"
|
||||
else
|
||||
warn "进程已不存在"
|
||||
warn "进程未响应,强制终止..."
|
||||
kill -9 "$pid"
|
||||
fi
|
||||
info "服务已停止"
|
||||
rm -f "$PID_FILE"
|
||||
}
|
||||
|
||||
# ========== 查看状态 ==========
|
||||
do_status() {
|
||||
if [ -f "$PID_FILE" ] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
|
||||
info "服务运行中 (PID: $(cat "$PID_FILE")), 端口: $BACKEND_PORT"
|
||||
local pid
|
||||
pid=$(get_server_pid || true)
|
||||
if [ -n "$pid" ]; then
|
||||
info "服务运行中 (PID: $pid), 端口: $BACKEND_PORT"
|
||||
else
|
||||
warn "服务未运行"
|
||||
rm -f "$PID_FILE" 2>/dev/null
|
||||
fi
|
||||
|
||||
if [ -f "$WATCHDOG_PID_FILE" ] && kill -0 "$(cat "$WATCHDOG_PID_FILE")" 2>/dev/null; then
|
||||
@@ -196,7 +228,9 @@ do_status() {
|
||||
# ========== 健康检查 ==========
|
||||
check_health() {
|
||||
# 检查进程是否存在
|
||||
if [ ! -f "$PID_FILE" ] || ! kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
|
||||
local pid
|
||||
pid=$(get_server_pid || true)
|
||||
if [ -z "$pid" ]; then
|
||||
echo "process_down"
|
||||
return
|
||||
fi
|
||||
@@ -345,9 +379,9 @@ do_watchdog_loop() {
|
||||
fi
|
||||
|
||||
# 重新启动
|
||||
source "$VENV_DIR/bin/activate" 2>/dev/null
|
||||
setup_venv
|
||||
mkdir -p "$LOG_DIR"
|
||||
nohup python3 -m uvicorn backend.main:app \
|
||||
nohup "$VENV_DIR/bin/python3" -m uvicorn backend.main:app \
|
||||
--host 0.0.0.0 \
|
||||
--port "$BACKEND_PORT" \
|
||||
--workers 1 \
|
||||
@@ -392,11 +426,8 @@ do_test() {
|
||||
local failed=0
|
||||
|
||||
# 确保依赖已安装
|
||||
if [ ! -d "$VENV_DIR" ]; then
|
||||
setup_venv
|
||||
else
|
||||
source "$VENV_DIR/bin/activate"
|
||||
fi
|
||||
setup_venv
|
||||
"$VENV_DIR/bin/python3" -m pip install -r requirements-dev.txt -q
|
||||
|
||||
if [ ! -d "frontend/node_modules" ]; then
|
||||
cd frontend && npm install --silent && cd ..
|
||||
@@ -405,7 +436,7 @@ do_test() {
|
||||
# 后端测试
|
||||
info "运行后端测试..."
|
||||
echo "────────────────────────────────────────"
|
||||
python3 -m pytest tests/ -v "$@" || failed=1
|
||||
"$VENV_DIR/bin/python3" -m pytest tests/ -v "$@" || failed=1
|
||||
echo ""
|
||||
|
||||
# 前端测试
|
||||
|
||||
Reference in New Issue
Block a user