Vigil
← All news

v1.2.0 — FFLogs user-OAuth (archived + private reports via Gold tier)

- **`fflogs_user_auth` table** (single-row, `id=1`) stores the connected user's refresh token + cached access token + scope. Alembic migration `267acc60ac83`. - **`FFLogsClient` u…

Added — user-OAuth (authorization code) flow

  • fflogs_user_auth table (single-row, id=1) stores the connected user's refresh token + cached access token + scope. Alembic migration 267acc60ac83.
  • FFLogsClient user-OAuth methods in ingest/fflogs.py: build_authorize_url(redirect_uri, state, scope), exchange_authorization_code(session, code, redirect_uri), refresh_user_token(session), graphql_user(session, query, variables), has_user_auth(session), user_auth_status(session). Refresh token rotates per FFLogs spec — we update the row on each refresh.
  • FFLogsArchivedError subclass of FFLogsAPIErrorgraphql() raises this specifically when the response is the "report archived, use /user" paywall, so callers can distinguish it.
  • graphql_with_archive_retry(session, query, variables) — convenience: try /client, fall back to /user only on FFLogsArchivedError and only when a user is connected. Used by both ingest/events.py::ingest_events_for_report (watched-report path, T-101) and jobs/backfill_field.py::_ingest_fight_events (field backfill, T-201). Default path stays cheap; the fallback only fires when needed.
  • API endpoints in api/main.py:
    • GET /auth/fflogs/login — generates an OAuth state, redirects to FFLogs consent screen.
    • GET /auth/fflogs/callback — receives code, exchanges for tokens, persists, redirects to /#fflogs-connected.
    • GET /api/fflogs-auth/status — returns connection state (drives UI).
    • DELETE /api/fflogs-auth/connection — disconnect (deletes the row).
  • React UI — new FFLogsAuthStatus.jsx component mounted in the header: shows "FFLogs Gold: Connect" (link to /auth/fflogs/login) when disconnected, "FFLogs Gold ✓" with a disconnect option when connected. Auto-clears the #fflogs-connected hash after return.
  • New config: FFLOGS_REDIRECT_URI env var (default http://127.0.0.1:8800/auth/fflogs/callback). Must match what's registered on the FFLogs OAuth client config page.
  • 13 new unit tests in tests/test_fflogs_user_oauth.py covering: authorize URL shape, state randomness, code exchange + token persistence, malformed responses, refresh-token usage, refresh on expired token, archived-error subclass detection, archive-retry fallback to /user, propagation when no user auth, has_user_auth and status payloads. Test isolation via httpx.MockTransport. 308 tests total (295 → 308).

Live AC — re-backfill of TOP + DSR after user connected

  • Pre-OAuth (v1.1.1 deep paginated backfill on TOP + DSR): TOP 4 kills_w_events, DSR 1.
  • Post-OAuth re-run (same --encounters 1068 1065 --reports-per-encounter 25 --events-top-n 15): TOP 4 → 15, DSR 1 → 15. Zero errors in the run log. +11 archived reports retrieved for TOP (+199k events), +14 for DSR (+260k events). Total runtime 153s.
  • Both ultimates now have enough kill-with-events coverage (≥3) for T-104 cross-pull consensus → fight_model for TOP and DSR is buildable.

Operator setup (one-time, you-side, not code)

  1. Open https://www.fflogs.com/api/clients/ → edit existing API client.
  2. Add to "Redirect URLs": http://127.0.0.1:8800/auth/fflogs/callback. Save.
  3. In the dashboard header, click "FFLogs Gold: Connect" → Approve on FFLogs → redirected back, status shows "FFLogs Gold ✓".
  4. Run any backfill / Poll-now / watched-report poll as normal; archived/private reports automatically retry via /user.