starter-kit/cmd/app/main.go
2025-05-21 12:39:40 +07:00

150 lines
3.8 KiB
Go

package main
import (
"context"
"fmt"
"os"
"time"
"starter-kit/internal/helper/config"
"starter-kit/internal/helper/database"
"starter-kit/internal/helper/feature"
"starter-kit/internal/helper/logger"
"starter-kit/internal/pkg/lifecycle"
"starter-kit/internal/transport/http"
)
// HTTPService implements the lifecycle.Service interface for the HTTP server
type HTTPService struct {
server *http.Server
cfg *config.Config
}
func NewHTTPService(cfg *config.Config) *HTTPService {
return &HTTPService{
server: http.NewServer(cfg),
cfg: cfg,
}
}
func (s *HTTPService) Name() string {
return "HTTP Server"
}
func (s *HTTPService) Start() error {
// Start the server in a goroutine
go func() {
if err := s.server.Start(); err != nil {
logger.WithError(err).Error("HTTP server error")
}
}()
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
if feature.IsEnabled(feature.EnableDatabase) {
logger.Info("Database feature is enabled, connecting...")
_, 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{})
} else {
logger.Info("Database feature is disabled")
}
// Register HTTP service with the lifecycle manager
httpService := NewHTTPService(cfg)
lifecycleMgr.Register(httpService)
// Start all services
if err := lifecycleMgr.Start(); err != nil {
logger.WithError(err).Fatal("Failed to start services")
}
// Handle OS signals for graceful shutdown
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{}
func (s *databaseService) Name() string {
return "Database Service"
}
func (s *databaseService) Start() error {
// Database connection is initialized in main
return nil
}
func (s *databaseService) Shutdown(ctx context.Context) error {
return database.Close()
}