添加一键启动脚本 start.sh (start/stop/restart/status/logs)
This commit is contained in:
@@ -0,0 +1,249 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# KVM Manager 一键启动脚本
|
||||||
|
# 用法: ./start.sh [start|stop|restart|status]
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# ── 配置 ──────────────────────────────────────────────
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
BACKEND_DIR="$SCRIPT_DIR/backend"
|
||||||
|
FRONTEND_DIR="$SCRIPT_DIR/frontend"
|
||||||
|
LOG_DIR="$SCRIPT_DIR/logs"
|
||||||
|
PID_DIR="$SCRIPT_DIR/.pid"
|
||||||
|
|
||||||
|
BACKEND_PORT=8004
|
||||||
|
FRONTEND_PORT=8006
|
||||||
|
BACKEND_CMD="$BACKEND_DIR/venv/bin/python -m uvicorn app.main:app --host 0.0.0.0 --port $BACKEND_PORT"
|
||||||
|
FRONTEND_CMD="$BACKEND_DIR/venv/bin/python $FRONTEND_DIR/serve.py"
|
||||||
|
|
||||||
|
# ── 颜色 ──────────────────────────────────────────────
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
# ── 工具函数 ──────────────────────────────────────────
|
||||||
|
info() { echo -e "${CYAN}[INFO]${NC} $1"; }
|
||||||
|
ok() { echo -e "${GREEN}[OK]${NC} $1"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||||
|
fail() { echo -e "${RED}[FAIL]${NC} $1"; }
|
||||||
|
|
||||||
|
mkdir -p "$LOG_DIR" "$PID_DIR"
|
||||||
|
|
||||||
|
# 检查端口是否被占用,返回 PID
|
||||||
|
port_pid() {
|
||||||
|
lsof -ti:$1 2>/dev/null | head -1
|
||||||
|
}
|
||||||
|
|
||||||
|
# 等待端口可用
|
||||||
|
wait_port() {
|
||||||
|
local port=$1 name=$2 max=10 count=0
|
||||||
|
while [ $count -lt $max ]; do
|
||||||
|
if port_pid $port >/dev/null 2>&1; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
count=$((count + 1))
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 启动后端 ──────────────────────────────────────────
|
||||||
|
start_backend() {
|
||||||
|
info "启动后端服务 (端口 $BACKEND_PORT)..."
|
||||||
|
|
||||||
|
# 检查是否已运行
|
||||||
|
local existing_pid=$(port_pid $BACKEND_PORT)
|
||||||
|
if [ -n "$existing_pid" ]; then
|
||||||
|
warn "后端已在运行 (PID: $existing_pid)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查 venv
|
||||||
|
if [ ! -d "$BACKEND_DIR/venv" ]; then
|
||||||
|
fail "未找到 Python 虚拟环境,请先运行: cd backend && python3 -m venv venv && source venv/bin/activate && pip install -r requirements.txt"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查 libvirtd
|
||||||
|
if ! systemctl is-active --quiet libvirtd 2>/dev/null; then
|
||||||
|
warn "libvirtd 未运行,尝试启动..."
|
||||||
|
systemctl start libvirtd 2>/dev/null || warn "libvirtd 启动失败,后端可能无法连接虚拟机"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$BACKEND_DIR"
|
||||||
|
nohup $BACKEND_CMD > "$LOG_DIR/backend.log" 2>&1 &
|
||||||
|
local pid=$!
|
||||||
|
echo $pid > "$PID_DIR/backend.pid"
|
||||||
|
|
||||||
|
sleep 2
|
||||||
|
if port_pid $BACKEND_PORT >/dev/null 2>&1; then
|
||||||
|
ok "后端服务已启动 (PID: $pid, 端口: $BACKEND_PORT)"
|
||||||
|
ok "日志: $LOG_DIR/backend.log"
|
||||||
|
else
|
||||||
|
fail "后端服务启动失败,查看日志:"
|
||||||
|
tail -20 "$LOG_DIR/backend.log"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 启动前端 ──────────────────────────────────────────
|
||||||
|
start_frontend() {
|
||||||
|
info "启动前端服务 (端口 $FRONTEND_PORT)..."
|
||||||
|
|
||||||
|
local existing_pid=$(port_pid $FRONTEND_PORT)
|
||||||
|
if [ -n "$existing_pid" ]; then
|
||||||
|
warn "前端已在运行 (PID: $existing_pid)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查 dist
|
||||||
|
if [ ! -d "$FRONTEND_DIR/dist" ]; then
|
||||||
|
warn "未找到前端构建产物,正在构建..."
|
||||||
|
cd "$FRONTEND_DIR"
|
||||||
|
npm install --silent && npm run build
|
||||||
|
if [ ! -d "$FRONTEND_DIR/dist" ]; then
|
||||||
|
fail "前端构建失败"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
ok "前端构建完成"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$FRONTEND_DIR"
|
||||||
|
nohup $FRONTEND_CMD > "$LOG_DIR/frontend.log" 2>&1 &
|
||||||
|
local pid=$!
|
||||||
|
echo $pid > "$PID_DIR/frontend.pid"
|
||||||
|
|
||||||
|
sleep 2
|
||||||
|
if port_pid $FRONTEND_PORT >/dev/null 2>&1; then
|
||||||
|
ok "前端服务已启动 (PID: $pid, 端口: $FRONTEND_PORT)"
|
||||||
|
ok "日志: $LOG_DIR/frontend.log"
|
||||||
|
else
|
||||||
|
fail "前端服务启动失败,查看日志:"
|
||||||
|
tail -20 "$LOG_DIR/frontend.log"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 停止服务 ──────────────────────────────────────────
|
||||||
|
stop_service() {
|
||||||
|
local name=$1 port=$2 pidfile=$PID_DIR/$3.pid
|
||||||
|
info "停止 $name (端口 $port)..."
|
||||||
|
|
||||||
|
# 先尝试 PID 文件
|
||||||
|
if [ -f "$pidfile" ]; then
|
||||||
|
local pid=$(cat "$pidfile")
|
||||||
|
if ps -p "$pid" >/dev/null 2>&1; then
|
||||||
|
kill "$pid" 2>/dev/null && ok "$name 已停止 (PID: $pid)" || warn "$name 停止失败"
|
||||||
|
fi
|
||||||
|
rm -f "$pidfile"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 再检查端口残留
|
||||||
|
local residual=$(port_pid $port)
|
||||||
|
if [ -n "$residual" ]; then
|
||||||
|
kill "$residual" 2>/dev/null && ok "$name 残留进程已清理 (PID: $residual)" || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_all() {
|
||||||
|
info "停止所有服务..."
|
||||||
|
stop_service "后端" $BACKEND_PORT "backend"
|
||||||
|
stop_service "前端" $FRONTEND_PORT "frontend"
|
||||||
|
ok "所有服务已停止"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 状态 ──────────────────────────────────────────────
|
||||||
|
show_status() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}========================================${NC}"
|
||||||
|
echo -e "${CYAN} KVM Manager 服务状态${NC}"
|
||||||
|
echo -e "${CYAN}========================================${NC}"
|
||||||
|
|
||||||
|
# 后端
|
||||||
|
local bpid=$(port_pid $BACKEND_PORT)
|
||||||
|
if [ -n "$bpid" ]; then
|
||||||
|
echo -e " 后端: ${GREEN}● 运行中${NC} PID: $bpid 端口: $BACKEND_PORT"
|
||||||
|
else
|
||||||
|
echo -e " 后端: ${RED}○ 未运行${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 前端
|
||||||
|
local fpid=$(port_pid $FRONTEND_PORT)
|
||||||
|
if [ -n "$fpid" ]; then
|
||||||
|
echo -e " 前端: ${GREEN}● 运行中${NC} PID: $fpid 端口: $FRONTEND_PORT"
|
||||||
|
else
|
||||||
|
echo -e " 前端: ${RED}○ 未运行${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e " 访问地址: ${YELLOW}http://localhost:$FRONTEND_PORT${NC}"
|
||||||
|
echo -e " API文档: ${YELLOW}http://localhost:$BACKEND_PORT/docs${NC}"
|
||||||
|
echo -e " 日志目录: ${YELLOW}$LOG_DIR${NC}"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 查看日志 ──────────────────────────────────────────
|
||||||
|
show_logs() {
|
||||||
|
local target=${1:-all}
|
||||||
|
case $target in
|
||||||
|
backend|b) tail -f "$LOG_DIR/backend.log" 2>/dev/null || fail "后端日志不存在" ;;
|
||||||
|
frontend|f) tail -f "$LOG_DIR/frontend.log" 2>/dev/null || fail "前端日志不存在" ;;
|
||||||
|
*) tail -f "$LOG_DIR/backend.log" "$LOG_DIR/frontend.log" 2>/dev/null || fail "日志不存在" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 主入口 ────────────────────────────────────────────
|
||||||
|
usage() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}KVM Manager 一键启动脚本${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "用法: $0 <命令>"
|
||||||
|
echo ""
|
||||||
|
echo "命令:"
|
||||||
|
echo " start 启动所有服务 (默认)"
|
||||||
|
echo " stop 停止所有服务"
|
||||||
|
echo " restart 重启所有服务"
|
||||||
|
echo " status 查看服务状态"
|
||||||
|
echo " logs 查看日志 (logs backend/frontend)"
|
||||||
|
echo ""
|
||||||
|
echo "示例:"
|
||||||
|
echo " $0 # 启动所有"
|
||||||
|
echo " $0 start # 启动所有"
|
||||||
|
echo " $0 stop # 停止所有"
|
||||||
|
echo " $0 restart # 重启所有"
|
||||||
|
echo " $0 status # 查看状态"
|
||||||
|
echo " $0 logs backend # 查看后端日志"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${1:-start}" in
|
||||||
|
start)
|
||||||
|
start_backend
|
||||||
|
start_frontend
|
||||||
|
show_status
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
stop_all
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
stop_all
|
||||||
|
sleep 1
|
||||||
|
start_backend
|
||||||
|
start_frontend
|
||||||
|
show_status
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
show_status
|
||||||
|
;;
|
||||||
|
logs)
|
||||||
|
show_logs "${2:-all}"
|
||||||
|
;;
|
||||||
|
-h|--help|help)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Reference in New Issue
Block a user