siftlode/backend/alembic/versions
npeter83 f255728f75 feat(stats): per-user API quota attribution + admin usage page
Track who burned how much YouTube API quota. A QuotaEvent audit log (migration
0009) records every spend with the triggering user (NULL = background/system) and
an action label, set via a request/job-scoped contextvar (quota.attribute) so no
call signatures change. User-initiated work (sync subscriptions, unsubscribe,
opt-in recent backfill, manual enrich) attributes to the user; scheduler work to
System, split by action.

- backend: QuotaEvent model + migration 0009; quota.attribute() contextvar;
  record_usage logs events; entry points wrapped (routes/sync, routes/channels,
  scheduler); GET /api/quota/my-usage + GET /api/quota/admin
- frontend: admin-only Stats page (header nav, page=stats) with daily bars +
  per-user breakdown by action and range picker; 'Your API usage' in Settings ->
  Sync for every user

Verified: attribution + endpoints compute correctly; events are per-user vs System.
2026-06-12 02:47:55 +02:00
..
0001_initial.py feat: M1 foundation — compose stack, FastAPI, Google OAuth, encrypted tokens 2026-06-11 01:01:37 +02:00
0002_ingest_core.py feat: M2 (part 1) — subscription import, YouTube client, quota guard 2026-06-11 01:22:07 +02:00
0003_tags.py feat: M3 — automatic channel tagging (language + topic system tags) 2026-06-11 01:57:19 +02:00
0004_feed_state_prefs.py feat: M4 (part 1) — feed API, watch state, user preferences 2026-06-11 02:11:02 +02:00
0005_shorts_probed.py fix: address reader-UI feedback (shorts, search, tags, login, UX) 2026-06-11 03:07:49 +02:00
0006_app_state.py feat: sync status indicator, admin pause/resume, filtered video count 2026-06-11 04:15:25 +02:00
0007_deep_requested.py feat(m5d): demand-driven deep backfill + per-user ETA 2026-06-11 23:07:09 +02:00
0008_invites.py feat(m5c): onboarding — DB invites, request-access, admin approval, email 2026-06-12 01:43:07 +02:00
0009_quota_events.py feat(stats): per-user API quota attribution + admin usage page 2026-06-12 02:47:55 +02:00