package router import ( "bookhoard/internal/database" "bookhoard/internal/handlers" "bookhoard/templates" "bytes" "log" "net/http" "github.com/labstack/echo/v5" ) func registerSearchRoutes(cfg *Config) { e := cfg.Echo // JWT middleware for protected routes jwtMiddleware := createJWTMiddleware(cfg) protected := e.Group("/api", jwtMiddleware) // Search endpoint - returns JSON or HTML based on HX-Request header protected.GET("/media-items/search", func(c *echo.Context) error { // Check if HTMX request (expects HTML) if c.Request().Header.Get("HX-Request") == "true" { return handleSearchHTML(c, cfg) } // Otherwise return JSON (API clients) return cfg.MediaHandler.SearchMediaItems(c) }) // Facet-value listings for the filter panel (faceted-navigation // Batch C): /api/media-items/facets?facet=X&library_id=... protected.GET("/media-items/facets", cfg.MediaHandler.GetFacets) // Rule-builder live count (faceted plan Phase 9): the composed // tree's true match count (COUNT(*)), rules + library scope. protected.GET("/media-items/filter-count", cfg.MediaHandler.FilterCount) protected.POST("/sync/books/query", cfg.MatchingHandler.QueryBooks) } // handleSearchHTML renders HTML response for HTMX requests func handleSearchHTML(c *echo.Context, cfg *Config) error { user, ok := c.Get("user").(database.Users) if !ok { return c.String(http.StatusUnauthorized, "Unauthorized") } // Extract filters via the SHARED parser (handlers.ParseMediaSearchParams) // — the HTMX grid partial and the JSON endpoint can never disagree on // which facet params exist or how they parse. (This branch previously // dropped publisher_filter and narrator_filter entirely — the shared // parser fixes that too.) params := handlers.ParseMediaSearchParams(c) params.UserID = user.ID // Filter rule builder: a malformed qf is a 400, never a silently // ignored token. if params.FilterParseErr != "" { return c.HTML(http.StatusBadRequest, `