Files
NetworkAuth/cmd/server.go

209 lines
5.4 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"
"io"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"networkDev/database"
"networkDev/middleware"
"networkDev/server"
"networkDev/utils"
"networkDev/utils/logger"
"networkDev/web"
"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: "启动HTTP服务器",
Long: `启动一个简单的HTTP服务器监听配置文件中指定的端口。`,
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)
// 初始化Redis如果配置存在失败不致命
utils.InitRedis()
// 初始化数据库(根据 viper 配置选择 SQLite 或 MySQL
// 如果初始化失败则回退并退出
if _, err := database.Init(); 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("默认系统设置初始化失败")
}
// 创建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模式和日志
configureGin()
// 创建Gin引擎
router := gin.New()
// 添加恢复中间件
router.Use(gin.Recovery())
// 添加日志中间件
router.Use(middleware.WrapHandler())
// 添加开发模式中间件(统一管理开发模式功能)
router.Use(middleware.DevModeMiddleware(router))
// 加载模板
if err := loadTemplates(router); err != nil {
logrus.WithError(err).Fatal("模板加载失败")
}
// 注册路由
registerRoutes(router)
return &http.Server{
Addr: addr,
Handler: router,
}
}
// 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) {
// 使用server包中的路由注册函数
server.RegisterRoutes(router)
}
// 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
// 清除终端上的 ^C 字符并移动光标到行首
fmt.Print("\r\033[K")
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()
}
}
// 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)
}
}