From a6f5d8d6938bf1da18af6caba6b7ceadd4ea014e Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Mon, 18 May 2026 17:52:37 -0400 Subject: [PATCH] refactor(handlers): relax library_id validation for All Libraries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - dashboard.go: library_id query param is now optional. Empty/missing library_id is passed as pgtype.UUID{Valid: false} to the service layer, enabling All Libraries mode. - series.go: library_id is optional for series listing. GetSeriesBooks no longer receives a libraryID — it always returns all books in a series regardless of library. - collections.go: Restructure GetCollection to handle system collections (query_type != "") with an optional libraryID. When libraryID is empty (All Libraries), GetDashboardSections receives pgtype.UUID{Valid: false} so no library filter is applied. --- internal/handlers/collections.go | 23 ++++++++++++----------- internal/handlers/dashboard.go | 26 ++++++++++++++------------ internal/handlers/series.go | 27 ++++++++++----------------- 3 files changed, 36 insertions(+), 40 deletions(-) diff --git a/internal/handlers/collections.go b/internal/handlers/collections.go index 389bae6..ae548ef 100644 --- a/internal/handlers/collections.go +++ b/internal/handlers/collections.go @@ -169,7 +169,7 @@ func (h *CollectionHandler) GetCollections(c *echo.Context) error { items, countErr := h.db.GetCollectionItemsForDashboard(c.Request().Context(), database.GetCollectionItemsForDashboardParams{ CollectionID: pgtype.UUID{Bytes: col.ID.Bytes, Valid: true}, LibraryID: pgtype.UUID{Bytes: libUUID, Valid: true}, - Limit: 10000, + Limit: pgtype.Int4{Int32: 10000, Valid: true}, }) if countErr == nil { for _, item := range items { @@ -223,11 +223,16 @@ func (h *CollectionHandler) GetCollection(c *echo.Context) error { libraryID := c.QueryParam("library_id") var bookList []BookInfo - if libraryID != "" && collection.QueryType.Valid && collection.QueryType.String != "" { - libUUID, libErr := uuid.Parse(libraryID) - if libErr != nil { + var libUUID pgtype.UUID + if libraryID != "" { + parsed, parseErr := uuid.Parse(libraryID) + if parseErr != nil { return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"}) } + libUUID = pgtype.UUID{Bytes: parsed, Valid: true} + } + + if collection.QueryType.Valid && collection.QueryType.String != "" { user := c.Get("user").(database.Users) userUUID := uuid.UUID(user.ID.Bytes) dashboardSvc := services.NewDashboardService(h.db) @@ -251,16 +256,12 @@ func (h *CollectionHandler) GetCollection(c *echo.Context) error { break } } - } else if libraryID != "" { - libUUID, libErr := uuid.Parse(libraryID) - if libErr != nil { - return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"}) - } + } else if libUUID.Valid { collItems, collErr := h.db.GetCollectionItemsForDashboard(c.Request().Context(), database.GetCollectionItemsForDashboardParams{ CollectionID: pgtype.UUID{Bytes: collectionID, Valid: true}, - LibraryID: pgtype.UUID{Bytes: libUUID, Valid: true}, - Limit: 10000, + LibraryID: libUUID, + Limit: pgtype.Int4{Int32: 10000, Valid: true}, }) if collErr != nil { bookList = []BookInfo{} diff --git a/internal/handlers/dashboard.go b/internal/handlers/dashboard.go index 651562f..e6fcc47 100644 --- a/internal/handlers/dashboard.go +++ b/internal/handlers/dashboard.go @@ -30,12 +30,13 @@ func (h *DashboardHandler) GetSections(c *echo.Context) error { userUUID := uuid.UUID(user.ID.Bytes) libraryID := c.QueryParam("library_id") - if libraryID == "" { - return c.JSON(http.StatusBadRequest, map[string]string{"error": "library_id required"}) - } - libUUID, err := uuid.Parse(libraryID) - if err != nil { - return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"}) + var libUUID pgtype.UUID + if libraryID != "" { + parsed, err := uuid.Parse(libraryID) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"}) + } + libUUID = pgtype.UUID{Bytes: parsed, Valid: true} } prefs, _ := h.dashboardService.GetDashboardPreferences(c.Request().Context(), userUUID, libUUID) @@ -202,14 +203,15 @@ func (h *DashboardHandler) GetPreferences(c *echo.Context) error { userUUID := uuid.UUID(user.ID.Bytes) libraryID := c.QueryParam("library_id") - if libraryID == "" { - return c.JSON(http.StatusBadRequest, map[string]string{"error": "library_id required"}) + var libUUID pgtype.UUID + if libraryID != "" { + parsed, err := uuid.Parse(libraryID) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"}) + } + libUUID = pgtype.UUID{Bytes: parsed, Valid: true} } - libUUID, err := uuid.Parse(libraryID) - if err != nil { - return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"}) - } prefs, err := h.dashboardService.GetDashboardPreferences(c.Request().Context(), userUUID, libUUID) if err != nil { // Return default preferences instead of 404 when none exist diff --git a/internal/handlers/series.go b/internal/handlers/series.go index 6c29970..2cd72a5 100644 --- a/internal/handlers/series.go +++ b/internal/handlers/series.go @@ -9,6 +9,7 @@ import ( "strconv" "github.com/google/uuid" + "github.com/jackc/pgx/v5/pgtype" "github.com/labstack/echo/v5" ) @@ -24,12 +25,13 @@ func NewSeriesHandler(db *database.Queries) *SeriesHandler { func (h *SeriesHandler) GetSeries(c *echo.Context) error { libraryID := c.QueryParam("library_id") - if libraryID == "" { - return c.JSON(http.StatusBadRequest, map[string]string{"error": "library_id required"}) - } - libUUID, err := uuid.Parse(libraryID) - if err != nil { - return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"}) + var libUUID pgtype.UUID + if libraryID != "" { + parsed, err := uuid.Parse(libraryID) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"}) + } + libUUID = pgtype.UUID{Bytes: parsed, Valid: true} } limit := 20 @@ -81,21 +83,12 @@ func (h *SeriesHandler) GetSeries(c *echo.Context) error { } func (h *SeriesHandler) GetSeriesBooks(c *echo.Context) error { - libraryID := c.QueryParam("library_id") - if libraryID == "" { - return c.JSON(http.StatusBadRequest, map[string]string{"error": "library_id required"}) - } - libUUID, err := uuid.Parse(libraryID) - if err != nil { - return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"}) - } - seriesName := c.QueryParam("name") if seriesName == "" { return c.JSON(http.StatusBadRequest, map[string]string{"error": "name required"}) } - books, err := h.seriesService.GetSeriesBooks(c.Request().Context(), libUUID, seriesName) + books, err := h.seriesService.GetSeriesBooks(c.Request().Context(), seriesName) if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to load series books"}) } @@ -118,7 +111,7 @@ func (h *SeriesHandler) GetSeriesBooks(c *echo.Context) error { }) } -func GetSeriesCardsData(ctx context.Context, db *database.Queries, libraryID uuid.UUID, limit, offset int) ([]services.SeriesInfo, int, error) { +func GetSeriesCardsData(ctx context.Context, db *database.Queries, libraryID pgtype.UUID, limit, offset int) ([]services.SeriesInfo, int, error) { svc := services.NewSeriesService(db) return svc.GetSeriesPage(ctx, libraryID, limit, offset) }