更新底层架构

This commit is contained in:
2026-03-18 21:51:17 +08:00
parent b69c6ccbca
commit 68bea98b81
71 changed files with 5220 additions and 7619 deletions

View File

@@ -1,142 +1,128 @@
package cmd
import (
"io"
"networkDev/config"
"networkDev/utils/logger"
"os"
"path/filepath"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"gopkg.in/natefinch/lumberjack.v2"
)
// ============================================================================
// 全局变量
// ============================================================================
var cfgFile string
// ============================================================================
// 命令定义
// ============================================================================
// rootCmd 代表没有调用子命令时的基础命令
var rootCmd = &cobra.Command{
Use: "networkDev",
Short: "一个基于Cobra的网络验证服务器应用",
Long: `networkDev是一个使用Cobra CLI框架构建的网络验证服务器应用
集成了Viper配置管理、Logrus日志记录和embed静态资源嵌入功能。`,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
// 在加载配置前配置logrus用于非HTTP日志
setupLogrusForNonHTTP()
},
}
// ============================================================================
// 公共函数
// ============================================================================
// Execute 添加所有子命令到根命令并设置适当的标志
// 这由main.main()调用。只需要对rootCmd执行一次。
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(initConfig)
// 在这里定义标志和配置设置
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "配置文件路径 (默认为 config.json)")
}
// ============================================================================
// 私有函数
// ============================================================================
// setupLogrusForNonHTTP 配置logrus用于非HTTP日志
// 在加载配置文件之前进行基本的logrus设置
func setupLogrusForNonHTTP() {
// 设置日志格式
logrus.SetFormatter(&logrus.TextFormatter{
TimestampFormat: "2006-01-02 15:04:05",
FullTimestamp: true,
ForceColors: false,
DisableColors: true,
})
// 设置默认日志级别
logrus.SetLevel(logrus.InfoLevel)
// 设置输出目标(稍后会根据配置文件调整)
logrus.SetOutput(os.Stdout)
if cfgFile != "" {
config.Init(cfgFile)
} else {
config.Init("./config.json")
}
// 根据配置文件进一步配置logrus
setupLogrusFromConfig()
// 初始化HTTP日志处理器
logger.InitLogger()
// 记录配置加载完成
logrus.Info("配置文件加载完成")
}
// initConfig 读取配置文件和环境变量
func initConfig() {
}
// setupLogrusFromConfig 根据配置文件进一步配置logrus
// 设置日志级别和输出目标,支持日志切割功能
func setupLogrusFromConfig() {
// 设置日志级别
if level := viper.GetString("log.level"); level != "" {
if logLevel, err := logrus.ParseLevel(level); err == nil {
logrus.SetLevel(logLevel)
}
}
// 设置日志输出目标
logFile := viper.GetString("log.file")
if logFile != "" {
// 确保日志目录存在
logDir := filepath.Dir(logFile)
if err := os.MkdirAll(logDir, 0755); err != nil {
logrus.WithError(err).Error("创建日志目录失败")
return
}
// 配置lumberjack日志轮转
lumberjackLogger := &lumberjack.Logger{
Filename: logFile,
MaxSize: viper.GetInt("log.max_size"), // MB
MaxBackups: viper.GetInt("log.max_backups"), // 保留的旧日志文件数量
MaxAge: viper.GetInt("log.max_age"), // 天数
Compress: true, // 压缩旧日志文件
}
// 同时输出到控制台和文件(带日志切割)
multiWriter := io.MultiWriter(os.Stdout, lumberjackLogger)
logrus.SetOutput(multiWriter)
logrus.WithFields(logrus.Fields{
"file": logFile,
"max_size": viper.GetInt("log.max_size"),
"max_backups": viper.GetInt("log.max_backups"),
"max_age": viper.GetInt("log.max_age"),
}).Info("日志切割功能已启用")
}
// 当日志文件路径为空时,保持默认输出到控制台,不创建任何目录
}
package cmd
import (
"NetworkAuth/config"
"NetworkAuth/utils/logger"
"io"
"os"
"path/filepath"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"gopkg.in/natefinch/lumberjack.v2"
)
var cfgFile string
// rootCmd 代表没有调用子命令时的基础命令
var rootCmd = &cobra.Command{
Use: "NetworkAuth",
Short: "网络授权服务命令行工具",
Long: `网络授权服务 (NetworkAuth) 是一个专注于应用鉴权、接口管理和动态逻辑分发的后端系统。
本命令行工具用于启动服务器、管理配置和执行维护任务。`,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
// 在加载配置前配置logrus用于非HTTP日志
setupLogrusForNonHTTP()
},
}
// Execute 添加所有子命令到根命令并设置适当的标志
// 这由main.main()调用。只需要对rootCmd执行一次。
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(initConfig)
// 在这里定义标志和配置设置
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "配置文件路径 (默认为 config.json)")
}
// setupLogrusForNonHTTP 配置logrus用于非HTTP日志
// 在加载配置文件之前进行基本的logrus设置
func setupLogrusForNonHTTP() {
// 设置日志格式
logrus.SetFormatter(&logrus.TextFormatter{
TimestampFormat: "2006-01-02 15:04:05",
FullTimestamp: true,
ForceColors: false,
DisableColors: true,
})
// 设置默认日志级别
logrus.SetLevel(logrus.InfoLevel)
// 设置输出目标(稍后会根据配置文件调整)
logrus.SetOutput(os.Stdout)
if cfgFile != "" {
// 使用命令行指定的配置文件
config.Init(cfgFile)
} else {
// 使用默认配置文件路径
config.Init("./config.json")
}
// 根据配置文件进一步配置logrus
setupLogrusFromConfig()
// 初始化HTTP日志处理器
logger.InitLogger()
// 记录配置加载完成
logrus.WithField("file", viper.ConfigFileUsed()).Info("配置文件加载完成")
}
// initConfig 读取配置文件和环境变量
func initConfig() {
}
// setupLogrusFromConfig 根据配置文件进一步配置logrus
// 设置日志级别和输出目标,支持日志切割功能
func setupLogrusFromConfig() {
// 设置日志级别
if level := viper.GetString("log.level"); level != "" {
if logLevel, err := logrus.ParseLevel(level); err == nil {
logrus.SetLevel(logLevel)
}
}
// 设置日志输出目标
logFile := viper.GetString("log.file")
if logFile != "" {
// 确保日志目录存在
logDir := filepath.Dir(logFile)
if err := os.MkdirAll(logDir, 0755); err != nil {
logrus.WithError(err).Error("创建日志目录失败")
return
}
// 配置lumberjack日志轮转
lumberjackLogger := &lumberjack.Logger{
Filename: logFile,
MaxSize: viper.GetInt("log.max_size"), // MB
MaxBackups: viper.GetInt("log.max_backups"), // 保留的旧日志文件数量
MaxAge: viper.GetInt("log.max_age"), // 天数
Compress: true, // 压缩旧日志文件
}
// 同时输出到控制台和文件(带日志切割)
multiWriter := io.MultiWriter(os.Stdout, lumberjackLogger)
logrus.SetOutput(multiWriter)
logrus.WithFields(logrus.Fields{
"file": logFile,
"max_size": viper.GetInt("log.max_size"),
"max_backups": viper.GetInt("log.max_backups"),
"max_age": viper.GetInt("log.max_age"),
}).Info("日志切割功能已启用")
}
// 日志文件路径为空时,保持默认输出到控制台,不创建任何目录
}

