package main import ( "backend/config" "backend/database" "backend/internal/secretbox" "backend/internal/service/appconfig" licenseservice "backend/internal/service/license" syncservice "backend/internal/service/sync" "backend/router" "context" "log" "net/http" "os" "os/signal" "syscall" "time" ) func main() { // 加载配置 // Logan.Li cfg := config.Load() // 机密字段主密钥:缺失/非法直接拒绝启动(fail-fast),避免带病运行 // Logan.Li if err := secretbox.LoadMasterKey(); err != nil { log.Fatal(err) } // 初始化数据库 // Logan.Li db, err := database.Init(cfg) if err != nil { log.Fatal("数据库初始化失败:", err) } // 历史明文机密一次性加密回写(幂等;双节点需配置相同 OPSLIC_MASTER_KEY) // Logan.Li if err := secretbox.MigratePlaintext(db); err != nil { log.Fatal("机密字段迁移失败:", err) } // 运行配置中心:DB → 环境变量 → 默认值(须在路由注册前装载) // Logan.Li if err := appconfig.Init(db); err != nil { log.Fatal("运行配置初始化失败:", err) } // 跨域白名单以页面配置为准(后台改动需重启生效) // Logan.Li if origins := appconfig.List("CORS_ORIGINS"); len(origins) > 0 { cfg.CORSOrigins = origins } // 启动在线状态清扫(超时未心跳的激活记录置为离线) // Logan.Li licenseservice.StartOnlineSweeper(db, cfg.OnlineTTLMinutes) // 启动节点定时双向同步(宽松节奏,默认 10 分钟) // Logan.Li syncservice.NewService(db, cfg).StartSyncLoop(cfg.SyncIntervalMinutes) // 初始化路由 // Logan.Li r := router.SetupRouter(db, cfg) // 单二进制构建时提供嵌入的 Vue 前端;开发模式会自动跳过。 // Logan.Li setupEmbeddedFrontend(r) // 启动服务器 // Logan.Li addr := ":" + cfg.Port srv := &http.Server{ Addr: addr, Handler: r, } // 优雅关机: 在独立 goroutine 中启动服务器 // Logan.Li go func() { log.Printf("服务器启动在 %s", addr) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatal("服务器启动失败:", err) } }() // 等待中断信号 // Logan.Li quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit log.Println("正在关闭服务器...") // 设置 5 秒超时来关闭服务器 // Logan.Li ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { log.Fatal("服务器强制关闭:", err) } log.Println("服务器已优雅关闭") }