Files
2025-08-07 20:03:53 +08:00

137 lines
3.3 KiB
Go

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)
}
// Keep using original renderer, now integrated with new template system
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")
}