package cmd import ( "context" "fmt" "net/http" "os" "os/signal" "syscall" "time" "NetworkAuth/database" "NetworkAuth/middleware" "NetworkAuth/models" "NetworkAuth/server" "NetworkAuth/services" "NetworkAuth/utils" "NetworkAuth/utils/logger" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" ) // serverCmd 代表服务器命令 var serverCmd = &cobra.Command{ Use: "server", Short: "启动 NetworkAuth 系统服务器", Long: `启动 NetworkAuth 系统 HTTP 服务器,监听配置文件中指定的端口,提供 Web 管理界面和 API 服务。`, Run: runServer, } func init() { // 将服务器命令添加到根命令 rootCmd.AddCommand(serverCmd) // 添加服务器特定的标志 serverCmd.Flags().StringP("host", "H", "", "服务器监听地址 (覆盖配置文件)") serverCmd.Flags().IntP("port", "p", 0, "服务器监听端口 (覆盖配置文件)") } // runServer 运行HTTP服务器 func runServer(cmd *cobra.Command, args []string) { // 获取配置 host := getServerHost(cmd) port := getServerPort(cmd) addr := fmt.Sprintf("%s:%d", host, port) // 获取全局日志实例 logger := logger.GetLogger() logger.LogServerStart(host, port) // 重定向 Gin 框架内部日志到 Logrus // 这将捕获 [GIN-debug] 路由注册日志和其他框架级输出 gin.DefaultWriter = logger.WriterLevel(logrus.DebugLevel) gin.DefaultErrorWriter = logger.WriterLevel(logrus.ErrorLevel) // 设置 Gin 模式 if !viper.GetBool("server.dev_mode") { gin.SetMode(gin.ReleaseMode) } // 初始化Redis(如果配置存在,失败不致命) utils.InitRedis() // 初始化数据库(根据 viper 配置选择 SQLite 或 MySQL) // 如果初始化失败(例如 MySQL 连不上),则打印错误并退出 db, err := database.Init() if err != nil { logrus.WithError(err).Fatal("数据库初始化失败,请检查配置或确认是否已安装") } if db != nil { // 检查系统是否已安装 isInstalled := false if db.Migrator().HasTable(&models.Settings{}) { var setting models.Settings if err := db.Where("name = ?", "is_installed").First(&setting).Error; err == nil { isInstalled = setting.Value == "1" } } if isInstalled { needSeedPortalNavigation, err := database.NeedSeedDefaultPortalNavigation() if err != nil { logrus.WithError(err).Fatal("检测默认门户导航状态失败") } // 执行自动迁移(确保表结构存在) if err := database.AutoMigrate(); err != nil { logrus.WithError(err).Fatal("数据库自动迁移失败") } // 初始化默认系统设置 if err := database.SeedDefaultSettings(); err != nil { logrus.WithError(err).Fatal("默认系统设置初始化失败") } if needSeedPortalNavigation { if err := database.SeedDefaultPortalNavigation(); err != nil { logrus.WithError(err).Fatal("默认门户导航初始化失败") } } // 初始化加密管理器 // 从数据库设置中获取加密密钥 encryptionKey := services.GetSettingsService().GetEncryptionKey() if err := utils.InitEncryption(encryptionKey); err != nil { logrus.WithError(err).Fatal("加密管理器初始化失败") } // 启动日志清理定时任务 services.StartLogCleanupTask() } else { logrus.Info("系统处于未安装状态,跳过数据库自动迁移和核心组件初始化") } } else { logrus.Info("系统处于未初始化状态,跳过数据库自动迁移和设置加载") } // 创建HTTP服务器 server := createHTTPServer(addr) // 启动服务器 startServer(server) } // getServerHost 获取服务器监听地址 func getServerHost(cmd *cobra.Command) string { if host, _ := cmd.Flags().GetString("host"); host != "" { return host } return viper.GetString("server.host") } // getServerPort 获取服务器监听端口 func getServerPort(cmd *cobra.Command) int { if port, _ := cmd.Flags().GetInt("port"); port != 0 { return port } return viper.GetInt("server.port") } // createHTTPServer 创建HTTP服务器 func createHTTPServer(addr string) *http.Server { // 创建 Gin 引擎 r := gin.New() // 使用默认的 Recovery 中间件 r.Use(gin.Recovery()) // 启用 CORS 中间件,支持前后端分离 r.Use(middleware.CorsMiddleware()) // 添加日志中间件 // 默认为 true,只有显式设置为 false 才关闭 enableAccessLog := true if viper.IsSet("server.access_log") { enableAccessLog = viper.GetBool("server.access_log") } if enableAccessLog { r.Use(middleware.WrapHandler()) } // 添加开发模式中间件(统一管理开发模式功能) r.Use(middleware.DevModeMiddleware()) // 添加安装检查中间件 r.Use(middleware.InstallCheckMiddleware()) // 添加维护模式中间件 r.Use(middleware.MaintenanceMiddleware()) // 注册路由 registerRoutes(r) return &http.Server{ Addr: addr, Handler: r, } } // registerRoutes 注册HTTP路由 func registerRoutes(r *gin.Engine) { // 使用server包中的路由注册函数 server.RegisterRoutes(r) } // startServer 启动服务器并处理优雅关闭 func startServer(server *http.Server) { // 获取全局日志实例 logger := logger.GetLogger() // 创建一个通道来接收操作系统信号 sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) // 在goroutine中启动服务器 go func() { logger.WithField("addr", server.Addr).Info("HTTP服务器已启动") if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { logger.LogError(err, "服务器启动失败") os.Exit(1) } }() // 等待中断信号 <-sigChan logger.Info("收到关闭信号,正在优雅关闭服务器...") // 创建一个带超时的上下文 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() // 优雅关闭服务器 if err := server.Shutdown(ctx); err != nil { logger.LogError(err, "服务器关闭时出错") } else { logger.LogServerStop() } }