package router import ( "bookhoard/internal/database" "bookhoard/internal/handlers" "bookhoard/internal/services" "bookhoard/internal/utils" "bookhoard/templates" "bytes" "log" "net/http" "strconv" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgtype" "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) }) 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 query parameters query := c.QueryParam("q") libraryID := c.QueryParam("library_id") limit, _ := strconv.Atoi(c.QueryParam("limit")) if limit == 0 { limit = 50 } offset, _ := strconv.Atoi(c.QueryParam("offset")) // Validate library_id var libUUID pgtype.UUID if libraryID != "" { lib, err := uuid.Parse(libraryID) if err != nil { return c.String(http.StatusBadRequest, "invalid library_id") } libUUID = pgtype.UUID{Bytes: lib, Valid: true} } else { libUUID = pgtype.UUID{Valid: false} } // Extract filters authorFilter := c.QueryParam("author_filter") seriesFilter := c.QueryParam("series_filter") genreFilter := c.QueryParam("genre_filter") tagsFilter := c.QueryParam("tags_filter") languageFilter := c.QueryParam("language_filter") yearMin, _ := strconv.Atoi(c.QueryParam("year_min")) yearMax, _ := strconv.Atoi(c.QueryParam("year_max")) hasCover := c.QueryParam("has_cover") == "true" sortParam := c.QueryParam("sort") if sortParam == "" { sortParam = "title ASC" } // Build search params params := services.SearchParams{ UserID: user.ID, LibraryID: libUUID, SearchQuery: query, AuthorFilter: authorFilter, SeriesFilter: seriesFilter, GenreFilter: genreFilter, TagsFilter: tagsFilter, LanguageFilter: languageFilter, YearMin: yearMin, YearMax: yearMax, HasCover: hasCover, Sort: sortParam, Limit: limit, Offset: offset, } // Execute search using handler method results, totalCount, err := cfg.MediaHandler.ExecuteSearch(c.Request().Context(), params) if err != nil { log.Printf("Search error: %v", err) return c.HTML(http.StatusInternalServerError, `