Rework the admin Overview tab into a full-width vertical list of
per-section summary cards (System, Crawler, Analysis, Mangas, Users,
Settings), each linking to its tab. Crawler and Analysis cards update
live over the existing SSE streams; System/Mangas/Users poll; the
Settings strip reflects daemon state. The disk/memory/CPU boxes move
into the System card.
Add a composed GET /admin/overview endpoint returning Users, Mangas and
Analysis aggregates (user counts + newest, manga sync-state counts +
chapter/page totals + newest, library analysis coverage), reusing the
existing MANGA_SYNC_STATE_CASE and the storage handler's try_join fan-out.
Extend the System tab with hardware sensors: CPU load average and
per-core usage (sysinfo), plus temperatures via sysinfo's Components API
(enabled the `component` feature). Sensors degrade to an "unavailable"
state when not exposed (e.g. inside containers without /sys access), and
a temperature at/above its critical threshold raises a warning alert.
Tests: integration tests for /admin/overview and the extended /admin/system
shape; vitest coverage for the new client types and getOverviewStats.
Bump to 0.85.0 (minor) in lockstep.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
Adds GET /api/v1/admin/system returning disk (scoped to storage_dir
via statvfs), memory, CPU, and a server-side alerts array that fires
at >90% disk or memory.
Disk uses nix::sys::statvfs directly rather than sysinfo's Disks API
to avoid mountpoint-matching gymnastics for the storage_dir. A new
`Storage::local_root() -> Option<&Path>` trait method exposes the
root; the default returns None so a future S3Storage gets `disk:
null` in the response instead of fabricated numbers.
CPU is sampled inline (refresh → 250ms sleep → refresh → read) so the
endpoint adds 250ms of latency per call. No background-cache yet —
admin traffic is low-volume and the moving parts aren't worth it
until polling shows up.
Alerts are evaluated server-side so the frontend can render them
without re-implementing the thresholds.