package v1 import ( "api-service/internal/config" "api-service/internal/database" authHandlers "api-service/internal/handlers/auth" healthcheckHandlers "api-service/internal/handlers/healthcheck" pasienPasienHandlers "api-service/internal/handlers/pasien" retribusiHandlers "api-service/internal/handlers/retribusi" "api-service/internal/middleware" services "api-service/internal/services/auth" "api-service/pkg/logger" "net/http" "strings" "time" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" swaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" ) func RegisterRoutes(cfg *config.Config) *gin.Engine { // Atur mode Gin berdasarkan konfigurasi gin.SetMode(cfg.Server.Mode) router := gin.New() // ============================================================================= // GLOBAL MIDDLEWARE STACK (Middleware yang diperlukan SEMUA route) // ============================================================================= middleware.InitializeAuth(cfg) router.Use(gin.Recovery()) // 1. CORS (Paling awal) router.Use(middleware.SecureCORSConfig(cfg.Security)) // 2. Rate Limiting router.Use(middleware.RateLimitByIPRedis(cfg.Security)) // 3. Logging & Recovery router.Use(logger.RequestLoggerMiddleware(logger.Default())) // 4. Error Handling (Terakhir, untuk menangkap error dari middleware di atasnya) router.Use(middleware.ErrorHandler()) // ============================================================================= // INISIALISASI SERVIS & HANDLER // ============================================================================= authService := services.NewAuthService(cfg) if authService == nil { logger.Fatal("Failed to initialize auth service") } dbService := database.New(cfg) // ============================================================================= // SWAGGER DOCUMENTATION (Publik - TANPA SecurityHeaders) // ============================================================================= // Route ini didefinisikan SEBELUM grup API agar tidak terkena middleware keamanan. router.GET("/swagger/*any", ginSwagger.WrapHandler( swaggerFiles.Handler, ginSwagger.DefaultModelsExpandDepth(-1), ginSwagger.DeepLinking(true), )) // ============================================================================= // API GROUPS (Dengan Keamanan Ketat) // ============================================================================= // Terapkan middleware keamanan dan validasi input HANYA ke grup API. // Ini adalah perubahan utama. apiGroup := router.Group("/api") apiGroup.Use(middleware.SecurityHeaders()) // <--- PINDAHKAN KE SINI apiGroup.Use(middleware.InputValidation(cfg.Security)) // <--- PINDAHKAN KE SINI // --- HEALTH CHECK & SYSTEM ROUTES --- healthCheckHandler := healthcheckHandlers.NewHealthCheckHandler(dbService) sistem := apiGroup.Group("/sistem") { sistem.GET("/health", healthCheckHandler.CheckHealth) sistem.GET("/databases", func(c *gin.Context) { c.JSON(200, gin.H{ "databases": dbService.ListDBs(), "health": dbService.Health(), "timestamp": time.Now().Unix(), }) }) sistem.GET("/info", func(c *gin.Context) { c.JSON(200, gin.H{ "service": "API Service v1.0.0", "websocket_active": true, "databases": dbService.ListDBs(), "timestamp": time.Now().Unix(), }) }) } // --- API v1 GROUP --- v1 := apiGroup.Group("/v1") { // ============================================================================= // PUBLIC ROUTES (No Authentication Required) // ============================================================================= authHandler := authHandlers.NewAuthHandler(authService) tokenHandler := authHandlers.NewTokenHandler(authService) v1.POST("/auth/login", authHandler.Login) v1.POST("/auth/register", authHandler.Register) v1.POST("/auth/refresh", authHandler.RefreshToken) v1.POST("/token/generate", tokenHandler.GenerateToken) v1.POST("/token/generate-direct", tokenHandler.GenerateTokenDirect) retribusiHandler := retribusiHandlers.NewRetribusiHandler() retribusiGroup := v1.Group("/retribusi") { retribusiGroup.GET("", retribusiHandler.GetRetribusi) retribusiGroup.GET("/dynamic", retribusiHandler.GetRetribusiDynamic) retribusiGroup.GET("/id/:id", retribusiHandler.GetRetribusiByID) retribusiGroup.POST("", func(c *gin.Context) { retribusiHandler.CreateRetribusi(c) }) retribusiGroup.PUT("/id/:id", func(c *gin.Context) { retribusiHandler.UpdateRetribusi(c) }) retribusiGroup.DELETE("/id/:id", func(c *gin.Context) { retribusiHandler.DeleteRetribusi(c) }) } // Pasien endpoints pasienPasienHandler := pasienPasienHandlers.NewPasienHandler() pasienPasienGroup := v1.Group("/pasien") { pasienPasienGroup.PUT("/:nomr", pasienPasienHandler.UpdatePasien) pasienPasienGroup.POST("/", pasienPasienHandler.CreatePasien) pasienPasienGroup.DELETE("/:nomr", pasienPasienHandler.DeletePasien) pasienPasienGroup.GET("/dynamic", pasienPasienHandler.GetPasienDynamic) pasienPasienGroup.GET("/", pasienPasienHandler.GetPasien) pasienPasienGroup.GET("/by-age", pasienPasienHandler.GetPasienByAge) pasienPasienGroup.GET("/:nomr", pasienPasienHandler.GetPasienByNomr) pasienPasienGroup.GET("/by-location", pasienPasienHandler.GetPasienByLocation) } // ============================================================================= // PROTECTED ROUTES (Authentication Required) // ============================================================================= protected := v1.Group("/") protected.Use(middleware.UnifiedAuthMiddleware(cfg, authService)) // farmasiObatHandler := farmasiObatHandlers.NewObatHandler() // protectedFarmasiGroup := protected.Group("/farmasi/obat") // { // protectedFarmasiGroup.GET("", farmasiObatHandler.GetObat) // protectedFarmasiGroup.GET("/kode/:kode", farmasiObatHandler.GetObatByID) // } // protectedAuthGroup := protected.Group("/auth") // { // protectedAuthGroup.GET("/me", authHandler.Me) // } } // ============================================================================= // DEBUG ROUTES (Publik - Tanpa keamanan ketat) // ============================================================================= router.GET("/debug/token", func(c *gin.Context) { authHeader := c.GetHeader("Authorization") if authHeader == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "Header Authorization hilang"}) return } parts := strings.SplitN(authHeader, " ", 2) if len(parts) != 2 || strings.ToLower(parts[0]) != "bearer" { c.JSON(http.StatusBadRequest, gin.H{"error": "Format header harus Bearer {token}"}) return } tokenString := parts[1] token, _, err := new(jwt.Parser).ParseUnverified(tokenString, jwt.MapClaims{}) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Gagal parsing token: " + err.Error()}) return } claims, ok := token.Claims.(jwt.MapClaims) if !ok { c.JSON(http.StatusBadRequest, gin.H{"error": "Format claim tidak valid"}) return } c.JSON(http.StatusOK, gin.H{ "header": token.Header, "claims": claims, }) }) return router }