Files
NetworkAuth/cmd/server.go

220 lines
5.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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()
}
}