package main import ( "flag" "fmt" "log" "os" "os/signal" "syscall" "Noooste/garage-ui/internal/auth" "Noooste/garage-ui/internal/config" "Noooste/garage-ui/internal/handlers" "Noooste/garage-ui/internal/routes" "Noooste/garage-ui/internal/services" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/logger" "github.com/gofiber/fiber/v3/middleware/recover" ) const version = "0.1.0" func main() { // Parse command-line flags configPath := flag.String("config", "config.yaml", "Path to configuration file") flag.Parse() // Load configuration log.Printf("Loading configuration from: %s", *configPath) cfg, err := config.Load(*configPath) if err != nil { log.Fatalf("Failed to load configuration: %v", err) } log.Printf("Starting Garage UI Backend v%s in %s mode", version, cfg.Server.Environment) // Initialize services log.Println("Initializing S3 service...") s3Service := services.NewS3Service(&cfg.Garage) log.Printf("Initializing authentication service (mode: %s)...", cfg.Auth.Mode) authService, err := auth.NewAuthService(&cfg.Auth) if err != nil { log.Fatalf("Failed to initialize auth service: %v", err) } // Initialize handlers healthHandler := handlers.NewHealthHandler(version) bucketHandler := handlers.NewBucketHandler(s3Service) objectHandler := handlers.NewObjectHandler(s3Service) userHandler := handlers.NewUserHandler() // Create Fiber app with configuration app := fiber.New(fiber.Config{ AppName: "Garage UI Backend v" + version, DisableStartupMessage: false, EnablePrintRoutes: cfg.IsDevelopment(), ErrorHandler: customErrorHandler, }) // Apply global middleware app.Use(recover.New()) // Panic recovery app.Use(logger.New(logger.Config{ Format: "[${time}] ${status} - ${method} ${path} (${latency})\n", })) // Setup routes log.Println("Setting up routes...") routes.SetupRoutes( app, cfg, authService, healthHandler, bucketHandler, objectHandler, userHandler, ) // Start server in a goroutine go func() { addr := cfg.GetAddress() log.Printf("Server listening on %s", addr) log.Printf("Health check available at: http://%s/health", addr) log.Printf("API documentation: http://%s/api/v1/", addr) if err := app.Listen(addr); err != nil { 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, os.Interrupt, syscall.SIGTERM) <-quit log.Println("Shutting down server...") if err := app.Shutdown(); err != nil { log.Fatalf("Server shutdown failed: %v", err) } log.Println("Server stopped gracefully") } // customErrorHandler handles errors globally func customErrorHandler(c fiber.Ctx, err error) error { // Default to 500 Internal Server Error code := fiber.StatusInternalServerError // Check if it's a Fiber error if e, ok := err.(*fiber.Error); ok { code = e.Code } // Log the error log.Printf("Error: %v", err) // Return JSON error response return c.Status(code).JSON(fiber.Map{ "success": false, "error": fiber.Map{ "code": fmt.Sprintf("ERROR_%d", code), "message": err.Error(), }, }) }