package web import ( "context" "log" "os" "os/signal" "strconv" "syscall" "time" "git.ma-al.com/goc_daniel/b2b/app/config" "git.ma-al.com/goc_daniel/b2b/app/delivery/middleware" "git.ma-al.com/goc_daniel/b2b/app/delivery/web/api" "git.ma-al.com/goc_daniel/b2b/app/delivery/web/api/public" "git.ma-al.com/goc_daniel/b2b/app/delivery/web/api/restricted" "git.ma-al.com/goc_daniel/b2b/app/delivery/web/general" // "github.com/gofiber/fiber/v2/middleware/filesystem" "github.com/gofiber/fiber/v3" // "github.com/gofiber/fiber/v3/middleware/filesystem" "github.com/gofiber/fiber/v3/middleware/logger" "github.com/gofiber/fiber/v3/middleware/recover" ) // Server represents the web server type Server struct { app *fiber.App cfg *config.Config api fiber.Router public fiber.Router restricted fiber.Router } // App returns the fiber app func (s *Server) App() *fiber.App { return s.app } // Cfg returns the config func (s *Server) Cfg() *config.Config { return s.cfg } // New creates a new server instance func New() *Server { return &Server{ app: fiber.New(fiber.Config{ ErrorHandler: customErrorHandler, }), cfg: config.Get(), } } // Setup configures the server with routes and middleware func (s *Server) Setup() error { // Global middleware s.app.Use(recover.New()) s.app.Use(logger.New()) // CORS middleware s.app.Use(middleware.CORSMiddleware()) // Language middleware - discovers client's language and stores in context s.app.Use(middleware.LanguageMiddleware()) // initialize healthcheck general.InitHealth(s.App(), s.Cfg()) // serve favicon general.Favicon(s.app, s.cfg) // initialize swagger endpoints general.InitSwagger(s.App()) // API routes s.api = s.app.Group("/api/v1") s.public = s.api.Group("/public") s.restricted = s.api.Group("/restricted") s.restricted.Use(middleware.AuthMiddleware()) // initialize language endpoints (general) api.NewLangHandler().InitLanguage(s.api, s.cfg) // Settings endpoint (general) api.NewSettingsHandler().InitSettings(s.api, s.cfg) // Auth routes (public) auth := s.public.Group("/auth") public.AuthHandlerRoutes(auth) // Repo routes (restricted) productDescription := s.restricted.Group("/product-description") restricted.ProductDescriptionHandlerRoutes(productDescription) // // Restricted routes example // restricted := s.api.Group("/restricted") // restricted.Use(middleware.AuthMiddleware()) // restricted.Get("/dashboard", func(c fiber.Ctx) error { // user := middleware.GetUser(c) // return c.JSON(fiber.Map{ // "message": "Welcome to the protected area", // "user": user, // }) // }) // // Admin routes example // admin := s.api.Group("/admin") // admin.Use(middleware.AuthMiddleware()) // admin.Use(middleware.RequireAdmin()) // admin.Get("/users", func(c fiber.Ctx) error { // return c.JSON(fiber.Map{ // "message": "Admin area - user management", // }) // }) // keep this at the end because its wilderange general.InitBo(s.App()) return nil } // Run starts the server func (s *Server) Run() error { // Run database migrations // if err := db.RunMigrations(); err != nil { // log.Printf("⚠️ Database migrations failed: %v", err) // } else { // log.Println("✓ Database migrations completed") // } // // Seed admin user // if err := db.SeedAdminUser("admin@example.com", "admin123"); err != nil { // log.Printf("⚠️ Admin user seeding failed: %v", err) // } addr := s.cfg.Server.Host + ":" + strconv.Itoa(s.cfg.Server.Port) log.Printf("Starting server on %s", addr) log.Printf("Swagger UI available at http://%s/swagger/index.html", addr) log.Printf("OpenAPI JSON available at http://%s/openapi.json", addr) go func() { if err := s.app.Listen(":3000"); err != nil { log.Println("Server stopped:", err) } }() // Wait for shutdown signal quit := make(chan os.Signal, 1) signal.Notify( quit, syscall.SIGINT, // Ctrl+C syscall.SIGTERM, // docker stop ) <-quit log.Println("Shutting down server...") // graceful shutdown with timeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := s.app.ShutdownWithContext(ctx); err != nil { log.Fatal("Shutdown error:", err) } log.Println("Server exited cleanly") return nil } // customErrorHandler handles errors func customErrorHandler(c fiber.Ctx, err error) error { code := fiber.StatusInternalServerError if e, ok := err.(*fiber.Error); ok { code = e.Code } return c.Status(code).JSON(fiber.Map{ "error": err.Error(), }) }