package handlers import ( "context" "log/slog" "net/http" "time" "lookbook/internal/data/session" ) // Router wraps http.ServeMux and automatically injects RequestContext into handlers. type Router struct { mux *http.ServeMux rc *RequestContext } func NewRouter(rc *RequestContext) *Router { return &Router{ mux: http.NewServeMux(), rc: rc, } } // Handle registers a handler that returns an error. // The RequestContext is automatically injected and error handling is applied. func (rt *Router) Handle(pattern string, h Handler) { rt.mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) { rc := &RequestContext{ DB: rt.rc.DB, Logger: rt.rc.Logger, TmplCache: rt.rc.TmplCache, IsAdmin: rt.loadAuth(r), } handler := WithErrorHandling(rc, h) handler(w, r) }) } // loadAuth checks if the request has a valid session cookie. func (rt *Router) loadAuth(r *http.Request) bool { cookie, err := r.Cookie("session_id") if err != nil { return false } ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second) defer cancel() sess, err := session.QFindBySessionID(ctx, rt.rc.DB, cookie.Value) if err != nil { rt.rc.Logger.Error("failed to find session", slog.Any("err", err)) return false } if sess == nil { return false } if time.Now().After(sess.ExpiresAt) { rt.rc.Logger.Info("session expired", slog.String("session_id", cookie.Value)) return false } return true } // HandleStd registers a standard http.Handler (for static files, etc.) func (rt *Router) HandleStd(pattern string, h http.Handler) { rt.mux.Handle(pattern, h) } func (rt *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) { rt.mux.ServeHTTP(w, r) }