2025-08-09 15:28:25 +07:00

174 lines
4.1 KiB
Go

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)
rbacHandler := handler.NewRBACHandler(services.rbacService)
a.router = router.NewRouter(
cfg,
handler.NewAuthHandler(services.authService),
middlewares.authMiddleware,
healthHandler,
handler.NewUserHandler(services.userService, validator.NewUserValidator()),
fileHandler,
rbacHandler,
)
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
rbacRepo *repository.RBACRepository
}
func (a *App) initRepositories() *repositories {
return &repositories{
userRepo: repository.NewUserRepository(a.db),
userProfileRepo: repository.NewUserProfileRepository(a.db),
titleRepo: repository.NewTitleRepository(a.db),
rbacRepo: repository.NewRBACRepository(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
rbacService *service.RBACServiceImpl
}
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")
rbacSvc := service.NewRBACService(repos.rbacRepo)
return &services{
userService: userSvc,
authService: authService,
fileService: fileSvc,
rbacService: rbacSvc,
}
}
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(),
}
}