package main import ( "context" "log" "net/http" "os" "os/signal" "syscall" "time" "containr/internal/api" "containr/internal/config" "containr/internal/database" "containr/internal/middleware" "github.com/gin-gonic/gin" "github.com/rs/cors" ) func main() { // Load configuration cfg := config.Load() // Initialize database db, err := database.NewConnection(cfg.DatabaseURL) if err != nil { log.Fatalf("Failed to connect to database: %v", err) } defer db.Close() // Run migrations if err := db.Migrate("migrations"); err != nil { log.Printf("Warning: Failed to run migrations: %v", err) } // Seed data for development if cfg.Environment == "development" { if err := db.SeedData(); err != nil { log.Printf("Warning: Failed to seed data: %v", err) } } // Initialize Redis redis := database.NewRedis(cfg.RedisURL) // Setup Gin router if cfg.Environment == "production" { gin.SetMode(gin.ReleaseMode) } router := gin.New() // Add middleware router.Use(middleware.Logger()) router.Use(middleware.Recovery()) router.Use(middleware.RequestID()) // CORS setup c := cors.New(cors.Options{ AllowedOrigins: cfg.CORS.AllowedOrigins, AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, AllowedHeaders: []string{"Origin", "Content-Type", "Authorization"}, ExposedHeaders: []string{"Content-Length"}, AllowCredentials: true, MaxAge: 86400, }) // Wrap Gin router with CORS handler := c.Handler(router) // Initialize API routes api.SetupRoutes(router, db, redis, cfg) // Create HTTP server server := &http.Server{ Addr: ":" + cfg.Port, Handler: handler, ReadTimeout: 15 * time.Second, WriteTimeout: 15 * time.Second, IdleTimeout: 60 * time.Second, } // Start server in a goroutine go func() { log.Printf("Server starting on port %s", cfg.Port) if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("Failed to start server: %v", err) } }() // Wait for interrupt signal to gracefully shutdown the server quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit log.Println("Shutting down server...") // Create a deadline for shutdown ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() // Attempt graceful shutdown if err := server.Shutdown(ctx); err != nil { log.Printf("Server forced to shutdown: %v", err) } log.Println("Server exited") }