package app import ( "context" "errors" "fmt" "net/http" "time" "git.apinb.com/ops/license/internal/config" "git.apinb.com/ops/license/internal/dashboard" "git.apinb.com/ops/license/internal/database" "git.apinb.com/ops/license/internal/httpapi" "git.apinb.com/ops/license/internal/issuance" "git.apinb.com/ops/license/internal/signing" "git.apinb.com/ops/license/internal/subject" "git.apinb.com/ops/license/internal/webui" ) const shutdownTimeout = 10 * time.Second // Run 装配并运行许可证管理服务,直到上下文被取消。 func Run(ctx context.Context, cfg config.Config) (resultErr error) { if ctx == nil { return errors.New("上下文不能为空") } db, err := database.Open(ctx, cfg.DatabaseDSN) if err != nil { return fmt.Errorf("打开数据库: %w", err) } sqlDB, err := db.DB() if err != nil { return fmt.Errorf("获取数据库连接池: %w", err) } defer func() { resultErr = errors.Join(resultErr, sqlDB.Close()) }() if err := database.Migrate(ctx, db); err != nil { return fmt.Errorf("执行数据库迁移: %w", err) } activeSigner, err := signing.LoadActive(cfg.SigningPrivateKeyFile, cfg.SigningCertificateFile) if err != nil { return err } registry, err := signing.NewRegistry(db) if err != nil { return err } if err := registry.Activate(ctx, activeSigner); err != nil { return fmt.Errorf("激活当前签发密钥: %w", err) } subjectRepository, err := subject.NewRepository(db) if err != nil { return err } subjectService, err := subject.NewService(subjectRepository) if err != nil { return err } issuanceRepository, err := issuance.NewRepository(db) if err != nil { return err } fileSigner, err := issuance.NewFileSigner(activeSigner) if err != nil { return err } issuanceService, err := issuance.NewService(issuanceRepository, subjectRepository, fileSigner) if err != nil { return err } dashboardService, err := dashboard.NewService(db) if err != nil { return err } apiRouter, err := httpapi.NewRouter(subjectService, issuanceService, dashboardService) if err != nil { return err } handler := http.NewServeMux() handler.Handle("/api/", apiRouter) handler.Handle("/", webui.Handler()) server := &http.Server{ Addr: cfg.ListenAddr, Handler: handler, ReadHeaderTimeout: 10 * time.Second, } serveResult := make(chan error, 1) go func() { serveResult <- server.ListenAndServe() }() select { case err := <-serveResult: if errors.Is(err, http.ErrServerClosed) { return nil } return fmt.Errorf("运行 HTTP 服务: %w", err) case <-ctx.Done(): shutdownContext, cancel := context.WithTimeout(context.Background(), shutdownTimeout) defer cancel() if err := server.Shutdown(shutdownContext); err != nil { return fmt.Errorf("关闭 HTTP 服务: %w", err) } serveErr := <-serveResult if serveErr != nil && !errors.Is(serveErr, http.ErrServerClosed) { return fmt.Errorf("关闭 HTTP 服务: %w", serveErr) } return nil } }