#!/bin/bash # EZOps AIOps 启动脚本 # 一键启动前后端服务,支持日志轮转 # 用法: ./run.sh [start|stop|restart|status|frontend|backend|help] set -e # ============================================================ # 配置区 # ============================================================ PROJECT_NAME="EZOps AIOps" BACKEND_PORT=8080 FRONTEND_PORT=5173 BACKEND_DIR="backend" FRONTEND_DIR="frontend" BACKEND_BIN="aiops-server" # 日志配置 MAX_LOG_FILES=3 MAX_LOG_SIZE=$((50 * 1024 * 1024)) # 50MB # ============================================================ # 通用函数 # ============================================================ RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' info() { echo -e "${BLUE}[INFO]${NC} $1"; } success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; } error() { echo -e "${RED}[ERROR]${NC} $1"; } SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" # PID 文件 BACKEND_PID_FILE="$SCRIPT_DIR/$BACKEND_DIR/.backend.pid" FRONTEND_PID_FILE="$SCRIPT_DIR/$FRONTEND_DIR/.frontend.pid" # 检查端口是否被占用 check_port() { lsof -ti:$1 2>/dev/null | head -1 } # 释放端口 kill_port() { local pids=$(lsof -ti:$1 2>/dev/null || echo "") if [ -n "$pids" ]; then info "释放端口 $1..." echo "$pids" | xargs kill -9 2>/dev/null || true sleep 1 fi } # 日志轮转(仅启动时调用) rotate_log() { local log_file=$1 [ ! -f "$log_file" ] && return 0 local file_size=$(stat -f%z "$log_file" 2>/dev/null || stat -c%s "$log_file" 2>/dev/null || echo "0") if [ "$file_size" -gt "$MAX_LOG_SIZE" ]; then [ -f "${log_file}.${MAX_LOG_FILES}" ] && rm -f "${log_file}.${MAX_LOG_FILES}" for i in $(seq $((MAX_LOG_FILES - 1)) -1 1); do [ -f "${log_file}.${i}" ] && mv "${log_file}.${i}" "${log_file}.$((i + 1))" done mv "$log_file" "${log_file}.1" info "日志已轮转: $log_file" fi } # ============================================================ # 后端服务 # ============================================================ start_backend() { info "正在启动后端服务..." cd "$SCRIPT_DIR/$BACKEND_DIR" mkdir -p logs data rotate_log "logs/server.log" # 释放被占用的端口 kill_port $BACKEND_PORT export PORT=$BACKEND_PORT export GIN_MODE=debug # 确保使用国内代理 export GOPROXY=https://goproxy.cn,direct # 编译并运行 info "编译 Go 程序..." if ! go build -o "$BACKEND_BIN" cmd/main.go; then error "Go 编译失败" return 1 fi nohup ./"$BACKEND_BIN" >> logs/server.log 2>&1 & echo $! > "$BACKEND_PID_FILE" # 等待服务启动 local retries=0 while ! curl -s "http://localhost:$BACKEND_PORT/health" > /dev/null 2>&1; do retries=$((retries + 1)) if [ $retries -ge 30 ]; then error "后端服务启动超时,请检查日志: $BACKEND_DIR/logs/server.log" tail -20 "$BACKEND_DIR/logs/server.log" 2>/dev/null return 1 fi sleep 1 done success "后端服务已启动 (PID: $(cat "$BACKEND_PID_FILE"), 端口: $BACKEND_PORT)" return 0 } stop_backend() { if [ -f "$BACKEND_PID_FILE" ]; then local pid=$(cat "$BACKEND_PID_FILE" 2>/dev/null || echo "") if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then kill "$pid" 2>/dev/null || true success "后端服务已停止" fi rm -f "$BACKEND_PID_FILE" fi kill_port $BACKEND_PORT } # ============================================================ # 前端服务 # ============================================================ start_frontend() { info "正在启动前端服务..." cd "$SCRIPT_DIR/$FRONTEND_DIR" mkdir -p logs rotate_log "logs/dev.log" kill_port $FRONTEND_PORT if [ ! -d "node_modules" ]; then warning "node_modules 不存在,正在安装依赖..." npm install fi nohup npm run dev >> logs/dev.log 2>&1 & echo $! > "$FRONTEND_PID_FILE" # 等待服务启动 local retries=0 while ! curl -s "http://localhost:$FRONTEND_PORT" > /dev/null 2>&1; do retries=$((retries + 1)) if [ $retries -ge 30 ]; then error "前端服务启动超时,请检查日志: $FRONTEND_DIR/logs/dev.log" tail -20 "$FRONTEND_DIR/logs/dev.log" 2>/dev/null return 1 fi sleep 1 done success "前端服务已启动 (PID: $(cat "$FRONTEND_PID_FILE"), 端口: $FRONTEND_PORT)" return 0 } stop_frontend() { if [ -f "$FRONTEND_PID_FILE" ]; then local pid=$(cat "$FRONTEND_PID_FILE" 2>/dev/null || echo "") if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then kill "$pid" 2>/dev/null || true success "前端服务已停止" fi rm -f "$FRONTEND_PID_FILE" fi kill_port $FRONTEND_PORT } # ============================================================ # 命令 # ============================================================ stop() { info "正在停止服务..." stop_backend stop_frontend info "所有服务已停止" } show_status() { info "服务状态:" if [ -n "$(check_port $BACKEND_PORT)" ]; then success "后端服务: 运行中 (PID: $(check_port $BACKEND_PORT), 端口: $BACKEND_PORT)" else warning "后端服务: 未运行" fi if [ -n "$(check_port $FRONTEND_PORT)" ]; then success "前端服务: 运行中 (PID: $(check_port $FRONTEND_PORT), 端口: $FRONTEND_PORT)" else warning "前端服务: 未运行" fi } show_help() { echo "$PROJECT_NAME 启动脚本" echo "" echo "用法: ./run.sh [命令]" echo "" echo "命令:" echo " start 启动前后端服务 (默认)" echo " stop 停止前后端服务" echo " restart 重启前后端服务" echo " backend 仅启动后端服务" echo " frontend 仅启动前端服务" echo " status 查看服务状态" echo " help 显示帮助信息" echo "" echo "示例:" echo " ./run.sh # 启动所有服务" echo " ./run.sh stop # 停止服务" echo " ./run.sh restart # 重启服务" echo " ./run.sh backend # 仅启动后端" } # ============================================================ # 主入口 # ============================================================ main() { local command=${1:-start} case "$command" in start) info "========== $PROJECT_NAME 启动脚本 ==========" # 检查依赖 if ! command -v go &> /dev/null; then error "Go 未安装,请先安装 Go" exit 1 fi if ! command -v npm &> /dev/null; then error "npm 未安装,请先安装 Node.js" exit 1 fi if start_backend && start_frontend; then echo "" success "========== 所有服务已启动 ==========" info "前端地址: http://localhost:$FRONTEND_PORT" info "后端地址: http://localhost:$BACKEND_PORT" info "健康检查: http://localhost:$BACKEND_PORT/health" echo "" info "使用 './run.sh stop' 停止服务" else error "服务启动失败" exit 1 fi ;; stop) stop ;; restart) stop sleep 2 main start ;; backend) info "========== $PROJECT_NAME (仅后端) ==========" start_backend show_status ;; frontend) info "========== $PROJECT_NAME (仅前端) ==========" start_frontend show_status ;; status) show_status ;; help|--help|-h) show_help ;; *) error "未知命令: $command" show_help exit 1 ;; esac } main "$@"