diff --git a/internal/app/files/gif_admin.go b/internal/app/files/gif_admin.go index a0056248..b9711087 100644 --- a/internal/app/files/gif_admin.go +++ b/internal/app/files/gif_admin.go @@ -142,22 +142,23 @@ func (s *Service) createGifCatalogEntry(ctx context.Context, title string, docum return entry, nil } -// AdminListGifCatalog returns every entry (enabled and disabled), for the -// admin panel's list view. +// AdminListGifCatalog returns every entry (enabled and disabled), unbounded, +// for the admin panel's list view. func (s *Service) AdminListGifCatalog(ctx context.Context) ([]domain.GifCatalogEntry, error) { if s.gifCatalog == nil { return nil, domain.ErrGifCatalogUnavailable } - return s.gifCatalog.ListGifCatalog(ctx, false) + return s.gifCatalog.ListGifCatalog(ctx, false, 0) } // ListGifCatalog is bots.gifCatalogSource's read: onlyEnabled=true is what -// @gif actually serves. +// @gif actually serves, capped at domain.MaxGifCatalogEntries -- the real +// per-response limit the client enforces. func (s *Service) ListGifCatalog(ctx context.Context, onlyEnabled bool) ([]domain.GifCatalogEntry, error) { if s.gifCatalog == nil { return nil, nil } - return s.gifCatalog.ListGifCatalog(ctx, onlyEnabled) + return s.gifCatalog.ListGifCatalog(ctx, onlyEnabled, domain.MaxGifCatalogEntries) } // AdminSetGifCatalogEnabled toggles whether an entry is served. @@ -219,7 +220,7 @@ func (s *Service) AdminAutoCategorizeGifCatalog(ctx context.Context) (int, error if s.gifCatalog == nil { return 0, domain.ErrGifCatalogUnavailable } - entries, err := s.gifCatalog.ListGifCatalog(ctx, false) + entries, err := s.gifCatalog.ListGifCatalog(ctx, false, 0) if err != nil { return 0, err } diff --git a/internal/store/gif_catalog.go b/internal/store/gif_catalog.go index 3e13c1c2..c88504b8 100644 --- a/internal/store/gif_catalog.go +++ b/internal/store/gif_catalog.go @@ -19,9 +19,14 @@ type GifCatalogStore interface { // Always false for an empty filename (that's the panel-upload sentinel, // never a real seed match). HasGifCatalogSourceFilename(ctx context.Context, filename string) (bool, error) - // ListGifCatalog returns every entry ordered by (sort_order, id). - // onlyEnabled=true is what @gif serves; the admin panel lists everything. - ListGifCatalog(ctx context.Context, onlyEnabled bool) ([]domain.GifCatalogEntry, error) + // ListGifCatalog returns entries ordered by (sort_order, id). + // onlyEnabled=true is what @gif serves. limit>0 caps the result (@gif's + // live-serving path passes domain.MaxGifCatalogEntries -- the real TL-level + // cap one inline response can carry); limit<=0 means unbounded, which is + // what the admin panel and bulk operations like auto-categorize need -- + // capping those at the same 50 the client renders per response would + // silently only ever touch the first page of a real catalog. + ListGifCatalog(ctx context.Context, onlyEnabled bool, limit int) ([]domain.GifCatalogEntry, error) // SetGifCatalogEnabled toggles whether an entry is served. changed=false // if the id doesn't exist. SetGifCatalogEnabled(ctx context.Context, id int64, enabled bool) (bool, error) diff --git a/internal/store/postgres/gif_catalog.go b/internal/store/postgres/gif_catalog.go index f05f4f39..4e41583b 100644 --- a/internal/store/postgres/gif_catalog.go +++ b/internal/store/postgres/gif_catalog.go @@ -48,13 +48,19 @@ func (s *GifCatalogStore) HasGifCatalogSourceFilename(ctx context.Context, filen return exists, nil } -func (s *GifCatalogStore) ListGifCatalog(ctx context.Context, onlyEnabled bool) ([]domain.GifCatalogEntry, error) { +func (s *GifCatalogStore) ListGifCatalog(ctx context.Context, onlyEnabled bool, limit int) ([]domain.GifCatalogEntry, error) { + // Postgres treats LIMIT NULL as "no limit" -- a nil *int parameter gets + // there without a second query string for the unbounded (admin) case. + var limitParam *int + if limit > 0 { + limitParam = &limit + } rows, err := s.db.Query(ctx, ` SELECT id, title, document_id, enabled, sort_order, created_by, created_at, updated_at, source_filename, category FROM gif_catalog WHERE NOT $1 OR enabled ORDER BY sort_order, id -LIMIT `+fmt.Sprint(domain.MaxGifCatalogEntries), onlyEnabled) +LIMIT $2`, onlyEnabled, limitParam) if err != nil { return nil, fmt.Errorf("list gif catalog: %w", err) }