The CRM portal at crm.series-x.ai. React-free SPA (vanilla JS), Madnion SSO, Supabase backend (Postgres + edge functions).
Browser ── nginx (crm.series-x.ai, TLS)
├── /crm/*.html SPA pages (SSO-gated: auth_request → :8793)
├── /crm/*.js|css assets (public, login page needs them)
├── /auth/* login/callback/logout → serves auth.html (SPA)
├── /auth/session POST: bridge token → crm_sso cookie; DELETE: logout
├── /auth/whoami session profile (profile widget)
└── /crm-api/* → edge function crm-api (Supabase, Madnion JWT)
Two auth layers:
crm_sso HttpOnly cookie, HMAC-signed, 12h TTL. Issued by the
session service (/opt/crm-integrations/madnion-sso.js, port 8793) via POST /auth/session.
The SPA bridges automatically after a successful Madnion sign-in.Authorization: Bearer on every data call. The edge function verifies the token AND CRM-admin
membership on each request.| Page | Route | Data source |
|---|---|---|
| Dashboard | /crm/dashboard.html |
Supabase (cashflow_snapshots, crm_subscriptions) |
| Opportunities | /crm/pipeline.html |
proposals table |
| Ideas canvas | /crm/ideas.html |
mind-maps tables |
| Proposals | /crm/proposals.html |
proposals table |
| Billing | /crm/billing.html |
dataset billing (rebuilt nightly) |
| Cashflow | /crm/cashflow.html |
dataset cashflow + snapshots |
| Subscriptions | /crm/subscriptions.html |
crm_subscriptions + datasets payments-out, wise-deepdive |
| Reports / Updates | datasets reports, updates |
|
| Projects | ClickUp PMS proxy | |
| Knowledge / Meetings | AdvisorPro read integration |
/root/crm-refresh/run_daily.sh): Stripe pull → JSON caches →
build_tables.py (billing HTML) → import-datasets.js (syncs datasets to Supabase) →
persist-supabase.js (cashflow snapshot + subscriptions upsert) → Wise + payments-out.stripe-webhook (payments/subscriptions) triggers the same persist step, so
cancellations/updates land in Supabase immediately.public.crm_datasets (name, payload jsonb, source, imported_at).
Manual resync: node /root/crm-refresh/import-datasets.js.github.com/series-a-uk/series-x-crm (private). Working copy /root/crm-repo.
Deploy pages: rsync crm-pages/ → /var/www/proposals/crm/. Deploy services: copy to
/opt/crm-integrations/, systemctl restart. Tests: node tests/session-bridge.mjs.
Client proposal pages (/roshni/, /richard-bennett/, etc.) stay on classic basic auth
deliberately. Do not convert them to SSO.