package router import ( "git.0x7f.app/WOJ/woj-server/internal/misc/config" "git.0x7f.app/WOJ/woj-server/internal/misc/log" "git.0x7f.app/WOJ/woj-server/internal/model" "git.0x7f.app/WOJ/woj-server/internal/web/metrics" _ "git.0x7f.app/WOJ/woj-server/internal/web/router/docs" "git.0x7f.app/WOJ/woj-server/pkg/utils" sentrygin "github.com/getsentry/sentry-go/gin" "github.com/gin-contrib/cors" "github.com/gin-contrib/pprof" ginZap "github.com/gin-contrib/zap" "github.com/gin-gonic/contrib/static" "github.com/gin-gonic/gin" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/samber/do" swaggerFiles "github.com/swaggo/files" "github.com/swaggo/gin-swagger" "net/http" "time" ) var _ Service = (*service)(nil) type Service interface { GetRouter() *gin.Engine HealthCheck() error } func NewService(i *do.Injector) (Service, error) { srv := &service{} srv.metric = do.MustInvoke[metrics.Service](i) srv.logger = do.MustInvoke[log.Service](i) conf := do.MustInvoke[config.Service](i).GetConfig() srv.engine = srv.initRouters(conf, i) return srv, srv.err } type service struct { logger log.Service engine *gin.Engine metric metrics.Service err error } func (s *service) GetRouter() *gin.Engine { return s.engine } func (s *service) HealthCheck() error { return s.err } func (s *service) initRouters(conf *model.Config, injector *do.Injector) *gin.Engine { gin.SetMode(utils.If[string](conf.Development, gin.DebugMode, gin.ReleaseMode)) r := gin.New() // +--------------+ // |Configurations| // +--------------+ if conf.WebServer.TrustedPlatform != "" { // Extract Origin IP r.TrustedPlatform = conf.WebServer.TrustedPlatform } // +-----------+ // |Middlewares| // +-----------+ // Sentry middleware r.Use(sentrygin.New(sentrygin.Options{Repanic: true})) // Logger middleware and debug if conf.Development { // Gin's default logger is pretty enough r.Use(gin.Logger()) r.Use(gin.Recovery()) // add prof pprof.Register(r) } else { ginLog := s.logger.GetLogger("gin") r.Use(ginZap.Ginzap(ginLog, time.RFC3339, false)) r.Use(ginZap.RecoveryWithZap(ginLog, true)) } // CORS middleware r.Use(cors.New(cors.Config{ AllowAllOrigins: true, AllowMethods: []string{"GET", "POST", "PUT", "OPTIONS"}, AllowHeaders: []string{"Authorization", "Origin", "Content-Length", "Content-Type"}, AllowCredentials: true, })) // Prometheus middleware s.metric.SetLogPaths([]string{"/api"}) r.Use(s.metric.Handler()) // +------+ // |Routes| // +------+ // metrics r.GET("/metrics", gin.WrapH(promhttp.Handler())) // swagger r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) // health r.GET("/health", func(c *gin.Context) { var err bool for _, v := range injector.HealthCheck() { if v != nil { err = true break } } resp := &struct { Timestamp time.Time `json:"timestamp"` Status string `json:"status"` }{ Timestamp: time.Now(), Status: utils.If(err, "unhealthy", "healthy"), } c.JSON(utils.If(err, http.StatusServiceUnavailable, http.StatusOK), resp) }) // api api := r.Group("/api/") s.setupApi(api, injector) // static files r.Use(static.Serve("/", static.LocalFile("./resource/frontend", true))) // fallback to frontend r.NoRoute(func(c *gin.Context) { c.File("./resource/frontend/index.html") }) return r }