Files
bookhoard/internal/router/router.go
T
john-okeefe 42b6fae297 chore(router): remove unused CollectionHandler from config
Remove the CollectionHandler field from router.Config struct and its
initialization in main.go. This field was never used - collections are
registered directly in handlers.SetupRoutes() where a CollectionHandler
is created locally.

Changes:
- Remove CollectionHandler field from internal/router/router.go Config
- Remove CollectionHandler: nil line from cmd/server/main.go

This cleans up dead code from the router refactoring. Collections
continue to work correctly as they are registered in SetupRoutes().

Related: Router refactoring completion
2026-02-07 17:59:06 -05:00

135 lines
4.2 KiB
Go

package router
import (
"bookhoard/internal/config"
"bookhoard/internal/database"
"bookhoard/internal/handlers"
"bookhoard/internal/middleware"
ratelimit "bookhoard/internal/middleware"
"bookhoard/internal/sync"
"log"
"net/http"
"time"
"github.com/go-playground/validator/v10"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
"github.com/labstack/echo-jwt/v4"
"github.com/labstack/echo/v4"
echomiddleware "github.com/labstack/echo/v4/middleware"
)
// CustomValidator wraps the go-playground validator
type CustomValidator struct {
validator *validator.Validate
}
func (cv *CustomValidator) Validate(i interface{}) error {
return cv.validator.Struct(i)
}
// Config holds all dependencies needed for route registration
type Config struct {
Echo *echo.Echo
Queries *database.Queries
Cfg *config.Config
DBPool interface{} // pgxpool.Pool interface
AuthHandler *handlers.AuthHandler
LibraryHandler *handlers.LibraryHandler
DeviceHandler *handlers.DeviceHandler
KOReaderHandler *handlers.KOReaderHandler
WSHandler *handlers.WSHandler
ConflictHandler *handlers.ConflictHandler
AnalyticsHandler *handlers.AnalyticsHandler
QueueHandler *handlers.QueueHandler
OPDSHandler *handlers.OPDSHandler
ConnManager *sync.ConnectionManager
QueueProcessor *sync.SyncQueueProcessor
DeviceAuthMiddleware *middleware.DeviceAuthMiddleware
LoginTracker *ratelimit.LoginAttemptTracker
}
// createJWTMiddleware creates a JWT middleware with proper user context setup
func createJWTMiddleware(cfg *Config) echo.MiddlewareFunc {
return echojwt.WithConfig(echojwt.Config{
SigningKey: []byte(cfg.Cfg.JWTSecret),
ContextKey: "user",
SuccessHandler: func(c echo.Context) {
token := c.Get("user").(*jwt.Token)
claims := token.Claims.(jwt.MapClaims)
c.Set("user_id", claims["user_id"])
c.Set("user_role", claims["user_role"])
c.Set("user_email", claims["user_email"])
c.Set("user_username", claims["user_username"])
// Parse UUID from string claims
userIDStr, _ := claims["user_id"].(string)
userUUID, err := uuid.Parse(userIDStr)
if err != nil {
c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid user ID in token"})
return
}
c.Set("user", database.Users{
ID: pgtype.UUID{Bytes: [16]byte(userUUID), Valid: true},
Email: claims["user_email"].(string),
Username: claims["user_username"].(string),
Role: claims["user_role"].(string),
})
},
})
}
// RegisterRoutes registers all application routes
func RegisterRoutes(cfg *Config) *handlers.Handler {
e := cfg.Echo
// Set up validator
v := validator.New()
if err := ratelimit.RegisterPasswordValidation(v); err != nil {
log.Fatal("Failed to register password validator:", err)
}
e.Validator = &CustomValidator{validator: v}
// Global middleware
e.Use(echomiddleware.Logger())
e.Use(echomiddleware.Recover())
e.Use(echomiddleware.CORS())
e.Use(ratelimit.RequestTracingMiddleware(cfg.Cfg))
// Rate limiter
rateLimiterConfig := ratelimit.RateLimiterConfig{
Enabled: cfg.Cfg.RateLimitEnabled,
RequestsPerMinute: cfg.Cfg.RequestsPerMinute,
CleanupInterval: 5 * time.Minute,
}
rateLimiter := ratelimit.NewRateLimiter(rateLimiterConfig)
rateLimitMiddleware := ratelimit.RateLimiterMiddleware(rateLimiter)
// Register core application routes (collections, devices, media, etc.) - ONCE
jwtMiddleware := createJWTMiddleware(cfg)
protected := e.Group("/api", jwtMiddleware)
ebookHandler := handlers.SetupRoutes(protected, cfg.Queries, cfg.ConnManager)
// Register route groups
registerAuthRoutes(cfg, rateLimitMiddleware)
registerLibraryRoutes(cfg)
registerDeviceRoutes(cfg)
registerSyncRoutes(cfg)
registerMediaRoutes(cfg)
registerConflictRoutes(cfg)
registerAnalyticsRoutes(cfg)
registerQueueRoutes(cfg)
registerOPDSRoutes(cfg)
registerWebSocketRoutes(cfg)
registerFrontendRoutes(cfg)
registerDocumentationRoutes(cfg)
// Register scanner routes (admin only)
admin := protected.Group("", handlers.AdminMiddleware)
registerScannerRoutes(admin, ebookHandler)
return ebookHandler
}