lookbook/internal/handlers/router.go
soup cdcc5b5293
Initial commit: Lookbook personal collection app
Pinterest-like app for saving images, videos, quotes, and embeds.

Features:
- Go backend with PostgreSQL, SSR templates
- Console-based admin auth (login/logout via browser console)
- Item types: images, videos (ffmpeg transcoding), quotes, embeds
- Media stored as BLOBs in PostgreSQL
- OpenGraph metadata extraction for links
- Embed detection for YouTube, Vimeo, Twitter/X
- Masonry grid layout, item detail pages
- Tag system with filtering
- Refresh metadata endpoint with change warnings
- Replace media endpoint for updating item images/videos
2026-01-17 01:09:23 -05:00

75 lines
1.7 KiB
Go

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)
}