package server import ( "billit/internal/handler" "billit/internal/logic" "billit/internal/view" "net/http" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" ) func (s *Server) RegisterRoutes() http.Handler { e := echo.New() e.Use(middleware.Logger()) e.Use(middleware.Recover()) e.Use(middleware.CORSWithConfig(middleware.CORSConfig{ AllowOrigins: []string{"https://*", "http://*"}, AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"}, AllowHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"}, AllowCredentials: true, MaxAge: 300, })) // Static files if s.assetsFS != nil { fileServer := http.FileServer(http.FS(s.assetsFS)) e.GET("/assets/*", echo.WrapHandler(fileServer)) } // ======================================== // Auth Setup // ======================================== authService := logic.NewAuthService(s.db) authHandlers := handler.NewAuthHandlers(authService) // ======================================== // API Routes (JSON responses) - Health only // ======================================== healthHandlers := handler.NewHealthHandlers(s.db) apiGroup := e.Group("/api") { apiGroup.GET("/health", healthHandlers.HealthHandler) } // ======================================== // Public Web Routes (no auth required) // ======================================== e.GET("/", authHandlers.LoginPageHandler) e.POST("/login", authHandlers.LoginHandler) e.GET("/register", authHandlers.RegisterPageHandler) e.POST("/register", authHandlers.RegisterHandler) e.GET("/logout", authHandlers.LogoutHandler) // ======================================== // Protected Web Routes (auth required) // ======================================== protected := e.Group("") protected.Use(authHandlers.AuthMiddleware) // Home homeHandlers := handler.NewHomeHandlers(s.db) protected.GET("/home", homeHandlers.HomePageHandler) // Account routes accountHandlers := handler.NewAccountHandlers(s.db, authService) protected.GET("/account", accountHandlers.AccountPageHandler) protected.POST("/account/details", accountHandlers.UpdateDetailsHandler) protected.POST("/account/password", accountHandlers.ChangePasswordHandler) // Buyer routes buyerHandlers := handler.NewBuyerHandlers(s.db) protected.GET("/buyer", buyerHandlers.BuyerListHandler) protected.GET("/buyer/create", buyerHandlers.BuyerCreatePageHandler) protected.POST("/buyer/create", buyerHandlers.BuyerCreateHandler) protected.GET("/buyer/edit/:id", buyerHandlers.BuyerEditPageHandler) protected.POST("/buyer/edit/:id", buyerHandlers.BuyerUpdateHandler) protected.DELETE("/buyer/:id", buyerHandlers.BuyerDeleteHandler) // Invoices list invoicesHandlers := handler.NewInvoicesHandlers(s.db) protected.GET("/invoice", invoicesHandlers.InvoicesListHandler) // Modal routes modalHandlers := handler.NewModalHandlers() protected.GET("/modal/confirm", modalHandlers.ConfirmHandler) // Product routes (web UI) productHandlers := handler.NewProductHandlers(s.db) protected.GET("/product", productHandlers.ProductListHandler) protected.GET("/product/create", productHandlers.ProductCreatePageHandler) protected.POST("/product/create", productHandlers.ProductCreateHandler) protected.GET("/product/edit/:sku", productHandlers.ProductEditPageHandler) protected.POST("/product/edit/:sku", productHandlers.ProductUpdateHandler) protected.DELETE("/product/:sku", productHandlers.ProductDeleteHandler) // Billing routes (web UI) billingHandlers := handler.NewBillingHandlers(s.db) protected.GET("/billing", billingHandlers.BillingPageHandler) protected.POST("/billing/calculate", billingHandlers.CalculateBillHandler) protected.POST("/billing/generate", billingHandlers.GenerateBillHandler) protected.GET("/billing/add-row", billingHandlers.AddProductRowHandler) // Invoice view (protected - only owner can view) protected.GET("/invoice/:id", billingHandlers.ShowInvoiceHandler) // Legacy health check (kept for backward compatibility) e.GET("/health", healthHandlers.HealthHandler) // Custom 404 handler for Echo HTTP errors e.HTTPErrorHandler = func(err error, c echo.Context) { if he, ok := err.(*echo.HTTPError); ok { switch he.Code { case http.StatusNotFound: _ = view.RenderNotFound(c, "") return case http.StatusInternalServerError: _ = view.RenderServerError(c, "") return } } // Default error handler for other cases e.DefaultHTTPErrorHandler(err, c) } // Catch-all for undefined routes (must be last) e.RouteNotFound("/*", func(c echo.Context) error { return view.RenderNotFound(c, "") }) return e }