Files
bitmagnet/internal/httpserver/httpserverfx/module.go
T
2025-07-04 17:59:22 +01:00

139 lines
3.5 KiB
Go

package httpserverfx
import (
"fmt"
"sort"
"time"
"github.com/bitmagnet-io/bitmagnet/internal/blocking"
"github.com/bitmagnet-io/bitmagnet/internal/config/configfx"
"github.com/bitmagnet-io/bitmagnet/internal/database"
"github.com/bitmagnet-io/bitmagnet/internal/health"
"github.com/bitmagnet-io/bitmagnet/internal/httpserver"
"github.com/bitmagnet-io/bitmagnet/internal/httpserver/circuitbreaker"
"github.com/bitmagnet-io/bitmagnet/internal/httpserver/cors"
"github.com/bitmagnet-io/bitmagnet/internal/httpserver/ginzap"
"github.com/bitmagnet-io/bitmagnet/internal/workers/registry"
"github.com/bitmagnet-io/bitmagnet/internal/workers/worker"
"github.com/gin-gonic/gin"
"go.uber.org/fx"
"go.uber.org/zap"
)
func New() fx.Option {
return fx.Module(
httpserver.Namespace,
configfx.NewConfigModule[httpserver.Config](httpserver.Namespace, httpserver.NewDefaultConfig()),
fx.Provide(
circuitbreaker.New,
fx.Annotate(
func(logger *zap.Logger) httpserver.Option {
return httpserver.NewOption("logger", func(engine *gin.Engine) {
engine.Use(ginzap.Ginzap(logger, time.RFC3339, true))
})
},
fx.ResultTags(`group:"http_server_options"`),
),
fx.Annotate(
func() httpserver.Option {
return httpserver.NewOption("recovery", func(engine *gin.Engine) {
engine.Use(gin.Recovery())
})
},
fx.ResultTags(`group:"http_server_options"`),
),
fx.Annotate(
func(
options []httpserver.Option,
config httpserver.Config,
) (gin.OptionFunc, error) {
return resolveOptions(options, config.Options)
},
fx.ParamTags(`group:"http_server_options"`),
),
fx.Annotate(
func(
config httpserver.Config,
handler circuitbreaker.Handler,
) (registry.Option, error) {
return registry.WithWorker(
httpserver.Namespace,
httpserver.New(handler, config.LocalAddress),
worker.WithDependencies(
database.Namespace,
blocking.Namespace,
health.Namespace,
),
), nil
},
fx.ResultTags(`group:"worker_options"`),
),
fx.Annotate(
func(
config httpserver.Config,
logger *zap.SugaredLogger,
) httpserver.Option {
return cors.New(config.Cors, logger)
},
fx.ResultTags(`group:"http_server_options"`),
),
),
fx.Invoke(
func(
config httpserver.Config,
option gin.OptionFunc,
handler circuitbreaker.Handler,
) error {
gin.SetMode(config.GinMode)
return handler.SetOption(option)
},
),
)
}
func resolveOptions(options []httpserver.Option, optionsNames []string) (gin.OptionFunc, error) {
paramMap := make(map[string]struct{})
for _, p := range optionsNames {
paramMap[p] = struct{}{}
}
enabledOptions := make([]httpserver.Option, 0, len(options))
foundMap := make(map[string]struct{}, len(options))
for _, o := range options {
if _, ok := foundMap[o.Key()]; ok {
return nil, fmt.Errorf("duplicate http server option: '%s'", o.Key())
}
foundMap[o.Key()] = struct{}{}
enabled := false
if _, ok := paramMap["*"]; ok {
enabled = true
} else if _, ok := paramMap[o.Key()]; ok {
enabled = true
}
if enabled {
enabledOptions = append(enabledOptions, o)
}
}
for p := range paramMap {
if _, ok := foundMap[p]; !ok && p != "*" {
return nil, fmt.Errorf("unknown http server option: '%s'", p)
}
}
sort.Slice(enabledOptions, func(i, j int) bool {
return enabledOptions[i].Key() < enabledOptions[j].Key()
})
return func(engine *gin.Engine) {
for _, opt := range enabledOptions {
opt.Apply(engine)
}
}, nil
}