quite a lot of things

This commit is contained in:
Arkaprabha Chakraborty
2025-12-06 03:05:44 +05:30
parent 39c61b7790
commit 28733e22d3
42 changed files with 4214 additions and 204 deletions

View File

@@ -3,8 +3,10 @@ package server
import (
"net/http"
"billit/cmd/web"
"github.com/a-h/templ"
"billit/internal/api"
"billit/internal/auth"
"billit/internal/web"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
@@ -22,27 +24,106 @@ func (s *Server) RegisterRoutes() http.Handler {
MaxAge: 300,
}))
// Static files
fileServer := http.FileServer(http.FS(web.Files))
e.GET("/assets/*", echo.WrapHandler(fileServer))
e.GET("/web", echo.WrapHandler(templ.Handler(web.HelloForm())))
e.POST("/hello", echo.WrapHandler(http.HandlerFunc(web.HelloWebHandler)))
// ========================================
// Auth Setup
// ========================================
userStore := auth.NewDBUserStore(s.db)
authService := auth.NewService(userStore)
authHandlers := web.NewAuthHandlers(authService)
e.GET("/", s.HelloWorldHandler)
// ========================================
// API Routes (JSON responses) - Health only, products/invoice via web UI
// ========================================
apiHandlers := api.NewHandlers(s.db)
apiGroup := e.Group("/api")
{
apiGroup.GET("/health", apiHandlers.HealthHandler)
}
e.GET("/health", s.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 := web.NewHomeHandlers(s.db)
protected.GET("/home", homeHandlers.HomePageHandler)
// Account routes
accountHandlers := web.NewAccountHandlers(s.db, authService)
protected.GET("/account", accountHandlers.AccountPageHandler)
protected.POST("/account/details", accountHandlers.UpdateDetailsHandler)
protected.POST("/account/password", accountHandlers.ChangePasswordHandler)
// Buyer routes
buyerHandlers := web.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 := web.NewInvoicesHandlers(s.db)
protected.GET("/invoice", invoicesHandlers.InvoicesListHandler)
// Product routes (web UI)
productHandlers := web.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 := web.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", apiHandlers.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:
_ = web.RenderNotFound(c, "")
return
case http.StatusInternalServerError:
_ = web.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 web.RenderNotFound(c, "")
})
return e
}
func (s *Server) HelloWorldHandler(c echo.Context) error {
resp := map[string]string{
"message": "Hello World",
}
return c.JSON(http.StatusOK, resp)
}
func (s *Server) healthHandler(c echo.Context) error {
return c.JSON(http.StatusOK, s.db.Health())
}

View File

@@ -1,39 +1,34 @@
package server
import (
"encoding/json"
"github.com/labstack/echo/v4"
"net/http"
"net/http/httptest"
"reflect"
"testing"
"billit/internal/database"
"github.com/labstack/echo/v4"
)
func TestHandler(t *testing.T) {
e := echo.New()
func TestHomeRoute(t *testing.T) {
// Create a minimal server with db for testing
db := database.New()
s := &Server{db: db}
handler := s.RegisterRoutes()
req := httptest.NewRequest(http.MethodGet, "/", nil)
resp := httptest.NewRecorder()
c := e.NewContext(req, resp)
s := &Server{}
// Assertions
if err := s.HelloWorldHandler(c); err != nil {
t.Errorf("handler() error = %v", err)
return
}
handler.ServeHTTP(resp, req)
if resp.Code != http.StatusOK {
t.Errorf("handler() wrong status code = %v", resp.Code)
return
}
expected := map[string]string{"message": "Hello World"}
var actual map[string]string
// Decode the response body into the actual map
if err := json.NewDecoder(resp.Body).Decode(&actual); err != nil {
t.Errorf("handler() error decoding response body: %v", err)
return
}
// Compare the decoded response with the expected value
if !reflect.DeepEqual(expected, actual) {
t.Errorf("handler() wrong response body. expected = %v, actual = %v", expected, actual)
return
t.Errorf("home route wrong status code = %v, want %v", resp.Code, http.StatusOK)
}
}
func TestRouterSetup(t *testing.T) {
// Test that Echo router can be set up without panic
e := echo.New()
if e == nil {
t.Error("failed to create echo instance")
}
}