178 lines
4.7 KiB
Go
178 lines
4.7 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 {
|
|
return &HTTPService{
|
|
server: http.NewServer(cfg, db.DB),
|
|
cfg: cfg,
|
|
db: db,
|
|
}
|
|
}
|
|
|
|
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() {
|
|
// 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)
|
|
|
|
// Initialize database connection
|
|
db, err := database.NewConnection(&cfg.Database)
|
|
if err != nil {
|
|
logger.WithError(err).Fatal("Failed to connect to database")
|
|
}
|
|
|
|
// Run database migrations
|
|
if err := database.Migrate(cfg.Database); err != nil {
|
|
logger.WithError(err).Fatal("Failed to migrate database")
|
|
}
|
|
|
|
// Register database cleanup on shutdown
|
|
lifecycleMgr.Register(&databaseService{db: db})
|
|
|
|
// Initialize HTTP service with database
|
|
httpService := NewHTTPService(cfg, &database.Database{DB: db})
|
|
if httpService == nil {
|
|
logger.Fatal("Failed to create HTTP service")
|
|
}
|
|
lifecycleMgr.Register(httpService)
|
|
|
|
// 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
|
|
}
|