package main import ( "context" "net/http" "os" "os/signal" "syscall" "time" "github.com/dnoulet/go-first/internal/config" "github.com/dnoulet/go-first/internal/handler" "github.com/dnoulet/go-first/internal/middleware" "github.com/dnoulet/go-first/internal/service" "github.com/gin-gonic/gin" "github.com/joho/godotenv" "github.com/rs/zerolog" "github.com/rs/zerolog/log" ) func main() { if err := godotenv.Load(); err != nil { log.Info().Msg("No .env file found, using config.yaml") } cfg, err := config.Load("config.yaml") if err != nil { log.Fatal().Err(err).Msg("Failed to load config") } setupLogging(cfg.Logging) if cfg.Server.Mode == "release" { gin.SetMode(gin.ReleaseMode) } router := setupRouter(cfg) srv := &http.Server{ Addr: cfg.Server.Address(), Handler: router, ReadTimeout: cfg.Server.ReadTimeoutDuration(), WriteTimeout: cfg.Server.WriteTimeoutDuration(), IdleTimeout: cfg.Server.IdleTimeoutDuration(), } go func() { log.Info().Str("address", cfg.Server.Address()).Msg("Starting server") if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatal().Err(err).Msg("Failed to start server") } }() quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit log.Info().Msg("Shutting down server...") ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { log.Fatal().Err(err).Msg("Server forced to shutdown") } log.Info().Msg("Server exited") } func setupLogging(cfg config.LoggingConfig) { level, err := zerolog.ParseLevel(cfg.Level) if err != nil { level = zerolog.InfoLevel } zerolog.SetGlobalLevel(level) if cfg.Format == "console" || cfg.Output == "stdout" { log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}) } } func setupRouter(cfg *config.Config) *gin.Engine { router := gin.New() router.Use(middleware.Recovery()) router.Use(middleware.Logger()) router.Use(middleware.SecurityHeaders()) router.Use(middleware.CORS(cfg.CORS.AllowedOrigins)) skillService := service.NewSkillService() projectService := service.NewProjectService() experienceService := service.NewExperienceService() serviceService := service.NewServiceService() emailService := service.NewMockEmailService() contactService := service.NewContactService(emailService) h := handler.NewHandler( skillService, projectService, experienceService, serviceService, contactService, ) h.RegisterRoutes(router) router.NoRoute(func(c *gin.Context) { c.JSON(404, gin.H{ "success": false, "error": gin.H{ "code": "NOT_FOUND", "message": "Route not found", }, }) }) return router }