Files
license/internal/app/app.go
2026-07-31 16:59:55 +08:00

117 lines
2.9 KiB
Go

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
}
}