SaaS dashboard: clients, ledger, queue, throughput, review verdicts (#9) #22

Merged
pbwraith merged 5 commits from issue-9 into main 2026-08-26 09:19:26 +02:00

5 Commits

Author SHA1 Message Date
reviewer
1523074cbe tests(#9): make the no-second-server guard survive #2 merging
The guard asserted gitea_saas/server.py was absent from the tree. That held
only until #2 (which owns the app factory) merged; afterwards it fired against
#2's own file rather than anything #9 shipped, and was the single failure when
issue-9 was merged onto main.

issue-9 adds exactly two files (gitea_saas/dashboard.py, tests/test_dashboard.py)
and never contained server.py, so the invariant the docstring describes was
always satisfied. It is now checked directly, via AST, so mount_dashboard's
docstring example (app = Flask(__name__)) is not mistaken for a real call.
2026-08-26 02:18:32 -05:00
r
43dca684d0 Merge remote-tracking branch 'origin/main' into HEAD 2026-08-26 02:14:37 -05:00
Claude
f7d0bc5666 fix(#9): bounce 2 — constant-time API-key compare + single header constant
Review finding (2026-08-26): the dashboard was the only auth path in the
service NOT using hmac.compare_digest — plain str != short-circuits on the
first differing byte (prefix timing leak on the shared secret) — and the
header name was a hardcoded literal instead of a named constant.

- import hmac; compare via hmac.compare_digest (503 fail-closed branch
  kept exactly as reviewed)
- API_KEY_HEADER module constant is the single source of truth; when #24
  consolidates auth paths this line becomes an import of the shared
  config constant (config.py lands with #2/#6, both unmerged)
- new test: a wrong key of the SAME length as the configured key must be
  rejected (the length-differing fixture cannot distinguish != from
  compare_digest)
2026-08-25 21:00:34 -05:00
Claude
c1b3105257 fix(#9): bounce — #16 id-key API + mount dashboard onto app factory
Review findings (manual review of !22):
1. Main moved: #16 renamed create_client/get_client records to flat
   id-keyed shape. Tests read the old nested ['client']/['client_id']
   shape (10 collection errors). Fixed to the flat ['id'] record;
   parameter names stay unchanged per review.
2. server.py three-way conflict (#2/#6/#9): issue #9 must NOT define a
   second Flask app. gitea_saas/server.py is dropped from this branch;
   dashboard.py now exposes mount_dashboard(app, core, db_path, api_key)
   which #2's app factory wires in. Endpoint tests build a throwaway app
   the same way (X-API-Key discipline, fail-closed 503, 401) and add a
   mount-contract test asserting no server.py ships with this issue.

Rebased onto current main (bcf03bd). Full gate: 58 passed, diff clean.
2026-08-25 11:38:54 -05:00
Claude
3daac0717c gitea-saas-core#9: SaaS operator dashboard — clients/queue/throughput/review verdicts/ledger flash (HTML page + authed JSON summary) 2026-08-25 11:30:15 -05:00