Commit Graph

4 Commits (main)

Author SHA1 Message Date
sheheryarsoomro12 2b8923f898 new 2026-08-20 11:46:42 +05:00
sheheryarsoomro12 ec7290cacd new 2026-08-20 11:09:20 +05:00
bahawal.baloch b7cc1bac71 Add hosted multi-user mode: accounts, MySQL, Docker, brand identity
Converts the single-user localhost tool into a hosted application while
leaving local mode intact.

The pipeline moves out of serve.py into app/services/{analysis,exporters}.py,
which import only the standard library and ppcbudget. serve.py becomes a thin
shim over them, so it still runs offline with no dependency beyond openpyxl and
there is one copy of the analysis rather than two. ppcbudget and run_report.py
are otherwise untouched.

app/ is a FastAPI application serving the same dashboard behind sign-in:

- Open signup with email confirmation, forgot/reset, argon2id hashing, and
  opaque DB-backed session cookies (HttpOnly, SameSite=Lax, Secure derived from
  APP_BASE_URL so TLS is later a config change, not a rewrite).
- Signup and forgot-password answer identically whether or not an address
  exists; login gives one generic message for unknown, wrong, locked and
  disabled alike. Email tokens are HMAC'd at rest and spent by a single atomic
  UPDATE, and /verify and /reset are inert pages that POST the token -- a mail
  scanner following the link cannot burn it.
- Routes are def, not async def: the pipeline is CPU-bound and would otherwise
  block the event loop. A semaphore bounds concurrent analyses.
- The process-wide SESSION global becomes a workspace per account, with
  per-workspace locks, size caps and idle eviction. The old code held one lock
  for the whole analysis; per-user locks fix that by construction.
- Accounts live in three oob_-prefixed tables. The connection URL is built with
  URL.create, since the password contains ? and # and a hand-built DSN
  truncates it there.
- Minimal /admin page for enabling, disabling and signing accounts out.

Hardening: server-side upload extension and size limits, an Origin guard on
writes, correlation-id 500s instead of echoed exceptions, and a CSP. Every page
script is external because script-src 'self' blocks inline blocks.

Restyled to the Utopia Brands guidelines. Brand swatches sit verbatim in the
--u-* properties and everything derives from them, including the Excel report.
--red and --amber are deliberately not from the guide: it covers identity, not
function, and has no warning colour, but this dashboard exists to show
campaigns going dark. The four data colours were checked for colour-vision
separation across every pair on both surfaces, and the day heatmap is now one
hue getting darker rather than a rainbow.

The claim that nothing is uploaded anywhere stays true only for local mode, so
the dashboard copy and the README now say so.

Tests: tests/test_auth_smoke.py covers the account lifecycle plus upload,
analyse and export against SQLite with mail captured, so it needs no MySQL and
no network. tests/synthetic.py builds a small stand-in export.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 13:42:00 +05:00
sheheryarsoomro12 54d41b12a4 Initial commit: PPC out-of-budget analyzer and dashboard.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 22:32:35 +05:00