View File

@@ -3,19 +3,19 @@ package cmd
import (
"context"
"fmt"
"io"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"networkDev/database"
"networkDev/middleware"
"networkDev/server"
"networkDev/utils"
"networkDev/utils/logger"
"networkDev/web"
"NetworkAuth/database"
"NetworkAuth/middleware"
"NetworkAuth/server"
"NetworkAuth/services"
"NetworkAuth/utils"
"NetworkAuth/utils/logger"
"NetworkAuth/web"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
@@ -23,22 +23,14 @@ import (
"github.com/spf13/viper"
)
// ============================================================================
// 命令定义
// ============================================================================
// serverCmd 代表服务器命令
var serverCmd = &cobra.Command{
Use: "server",
Short: "启动HTTP服务",
Long: `启动一个简单的HTTP服务器监听配置文件中指定的端口。`,
Short: "启动网络授权服务",
Long: `启动 NetworkAuth HTTP 服务器,监听配置文件中指定的端口,提供 Web 管理界面和 API 服务`,
Run: runServer,
}
// ============================================================================
// 初始化函数
// ============================================================================
func init() {
// 将服务器命令添加到根命令
rootCmd.AddCommand(serverCmd)
@@ -48,10 +40,6 @@ func init() {
serverCmd.Flags().IntP("port", "p", 0, "服务器监听端口 (覆盖配置文件)")
}
// ============================================================================
// 主要函数
// ============================================================================
// runServer 运行HTTP服务器
func runServer(cmd *cobra.Command, args []string) {
// 获取配置
@@ -63,21 +51,47 @@ func runServer(cmd *cobra.Command, args []string) {
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
// 如果初始化失败则回退并退出
if _, err := database.Init(); err != nil {
db, err := database.Init()
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 db != nil {
// 执行自动迁移(确保表结构存在)
if err := database.AutoMigrate(); err != nil {
logrus.WithError(err).Fatal("数据库自动迁移失败")
}
// 初始化默认系统设置
if err := database.SeedDefaultSettings(); 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("系统处于未初始化状态,跳过数据库自动迁移和设置加载")
}
// 创建HTTP服务器
@@ -87,10 +101,6 @@ func runServer(cmd *cobra.Command, args []string) {
startServer(server)
}
// ============================================================================
// 辅助函数
// ============================================================================
// getServerHost 获取服务器监听地址
func getServerHost(cmd *cobra.Command) string {
if host, _ := cmd.Flags().GetString("host"); host != "" {
@@ -109,49 +119,51 @@ func getServerPort(cmd *cobra.Command) int {
// createHTTPServer 创建HTTP服务器
func createHTTPServer(addr string) *http.Server {
// 配置Gin模式和日志
configureGin()
// 创建 Gin 引擎
r := gin.New()
// 创建Gin引擎
router := gin.New()
// 添加恢复中间件
router.Use(gin.Recovery())
// 使用默认的 Recovery 中间件
r.Use(gin.Recovery())
// 添加日志中间件
router.Use(middleware.WrapHandler())
// 默认为 true只有显式设置为 false 才关闭
enableAccessLog := true
if viper.IsSet("server.access_log") {
enableAccessLog = viper.GetBool("server.access_log")
}
if enableAccessLog {
r.Use(middleware.WrapHandler())
}
// 添加开发模式中间件(统一管理开发模式功能)
router.Use(middleware.DevModeMiddleware(router))
// 添加安装检查中间件
r.Use(middleware.InstallCheckMiddleware())
// 加载模板
if err := loadTemplates(router); err != nil {
logrus.WithError(err).Fatal("模板加载失败")
// 添加维护模式中间件
r.Use(middleware.MaintenanceMiddleware())
// 添加开发模式中间件(统一管理开发模式功能:模板热重载等)
r.Use(middleware.DevModeMiddleware(r))
// 加载并设置 HTML 模板
if tmpl, err := web.ParseTemplates(); err == nil {
r.SetHTMLTemplate(tmpl)
} else {
logrus.WithError(err).Error("HTML模板加载失败")
}
// 注册路由
registerRoutes(router)
registerRoutes(r)
return &http.Server{
Addr: addr,
Handler: router,
Handler: r,
}
}
// loadTemplates 加载模板到Gin引擎
func loadTemplates(router *gin.Engine) error {
tmpl, err := web.ParseTemplates()
if err != nil {
return err
}
router.SetHTMLTemplate(tmpl)
return nil
}
// registerRoutes 注册HTTP路由
func registerRoutes(router *gin.Engine) {
func registerRoutes(r *gin.Engine) {
// 使用server包中的路由注册函数
server.RegisterRoutes(router)
server.RegisterRoutes(r)
}
// startServer 启动服务器并处理优雅关闭
@@ -174,8 +186,6 @@ func startServer(server *http.Server) {
// 等待中断信号
<-sigChan
// 清除终端上的 ^C 字符并移动光标到行首
fmt.Print("\r\033[K")
logger.Info("收到关闭信号,正在优雅关闭服务器...")
// 创建一个带超时的上下文
@@ -189,20 +199,3 @@ func startServer(server *http.Server) {
logger.LogServerStop()
}
}
// configureGin 配置Gin的全局设置
func configureGin() {
// 禁用Gin的颜色输出提高控制台兼容性
gin.DisableConsoleColor()
// 设置Gin的输出为丢弃因为我们使用自定义日志中间件
gin.DefaultWriter = io.Discard
gin.DefaultErrorWriter = io.Discard
// 根据配置设置Gin模式
if viper.GetString("app.mode") == "production" {
gin.SetMode(gin.ReleaseMode)
} else {
gin.SetMode(gin.DebugMode)
}
}