| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- #!/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
|