package main import ( "fmt" "log/slog" "os" "os/signal" "syscall" "time" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/cors" "github.com/gofiber/fiber/v3/middleware/recover" "github.com/rogeecn/database_render/internal/config" "github.com/rogeecn/database_render/internal/database" "github.com/rogeecn/database_render/internal/handler" "github.com/rogeecn/database_render/internal/repository" "github.com/rogeecn/database_render/internal/service" "github.com/rogeecn/database_render/internal/template" ) func main() { // Initialize structured logging opts := &slog.HandlerOptions{ Level: slog.LevelInfo, } logger := slog.New(slog.NewJSONHandler(os.Stdout, opts)) slog.SetDefault(logger) // Load configuration cfg, err := config.LoadConfig("") if err != nil { slog.Error("failed to load configuration", "error", err) os.Exit(1) } // Initialize database db, err := database.NewConnectionManager(cfg) if err != nil { slog.Error("failed to initialize database", "error", err) os.Exit(1) } defer func() { if err := db.Close(); err != nil { slog.Error("failed to close database", "error", err) } }() // Initialize repository and services repo := repository.NewDataRepository(db, cfg) svc := service.NewDataService(repo, cfg) if err := svc.ValidateConfiguration(); err != nil { slog.Error("configuration validation failed", "error", err) os.Exit(1) } // Initialize renderer renderer, err := template.NewRenderer(svc, cfg) if err != nil { slog.Error("failed to initialize renderer", "error", err) os.Exit(1) } // Initialize handlers dataHandler := handler.NewDataHandler(svc) // Initialize Fiber app app := fiber.New(fiber.Config{ ReadTimeout: 30 * time.Second, WriteTimeout: 30 * time.Second, IdleTimeout: 60 * time.Second, ErrorHandler: renderer.ErrorHandler, Views: nil, // Using custom renderer }) // Setup middleware app.Use(recover.New()) app.Use(cors.New(cors.Config{ AllowOrigins: []string{"*"}, AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, AllowHeaders: []string{"Origin", "Content-Type", "Accept"}, })) // Setup routes // API routes dataHandler.SetupRoutes(app) // Template routes app.Get("/", func(c fiber.Ctx) error { tableName := c.Query("table") if tableName == "" { return renderer.RenderIndex(c) } return renderer.RenderList(c, tableName) }) // Setup static file serving renderer.ServeStatic(app) // Health check endpoint app.Get("/health", func(c fiber.Ctx) error { return c.JSON(map[string]string{ "status": "ok", "timestamp": time.Now().Format(time.RFC3339), }) }) // Start server port := cfg.App.Port if port == 0 { port = 8080 } // Start server in a goroutine go func() { slog.Info("starting server", "port", port) if err := app.Listen(fmt.Sprintf(":%d", port)); err != nil { slog.Error("server error", "error", err) os.Exit(1) } }() // Wait for interrupt signal to gracefully shutdown the server quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit slog.Info("shutting down server...") // Shutdown server if err := app.Shutdown(); err != nil { slog.Error("server shutdown error", "error", err) os.Exit(1) } slog.Info("server gracefully stopped") }