Files
Mangalord/backend/migrations/0027_storage_sizes.sql
MechaCat02 790549636f
Some checks failed
deploy / test-backend (push) Waiting to run
deploy / build-and-push (push) Has been cancelled
deploy / deploy (push) Has been cancelled
deploy / test-frontend (push) Has been cancelled
feat(storage): admin storage-usage stats + per-manga/chapter sizes
Persist blob sizes (covers + chapter pages) and surface upload storage
usage to admins in two places:

- Admin System tab: total/covers/chapters totals, ratio of disk, average
  image sizes, and top-5 largest mangas/chapters leaderboards, behind a
  new GET /api/v1/admin/storage endpoint (separate from /admin/system so
  the cheap DB aggregates aren't coupled to its 250ms CPU sample).
- Manga detail page: total chapter-content size and per-chapter size,
  with an em-dash for uncrawled or not-yet-measured chapters.

Sizes are captured at write time (crawler put_stream return, uploaded
page/cover byte length) into nullable pages.size_bytes /
mangas.cover_size_bytes columns; NULL means "not yet measured", distinct
from a real 0. A one-shot, idempotent admin "Backfill sizes" action
(POST /api/v1/admin/storage/backfill) stats pre-existing blobs in
keyset-paginated, capped batches and reports more_remaining so a large
legacy library can be drained over multiple runs.

Aggregates and leaderboards treat NULL honestly (only fully-measured
entities are ranked); a dashboard banner flags unmeasured rows so partial
figures aren't read as complete. persist_pages now prunes stale page rows
on a shrinking re-crawl so totals and page_count stay consistent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 20:18:41 +02:00

21 lines
1.1 KiB
SQL

-- Persist blob sizes so storage-usage stats are a pure DB aggregate
-- rather than a filesystem stat per page. Sizes are captured at write
-- time (the crawler's put_stream already returns bytes written; uploads
-- and covers know their byte length). Pre-existing rows carry NULL until
-- the admin "backfill sizes" action stats their blobs.
-- NULL = "size unknown / not yet backfilled". A measured value (even 0)
-- is distinct from unknown, so a crawled-but-un-backfilled page is never
-- mistaken for a genuinely empty one. SUM/AVG ignore NULL, so an
-- un-backfilled library reports honest partial totals rather than zeros.
ALTER TABLE pages ADD COLUMN size_bytes BIGINT;
-- Nullable for the same reason, mirroring the nullable cover_image_path.
ALTER TABLE mangas ADD COLUMN cover_size_bytes BIGINT;
-- Covering index: makes the per-chapter / per-manga SUM(size_bytes)
-- roll-ups (chapter list, leaderboards) index-only. The existing
-- pages_chapter_idx (chapter_id, page_number) can serve the WHERE filter
-- but not the SUM without heap visits.
CREATE INDEX pages_chapter_size_idx ON pages (chapter_id) INCLUDE (size_bytes);