#!/bin/bash # LokiDeploy 启动脚本 # 一键启动前后端服务,支持日志轮转 # 用法: ./run.sh [start|stop|restart|status|frontend|backend|help] set -e # ============================================================ # 配置区 # ============================================================ PROJECT_NAME="LokiDeploy" BACKEND_PORT=8080 FRONTEND_PORT=3000 BACKEND_DIR="backend" FRONTEND_DIR="frontend" BACKEND_BIN="bin/lokideploy" # 日志配置 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 } # 检测 Go detect_go() { if command -v go &> /dev/null; then echo "go" elif [ -f "$HOME/go/bin/go" ]; then echo "$HOME/go/bin/go" else echo "" fi } cleanup() { : } # ============================================================ # 后端服务 (Go + Gin) # ============================================================ start_backend() { info "正在启动后端服务..." cd "$SCRIPT_DIR/$BACKEND_DIR" mkdir -p logs rotate_log "logs/backend.log" kill_port $BACKEND_PORT local GO_CMD=$(detect_go) if [ -z "$GO_CMD" ]; then error "Go 未安装,请先安装 Go 1.23+" return 1 fi # 编译二进制(若不存在或源码更新) if [ ! -f "$BACKEND_BIN" ]; then warning "编译后端二进制..." CGO_ENABLED=0 GOPROXY=https://goproxy.cn,direct $GO_CMD build -ldflags="-s -w" -o "$BACKEND_BIN" . || { error "后端编译失败" return 1 } fi # 启动(后台) export SERVER_PORT=$BACKEND_PORT export DATA_DIR="$SCRIPT_DIR/data" export JWT_SECRET="${JWT_SECRET:-lokideploy-secret-key-change-in-production}" nohup "./$BACKEND_BIN" >> logs/backend.log 2>&1 & echo $! > "$BACKEND_PID_FILE" # 等待服务启动 local retries=0 while [ -z "$(check_port $BACKEND_PORT)" ]; do retries=$((retries + 1)) if [ $retries -ge 30 ]; then error "后端服务启动超时,请检查日志: $BACKEND_DIR/logs/backend.log" return 1 fi sleep 1 done success "后端服务已启动 (PID: $(cat "$BACKEND_PID_FILE"), 端口: $BACKEND_PORT)" info "API: http://localhost:$BACKEND_PORT/api/v1" info "默认登录: admin / admin123" 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 } # ============================================================ # 前端服务 (Vite Dev Server) # ============================================================ 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 || { error "依赖安装失败"; return 1; } fi nohup npm run dev >> logs/dev.log 2>&1 & echo $! > "$FRONTEND_PID_FILE" # 等待服务启动 local retries=0 while [ -z "$(check_port $FRONTEND_PORT)" ]; do retries=$((retries + 1)) if [ $retries -ge 30 ]; then error "前端服务启动超时,请检查日志: $FRONTEND_DIR/logs/dev.log" return 1 fi sleep 1 done success "前端服务已启动 (PID: $(cat "$FRONTEND_PID_FILE"), 端口: $FRONTEND_PORT)" info "前端地址: http://localhost:$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 " 后端 API: http://localhost:$BACKEND_PORT/api/v1" echo " 前端页面: http://localhost:$FRONTEND_PORT" echo "" echo "示例:" echo " ./run.sh # 启动所有服务" echo " ./run.sh stop # 停止服务" echo " ./run.sh backend # 仅启动后端" } # ============================================================ # 主入口 # ============================================================ trap cleanup EXIT INT TERM main() { local command=${1:-start} case "$command" in start) info "========== $PROJECT_NAME 启动脚本 ==========" if [ -z "$(detect_go)" ]; then error "Go 未安装,请先安装 Go 1.23+" 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 "后端 API: http://localhost:$BACKEND_PORT/api/v1" info "默认登录: admin / admin123" echo "" info "使用 './run.sh stop' 停止服务" info "使用 './run.sh status' 查看状态" 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 "$@"