feat(web): add logging middleware and refactor handlers

Introduced LoggerMiddleware for HTTP request logging. Refactored handler
methods to return http.HandlerFunc for improved composability. Updated
route registration and tests to use new handler signatures.
This commit is contained in:
Ruidy 2025-09-28 19:43:30 +02:00
parent d112a4f6e8
commit bf721dc130
No known key found for this signature in database
GPG key ID: 705C24D202990805
6 changed files with 77 additions and 47 deletions

View file

@ -9,7 +9,8 @@ import (
"github.com/rjNemo/payit/internal/payments"
)
func (h *Handler) createCheckoutSession(w http.ResponseWriter, r *http.Request) {
func (h *Handler) createCheckoutSession() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req payments.CheckoutSessionRequest
if r.Body != nil {
@ -43,4 +44,5 @@ func (h *Handler) createCheckoutSession(w http.ResponseWriter, r *http.Request)
http.Error(w, "failed to encode response", http.StatusInternalServerError)
return
}
}
}

View file

@ -37,7 +37,7 @@ func TestCreateCheckoutSessionSuccess(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, "/api/checkout", bytes.NewReader(body))
rec := httptest.NewRecorder()
handler.createCheckoutSession(rec, req)
handler.createCheckoutSession()(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d", rec.Code)
@ -70,7 +70,7 @@ func TestCreateCheckoutSessionDefaultsQuantity(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, "/api/checkout", http.NoBody)
rec := httptest.NewRecorder()
handler.createCheckoutSession(rec, req)
handler.createCheckoutSession()(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d", rec.Code)
@ -86,7 +86,7 @@ func TestCreateCheckoutSessionRejectsInvalidJSON(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, "/api/checkout", bytes.NewBufferString("{"))
rec := httptest.NewRecorder()
handler.createCheckoutSession(rec, req)
handler.createCheckoutSession()(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("expected status 400, got %d", rec.Code)
@ -101,7 +101,7 @@ func TestCreateCheckoutSessionStripeFailure(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, "/api/checkout", http.NoBody)
rec := httptest.NewRecorder()
handler.createCheckoutSession(rec, req)
handler.createCheckoutSession()(rec, req)
if rec.Code != http.StatusInternalServerError {
t.Fatalf("expected status 500, got %d", rec.Code)
@ -111,7 +111,7 @@ func TestCreateCheckoutSessionStripeFailure(t *testing.T) {
func TestCreateCheckoutSessionMethodNotAllowed(t *testing.T) {
handler := &Handler{checkout: &fakeCheckoutService{}}
mux := http.NewServeMux()
mux.HandleFunc("POST /api/checkout", handler.createCheckoutSession)
mux.HandleFunc("POST /api/checkout", handler.createCheckoutSession())
req := httptest.NewRequest(http.MethodGet, "/api/checkout", http.NoBody)
rec := httptest.NewRecorder()

View file

@ -0,0 +1,26 @@
package web
import (
"log"
"net/http"
"time"
)
type WrappedWriter struct {
http.ResponseWriter
StatusCode int
}
func (w *WrappedWriter) WriteHeader(statusCode int) {
w.StatusCode = statusCode
w.ResponseWriter.WriteHeader(statusCode)
}
func LoggerMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
wrapped := &WrappedWriter{ResponseWriter: w, StatusCode: http.StatusOK}
next.ServeHTTP(wrapped, r)
log.Printf("%s %s %d %v", r.Method, r.URL.Path, wrapped.StatusCode, time.Since(start))
})
}

View file

@ -13,7 +13,8 @@ type checkoutPageData struct {
Currency string
}
func (h *Handler) renderCheckoutPage(w http.ResponseWriter, r *http.Request) {
func (h *Handler) renderCheckoutPage() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
price := float64(h.cfg.Product.PriceCents) / 100
data := checkoutPageData{
ProductName: h.cfg.Product.Name,
@ -27,4 +28,5 @@ func (h *Handler) renderCheckoutPage(w http.ResponseWriter, r *http.Request) {
http.Error(w, "failed to render page", http.StatusInternalServerError)
return
}
}
}

View file

@ -5,7 +5,7 @@ import (
)
func (h *Handler) registerRoutes(mux *http.ServeMux) {
mux.HandleFunc("POST /api/checkout", h.createCheckoutSession)
mux.Handle("GET /", http.HandlerFunc(h.renderCheckoutPage))
mux.Handle("POST /api/checkout", h.createCheckoutSession())
mux.Handle("GET /", h.renderCheckoutPage())
mux.Handle("GET /static/", http.StripPrefix("/static/", http.FileServer(http.FS(h.fs))))
}

View file

@ -41,5 +41,5 @@ func NewServer(cfg config.Config) http.Handler {
mux := http.NewServeMux()
h.registerRoutes(mux)
return mux
return LoggerMiddleware(mux)
}