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