package handlers import ( "cargo-erp-backend/internal/handlers/router/auth" "cargo-erp-backend/internal/handlers/router/basic_pricing" "cargo-erp-backend/internal/handlers/router/city" "cargo-erp-backend/internal/handlers/router/cost_component" "cargo-erp-backend/internal/handlers/router/district" "cargo-erp-backend/internal/handlers/router/postal_code" "cargo-erp-backend/internal/handlers/router/province" "cargo-erp-backend/internal/handlers/router/subdistrict" "cargo-erp-backend/internal/middleware" "cargo-erp-backend/pkg/helpers" "fmt" "net/http" "time" "github.com/gin-gonic/gin" "github.com/prometheus/client_golang/prometheus/promhttp" "go.uber.org/zap" ) type HandlersInfterface interface { Run() } type Handlers struct { Helper helpers.HelperInterface } func NewHandlers(helper helpers.HelperInterface) HandlersInfterface { return &Handlers{ Helper: helper, } } func (h *Handlers) ZapLoggerMiddleware(logger *zap.Logger) gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() path := c.Request.URL.Path query := c.Request.URL.RawQuery // Lanjutkan eksekusi ke handler utama c.Next() // Hitung durasi response eksekusi latency := time.Since(start) status := c.Writer.Status() // Siapkan field data log terstruktur fields := []zap.Field{ zap.Int("status", status), zap.String("method", c.Request.Method), zap.String("path", path), zap.String("query", query), zap.String("ip", c.ClientIP()), zap.Duration("latency", latency), zap.String("user-agent", c.Request.UserAgent()), } // Jika ada error internal dari Gin, masukkan ke log if len(c.Errors) > 0 { for _, e := range c.Errors.Errors() { logger.Error(e, fields...) } return } // Tentukan level log berdasarkan HTTP Status Code if status >= 500 { logger.Error("Server Error", fields...) } else if status >= 400 { logger.Warn("Client Error", fields...) } else { logger.Info("Request Success", fields...) } } } func (h *Handlers) CORSMiddleware() gin.HandlerFunc { return func(c *gin.Context) { c.Writer.Header().Set("Access-Control-Allow-Origin", "*") c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With") c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE, PATCH") if c.Request.Method == "OPTIONS" { c.AbortWithStatus(http.StatusNoContent) return } c.Next() } } func (h *Handlers) Run() { host := fmt.Sprintf("%v", h.Helper.Config().Get("app.host")) port := fmt.Sprintf("%v", h.Helper.Config().Get("app.port")) r := gin.Default() r.Use(h.ZapLoggerMiddleware(h.Helper.Log())) r.Use(gin.Recovery()) r.Use(h.CORSMiddleware()) r.Use(middleware.MetricsMiddleware()) // prometheus metrics (no auth) r.GET("/metrics", gin.WrapH(promhttp.Handler())) r.GET("/ping", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "pong", }) }) //auth auth.NewAuthHandler(h.Helper, r).Router() //master-data masterdata := r.Group("master-data") masterdata.Use(middleware.NewAuthMiddleware(h.Helper).Check()) //province { province.NewProvinceHandler(h.Helper, masterdata).Router() city.NewCityHandler(h.Helper, masterdata).Router() district.NewDistrictHandler(h.Helper, masterdata).Router() subdistrict.NewSubdistrictHandler(h.Helper, masterdata).Router() postal_code.NewPostalCodeHandler(h.Helper, masterdata).Router() cost_component.NewCostComponentHandler(h.Helper, masterdata).Router() } //basic-pricing basicPricingGroup := r.Group("basic-pricings") basicPricingGroup.Use(middleware.NewAuthMiddleware(h.Helper).Check()) basic_pricing.NewBasicPricingHandler(h.Helper, basicPricingGroup).Router() h.Helper.Log().Info("Starting Apps") r.Run(fmt.Sprintf("%v:%v", host, port)) }