Files
api_antrean/internal/routes/v1/routes.go
2025-11-03 05:56:41 +00:00

197 lines
7.1 KiB
Go

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
}