zee-solution/cmd/app/main.go
ulflow_phattt2901 f8957e0d95
Some checks failed
CI Pipeline / Security Scan (push) Successful in 5m1s
CI Pipeline / Lint (push) Failing after 6m9s
CI Pipeline / Test (push) Has been skipped
CI Pipeline / Build (push) Has been skipped
CI Pipeline / Notification (push) Successful in 1s
chore:update docker
2025-06-06 19:11:07 +07:00

201 lines
5.9 KiB
Go

package main
import (
"context"
"fmt"
"os"
"time"
"zee/internal/helper/config"
"zee/internal/helper/database"
"zee/internal/helper/feature"
"zee/internal/helper/logger"
"zee/internal/pkg/lifecycle"
"zee/internal/transport/http"
"gorm.io/gorm"
)
// HTTPService implements the lifecycle.Service interface for the HTTP server
type HTTPService struct {
server *http.Server
cfg *config.Config
db *database.Database
}
func NewHTTPService(cfg *config.Config, db *database.Database) *HTTPService {
var gormDB *gorm.DB
if db != nil {
gormDB = db.DB
}
return &HTTPService{
server: http.NewServer(cfg, gormDB), // gormDB can be nil here
cfg: cfg,
db: db, // db itself (the *database.Database wrapper) can also be nil
}
}
func (s *HTTPService) Name() string {
return "HTTP Server"
}
func (s *HTTPService) Start() error {
// Tạo channel để nhận lỗi từ goroutine
errChan := make(chan error, 1)
// Khởi động server trong goroutine
go func() {
logger.Infof("Đang khởi động %s trên %s:%d...", s.Name(), s.cfg.Server.Host, s.cfg.Server.Port)
if err := s.server.Start(); err != nil {
logger.WithError(err).Error("Lỗi HTTP server")
errChan <- fmt.Errorf("lỗi HTTP server: %w", err)
return
}
errChan <- nil
}()
// Chờ server khởi động hoặc báo lỗi
select {
case err := <-errChan:
return err // Trả về lỗi nếu có
case <-time.After(5 * time.Second):
// Nếu sau 5 giây không có lỗi, coi như server đã khởi động thành công
logger.Infof("%s đã khởi động thành công trên %s:%d", s.Name(), s.cfg.Server.Host, s.cfg.Server.Port)
return nil
}
}
func (s *HTTPService) Shutdown(ctx context.Context) error {
return s.server.Shutdown(ctx)
}
func main() {
fmt.Println("Starting application 222...")
// Initialize config loader
configLoader := config.NewConfigLoader()
// Load configuration
cfg, err := configLoader.Load()
if err != nil {
fmt.Printf("Failed to load configuration: %v\n", err)
os.Exit(1)
}
// Initialize logger
logger.Init(&logger.LogConfig{
Level: cfg.Logger.Level,
Format: "json",
EnableCaller: true,
ReportCaller: true,
})
// Initialize feature flags
if err := feature.Init(); err != nil {
logger.WithError(err).Fatal("Failed to initialize feature flags")
}
// Print application info
logger.Infof("Starting %s v%s", cfg.App.Name, cfg.App.Version)
logger.Infof("Environment: %s", cfg.App.Environment)
logger.Infof("Log Level: %s", cfg.Logger.Level)
logger.Infof("Timezone: %s", cfg.App.Timezone)
// Print server config
logger.Infof("Server config: %s:%d", cfg.Server.Host, cfg.Server.Port)
logger.Infof("Read Timeout: %d seconds", cfg.Server.ReadTimeout)
logger.Infof("Write Timeout: %d seconds", cfg.Server.WriteTimeout)
logger.Infof("Shutdown Timeout: %d seconds", cfg.Server.ShutdownTimeout)
// Create a new lifecycle manager
shutdownTimeout := 30 * time.Second // Default shutdown timeout
if cfg.Server.ShutdownTimeout > 0 {
shutdownTimeout = time.Duration(cfg.Server.ShutdownTimeout) * time.Second
}
lifecycleMgr := lifecycle.New(shutdownTimeout)
var gormDB *gorm.DB // Declare gormDB for database connection
var dbInstance *database.Database // Declare dbInstance for HTTPService and other potential users
if feature.IsEnabled(feature.EnableDatabase) {
logger.Info("Feature flag 'enable_database' is true. Initializing database connection...")
// Initialize database connection
var dbErr error
gormDB, dbErr = database.NewConnection(&cfg.Database)
if dbErr != nil {
logger.WithError(dbErr).Fatal("Failed to connect to database")
}
// Run database migrations
if err := database.Migrate(cfg.Database); err != nil { // Migrate still needs cfg.Database for path etc.
logger.WithError(err).Fatal("Failed to migrate database")
}
// Register database cleanup on shutdown
lifecycleMgr.Register(&databaseService{db: gormDB})
// Prepare dbInstance for HTTPService
dbInstance = &database.Database{DB: gormDB, Config: &cfg.Database}
} else {
logger.Info("Feature flag 'enable_database' is false. Skipping database initialization.")
// gormDB remains nil
// dbInstance remains nil
// No databaseService is registered with lifecycleMgr if DB is disabled.
}
// Initialize HTTP service
// NewHTTPService and its chain (http.NewServer, http.SetupRouter)
// must be able to handle a nil dbInstance or a dbInstance with a nil DB.
// The NewHTTPService function already checks if its db argument (or db.DB) is nil.
httpService := NewHTTPService(cfg, dbInstance) // dbInstance can be nil here
if httpService == nil {
logger.Fatal("Failed to create HTTP service")
}
lifecycleMgr.Register(httpService) // HTTP service is always registered
// Start all services
logger.Info("Đang khởi động các dịch vụ...")
if err := lifecycleMgr.Start(); err != nil {
logger.WithError(err).Fatal("Lỗi nghiêm trọng: Không thể khởi động các dịch vụ")
}
// Handle OS signals for graceful shutdown in a separate goroutine
go lifecycleMgr.ShutdownOnSignal()
// Wait for all services to complete
if err := lifecycleMgr.Wait(); err != nil {
logger.WithError(err).Error("Service error")
}
// Close feature flags
if err := feature.Close(); err != nil {
logger.WithError(err).Error("Failed to close feature flags")
}
logger.Info("Application stopped")
}
// databaseService implements the lifecycle.Service interface for database operations
type databaseService struct {
db *gorm.DB
}
func (s *databaseService) Name() string {
return "Database Service"
}
func (s *databaseService) Start() error {
// Database initialization is handled in main
return nil
}
func (s *databaseService) Shutdown(ctx context.Context) error {
if s.db != nil {
sqlDB, err := s.db.DB()
if err != nil {
return fmt.Errorf("failed to get database instance: %w", err)
}
return sqlDB.Close()
}
return nil
}