// internal/infrastructure/transport/http/servers/server.go package servers import ( "context" // PERBAIKAN: Tambahkan import untuk graceful shutdown "fmt" "log" "net/http" "time" "service/internal/infrastructure/cache" // PERBAIKAN: Tambahkan import cache "service/internal/infrastructure/config" "service/internal/infrastructure/transport/http/middleware" "service/internal/infrastructure/transport/http/routes" authHttp "service/internal/infrastructure/transport/http/handlers/main/auth" healthHttp "service/internal/infrastructure/transport/http/handlers/main/health" roleHttp "service/internal/infrastructure/transport/http/handlers/main/master/roles" // Inisialisasi pkg "service/pkg/errors" "github.com/gin-gonic/gin" swaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" ) type Server struct { engine *gin.Engine config *config.ServerRESTConfig cacheConfig cache.CacheConfig // PERBAIKAN: Tambahkan cacheConfig httpServer *http.Server // PERBAIKAN: Simpan instance http.Server untuk graceful shutdown } // NewHTTPServer membuat instance server HTTP baru func NewHTTPServer( restConfig *config.ServerRESTConfig, registry *ServiceRegistry, ) *Server { if !restConfig.Enabled { log.Println("HTTP server is disabled in configuration.") return &Server{ config: restConfig, } } // Set mode Gin berdasarkan konfigurasi global if registry.Config.Server.Mode == "release" { gin.SetMode(gin.ReleaseMode) } engine := gin.New() // Middleware untuk recovery dan penanganan error konsisten. // Diletakkan paling awal untuk menangkap panic dari middleware/handler lain. engine.Use(errors.CombinedMiddleware()) // Middleware lainnya engine.Use(middleware.LoggingMiddleware()) engine.Use(middleware.SecurityMiddleware()) engine.Use(middleware.CORSMiddleware()) // PERBAIKAN: Berikan cacheManager ke RateLimitMiddleware engine.Use(middleware.RateLimitMiddleware(registry.CacheManager)) // Inisialisasi ModuleHandlers secara dinamis berdasarkan fitur yang aktif appHandlers := &routes.ModuleHandlers{} if registry.AuthService != nil { appHandlers.Auth = authHttp.NewAuthHandler(registry.AuthService) } // Inisialisasi Handlers Modul Master if registry.Master != nil { if registry.Master.RolePages != nil { appHandlers.RolePages = roleHttp.NewRolPagesHandler(registry.Master.RolePages) } if registry.Master.RolePermission != nil { appHandlers.RolePermission = roleHttp.NewRolPermissionHandler(registry.Master.RolePermission) } if registry.Master.RoleMaster != nil { appHandlers.RoleMaster = roleHttp.NewRoleMasterHandler(registry.Master.RoleMaster) } } // Inisialisasi Health Handler dengan dependensi yang benar // Gunakan constructor baru yang menerima cache manager healthHandler := healthHttp.NewHealthHandlerWithCache(registry.PrimaryDB, registry.CacheManager, registry.Config, registry.DBManager) // Setup routes menggunakan handler yang sudah diinisialisasi routes.SetupRoutes( engine, registry.Config, registry.CacheManager, healthHandler, appHandlers, ) return &Server{ engine: engine, config: restConfig, } } // RegisterSwagger mendaftarkan dokumentasi Swagger UI func (s *Server) RegisterSwagger() { if s.engine == nil { log.Println("Cannot register Swagger: engine is nil (server disabled)") return } s.engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) } // Start menjalankan server HTTP func (s *Server) Start(globalConfig *config.ServerConfig) error { if !s.config.Enabled { log.Println("HTTP server is disabled, not starting.") return nil } addr := fmt.Sprintf(":%d", s.config.Port) log.Printf("Starting HTTP server on %s", addr) // PERBAIKAN: Simpan instance server ke struct s.httpServer = &http.Server{ Addr: addr, Handler: s.engine, ReadTimeout: time.Duration(globalConfig.ReadTimeout) * time.Second, WriteTimeout: time.Duration(globalConfig.WriteTimeout) * time.Second, IdleTimeout: 60 * time.Second, // Good practice to have an idle timeout } return s.httpServer.ListenAndServe() } // PERBAIKAN: Tambahkan method untuk graceful shutdown // Shutdown memberhentikan server HTTP dengan graceful shutdown func (s *Server) Shutdown(ctx context.Context) error { if s.httpServer == nil { return nil } log.Println("Shutting down HTTP server...") return s.httpServer.Shutdown(ctx) } // GetEngine mengembalikan instance Gin engine (terutama untuk testing) func (s *Server) GetEngine() *gin.Engine { return s.engine }