package apiimp import ( "os" "github.com/go-chi/chi/v5" cm "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/cors" "github.com/go-chi/metrics" api "github.com/tech/sendico/pkg/api/http" amr "github.com/tech/sendico/pkg/api/routers" "github.com/tech/sendico/pkg/api/routers/health" "github.com/tech/sendico/pkg/auth" "github.com/tech/sendico/pkg/db" "github.com/tech/sendico/pkg/messaging" notifications "github.com/tech/sendico/pkg/messaging/notifications/processor" "github.com/tech/sendico/pkg/mlogger" "github.com/tech/sendico/pkg/mservice" "github.com/tech/sendico/server/interface/api/sresponse" wsh "github.com/tech/sendico/server/interface/api/ws" "github.com/tech/sendico/server/interface/middleware" "github.com/tech/sendico/server/internal/api/routers" mr "github.com/tech/sendico/server/internal/api/routers/metrics" "github.com/tech/sendico/server/internal/api/ws" "go.uber.org/zap" "moul.io/chizap" ) type Middleware struct { logger mlogger.Logger router *chi.Mux apiEndpoint string health amr.Health metrics mr.Metrics wshandler ws.Router messaging amr.Messaging epdispatcher *routers.Dispatcher } func (mw *Middleware) Handler(service mservice.Type, endpoint string, method api.HTTPMethod, handler sresponse.HandlerFunc) { mw.epdispatcher.Handler(service, endpoint, method, handler) } func (mw *Middleware) AccountHandler(service mservice.Type, endpoint string, method api.HTTPMethod, handler sresponse.AccountHandlerFunc) { mw.epdispatcher.AccountHandler(service, endpoint, method, handler) } func (mw *Middleware) PendingAccountHandler(service mservice.Type, endpoint string, method api.HTTPMethod, handler sresponse.PendingAccountHandlerFunc) { mw.epdispatcher.PendingAccountHandler(service, endpoint, method, handler) } func (mw *Middleware) WSHandler(messageType string, handler wsh.HandlerFunc) { mw.wshandler.InstallHandler(messageType, handler) } func (mw *Middleware) Consumer(processor notifications.EnvelopeProcessor) error { return mw.messaging.Consumer(processor) } func (mw *Middleware) Producer() messaging.Producer { return mw.messaging.Producer() } func (mw *Middleware) Messaging() messaging.Register { return mw } func (mw *Middleware) Finish() { mw.messaging.Finish() mw.health.Finish() } func (mw *Middleware) SetStatus(status health.ServiceStatus) { mw.health.SetStatus(status) } func (mw *Middleware) installMiddleware(config *middleware.Config, debug bool) { mw.logger.Debug("Installing middleware stack...") // Collect metrics for all incoming HTTP requests mw.router.Use(metrics.Collector(metrics.CollectorOpts{ Host: false, // avoid high-cardinality "host" label Proto: true, // include HTTP protocol label })) mw.router.Use(cm.RequestID) mw.router.Use(cm.RealIP) if debug { mw.router.Use(chizap.New(mw.logger.Named("http_trace"), &chizap.Opts{ WithReferer: true, WithUserAgent: true, })) } mw.router.Use(cors.Handler(cors.Options{ AllowedOrigins: config.CORS.AllowedOrigins, AllowedMethods: config.CORS.AllowedMethods, AllowedHeaders: config.CORS.AllowedHeaders, ExposedHeaders: config.CORS.ExposedHeaders, AllowCredentials: config.CORS.AllowCredentials, MaxAge: config.CORS.MaxAge, OptionsPassthrough: false, Debug: debug, })) mw.router.Use(cm.Recoverer) mw.router.Handle("/metrics", metrics.Handler()) mw.logger.Info("Middleware stack installation complete") } func CreateMiddleware(logger mlogger.Logger, db db.Factory, enforcer auth.Enforcer, router *chi.Mux, config *middleware.Config, debug bool) (*Middleware, error) { p := &Middleware{ logger: logger.Named("middleware"), router: router, apiEndpoint: os.Getenv(config.EndPointEnv), } p.logger.Info("Set endpoint", zap.String("endpoint", p.apiEndpoint)) p.installMiddleware(config, debug) var err error if p.messaging, err = amr.NewMessagingRouter(p.logger, &config.Messaging); err != nil { p.logger.Error("Failed to create messaging router", zap.Error(err)) return nil, err } if p.health, err = amr.NewHealthRouter(p.logger, p.router, p.apiEndpoint); err != nil { p.logger.Error("Failed to create healthcheck router", zap.Error(err), zap.String("api_endpoint", p.apiEndpoint)) return nil, err } if p.metrics, err = mr.NewMetricsRouter(p.logger, p.router, p.apiEndpoint); err != nil { p.logger.Error("Failed to create metrics router", zap.Error(err), zap.String("api_endpoint", p.apiEndpoint)) return nil, err } adb, err := db.NewAccountDB() if err != nil { p.logger.Error("Faild to create account database", zap.Error(err)) return nil, err } rtdb, err := db.NewRefreshTokensDB() if err != nil { p.logger.Error("Faild to create refresh token management database", zap.Error(err)) return nil, err } cdb, err := db.NewConfirmationsDB() if err != nil { p.logger.Error("Failed to create confirmations database", zap.Error(err)) return nil, err } p.epdispatcher = routers.NewDispatcher(p.logger, p.router, adb, cdb, rtdb, enforcer, config) p.wshandler = ws.NewRouter(p.logger, p.router, &config.WebSocket, p.apiEndpoint) return p, nil }