refactor(handlers): relax library_id validation for All Libraries

- 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.
This commit is contained in:
2026-05-18 17:52:37 -04:00
parent 6a352a6afb
commit a6f5d8d693
3 changed files with 36 additions and 40 deletions
+12 -11
View File
@@ -169,7 +169,7 @@ func (h *CollectionHandler) GetCollections(c *echo.Context) error {
items, countErr := h.db.GetCollectionItemsForDashboard(c.Request().Context(), database.GetCollectionItemsForDashboardParams{ items, countErr := h.db.GetCollectionItemsForDashboard(c.Request().Context(), database.GetCollectionItemsForDashboardParams{
CollectionID: pgtype.UUID{Bytes: col.ID.Bytes, Valid: true}, CollectionID: pgtype.UUID{Bytes: col.ID.Bytes, Valid: true},
LibraryID: pgtype.UUID{Bytes: libUUID, Valid: true}, LibraryID: pgtype.UUID{Bytes: libUUID, Valid: true},
Limit: 10000, Limit: pgtype.Int4{Int32: 10000, Valid: true},
}) })
if countErr == nil { if countErr == nil {
for _, item := range items { for _, item := range items {
@@ -223,11 +223,16 @@ func (h *CollectionHandler) GetCollection(c *echo.Context) error {
libraryID := c.QueryParam("library_id") libraryID := c.QueryParam("library_id")
var bookList []BookInfo var bookList []BookInfo
if libraryID != "" && collection.QueryType.Valid && collection.QueryType.String != "" { var libUUID pgtype.UUID
libUUID, libErr := uuid.Parse(libraryID) if libraryID != "" {
if libErr != nil { parsed, parseErr := uuid.Parse(libraryID)
if parseErr != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"}) 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) user := c.Get("user").(database.Users)
userUUID := uuid.UUID(user.ID.Bytes) userUUID := uuid.UUID(user.ID.Bytes)
dashboardSvc := services.NewDashboardService(h.db) dashboardSvc := services.NewDashboardService(h.db)
@@ -251,16 +256,12 @@ func (h *CollectionHandler) GetCollection(c *echo.Context) error {
break break
} }
} }
} else if libraryID != "" { } else if libUUID.Valid {
libUUID, libErr := uuid.Parse(libraryID)
if libErr != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"})
}
collItems, collErr := h.db.GetCollectionItemsForDashboard(c.Request().Context(), collItems, collErr := h.db.GetCollectionItemsForDashboard(c.Request().Context(),
database.GetCollectionItemsForDashboardParams{ database.GetCollectionItemsForDashboardParams{
CollectionID: pgtype.UUID{Bytes: collectionID, Valid: true}, CollectionID: pgtype.UUID{Bytes: collectionID, Valid: true},
LibraryID: pgtype.UUID{Bytes: libUUID, Valid: true}, LibraryID: libUUID,
Limit: 10000, Limit: pgtype.Int4{Int32: 10000, Valid: true},
}) })
if collErr != nil { if collErr != nil {
bookList = []BookInfo{} bookList = []BookInfo{}
+11 -9
View File
@@ -30,13 +30,14 @@ func (h *DashboardHandler) GetSections(c *echo.Context) error {
userUUID := uuid.UUID(user.ID.Bytes) userUUID := uuid.UUID(user.ID.Bytes)
libraryID := c.QueryParam("library_id") libraryID := c.QueryParam("library_id")
if libraryID == "" { var libUUID pgtype.UUID
return c.JSON(http.StatusBadRequest, map[string]string{"error": "library_id required"}) if libraryID != "" {
} parsed, err := uuid.Parse(libraryID)
libUUID, err := uuid.Parse(libraryID)
if err != nil { if err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"}) 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) 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) userUUID := uuid.UUID(user.ID.Bytes)
libraryID := c.QueryParam("library_id") libraryID := c.QueryParam("library_id")
if libraryID == "" { var libUUID pgtype.UUID
return c.JSON(http.StatusBadRequest, map[string]string{"error": "library_id required"}) if libraryID != "" {
} parsed, err := uuid.Parse(libraryID)
libUUID, err := uuid.Parse(libraryID)
if err != nil { if err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"}) return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"})
} }
libUUID = pgtype.UUID{Bytes: parsed, Valid: true}
}
prefs, err := h.dashboardService.GetDashboardPreferences(c.Request().Context(), userUUID, libUUID) prefs, err := h.dashboardService.GetDashboardPreferences(c.Request().Context(), userUUID, libUUID)
if err != nil { if err != nil {
// Return default preferences instead of 404 when none exist // Return default preferences instead of 404 when none exist
+8 -15
View File
@@ -9,6 +9,7 @@ import (
"strconv" "strconv"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
"github.com/labstack/echo/v5" "github.com/labstack/echo/v5"
) )
@@ -24,13 +25,14 @@ func NewSeriesHandler(db *database.Queries) *SeriesHandler {
func (h *SeriesHandler) GetSeries(c *echo.Context) error { func (h *SeriesHandler) GetSeries(c *echo.Context) error {
libraryID := c.QueryParam("library_id") libraryID := c.QueryParam("library_id")
if libraryID == "" { var libUUID pgtype.UUID
return c.JSON(http.StatusBadRequest, map[string]string{"error": "library_id required"}) if libraryID != "" {
} parsed, err := uuid.Parse(libraryID)
libUUID, err := uuid.Parse(libraryID)
if err != nil { if err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"}) return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"})
} }
libUUID = pgtype.UUID{Bytes: parsed, Valid: true}
}
limit := 20 limit := 20
if l := c.QueryParam("limit"); l != "" { if l := c.QueryParam("limit"); l != "" {
@@ -81,21 +83,12 @@ func (h *SeriesHandler) GetSeries(c *echo.Context) error {
} }
func (h *SeriesHandler) GetSeriesBooks(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") seriesName := c.QueryParam("name")
if seriesName == "" { if seriesName == "" {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "name required"}) 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 { if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to load series books"}) 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) svc := services.NewSeriesService(db)
return svc.GetSeriesPage(ctx, libraryID, limit, offset) return svc.GetSeriesPage(ctx, libraryID, limit, offset)
} }