package app import ( "context" "log" "net/http" "os" "os/signal" "syscall" "time" "eslogad-be/config" "eslogad-be/internal/client" "eslogad-be/internal/handler" "eslogad-be/internal/middleware" "eslogad-be/internal/processor" "eslogad-be/internal/repository" "eslogad-be/internal/router" "eslogad-be/internal/service" "eslogad-be/internal/validator" "gorm.io/gorm" ) type App struct { server *http.Server db *gorm.DB router *router.Router shutdown chan os.Signal } func NewApp(db *gorm.DB) *App { return &App{ db: db, shutdown: make(chan os.Signal, 1), } } func (a *App) Initialize(cfg *config.Config) error { repos := a.initRepositories() processors := a.initProcessors(cfg, repos) services := a.initServices(processors, repos, cfg) middlewares := a.initMiddleware(services) healthHandler := handler.NewHealthHandler() fileHandler := handler.NewFileHandler(services.fileService) a.router = router.NewRouter( cfg, handler.NewAuthHandler(services.authService), middlewares.authMiddleware, healthHandler, handler.NewUserHandler(services.userService, &validator.UserValidatorImpl{}), fileHandler, ) return nil } func (a *App) Start(port string) error { engine := a.router.Init() a.server = &http.Server{ Addr: ":" + port, Handler: engine, ReadTimeout: 15 * time.Second, WriteTimeout: 15 * time.Second, IdleTimeout: 60 * time.Second, } signal.Notify(a.shutdown, os.Interrupt, syscall.SIGTERM) go func() { log.Printf("Server starting on port %s", port) if err := a.server.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("Failed to start server: %v", err) } }() <-a.shutdown log.Println("Shutting down server...") ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() if err := a.server.Shutdown(ctx); err != nil { log.Printf("Server forced to shutdown: %v", err) return err } log.Println("Server exited gracefully") return nil } func (a *App) Shutdown() { close(a.shutdown) } type repositories struct { userRepo *repository.UserRepositoryImpl userProfileRepo *repository.UserProfileRepository titleRepo *repository.TitleRepository } func (a *App) initRepositories() *repositories { return &repositories{ userRepo: repository.NewUserRepository(a.db), userProfileRepo: repository.NewUserProfileRepository(a.db), titleRepo: repository.NewTitleRepository(a.db), } } type processors struct { userProcessor *processor.UserProcessorImpl } func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processors { return &processors{ userProcessor: processor.NewUserProcessor(repos.userRepo, repos.userProfileRepo), } } type services struct { userService *service.UserServiceImpl authService *service.AuthServiceImpl fileService *service.FileServiceImpl } func (a *App) initServices(processors *processors, repos *repositories, cfg *config.Config) *services { authConfig := cfg.Auth() jwtSecret := authConfig.AccessTokenSecret() authService := service.NewAuthService(processors.userProcessor, jwtSecret) userSvc := service.NewUserService(processors.userProcessor, repos.titleRepo) // File storage client and service fileCfg := cfg.S3Config s3Client := client.NewFileClient(fileCfg) fileSvc := service.NewFileService(s3Client, processors.userProcessor, "profile", "documents") return &services{ userService: userSvc, authService: authService, fileService: fileSvc, } } type middlewares struct { authMiddleware *middleware.AuthMiddleware } func (a *App) initMiddleware(services *services) *middlewares { return &middlewares{ authMiddleware: middleware.NewAuthMiddleware(services.authService), } } type validators struct { userValidator *validator.UserValidatorImpl } func (a *App) initValidators() *validators { return &validators{ userValidator: validator.NewUserValidator(), } }