docs: Phase 3 portal — API_REFERENCE, ARCHITECTURE, DEPLOYMENT (email-change config), HANDOFF pass-48 (P3f)
This commit is contained in:
@@ -968,3 +968,8 @@ Outbox + dispatcher; the webhook never sends inline. Pure render + dedupe in `li
|
||||
- Dispatcher `POST /api/cron/notifications` (nodejs, force-dynamic; constant-time `CRON_SECRET` bearer; 503 if unset, 401 on mismatch; `proxy.ts` whitelists `/api/cron`). Drains ≤20 oldest `pending` email rows with `attempts < MAX`, sends with the row id as Resend `Idempotency-Key` (overlap-safe), ~4.5/s spacing. Success → `sent`+`sent_at`; failure → `attempts++`, `pending` if retryable and budget remains else `failed`. Returns `{ scanned, sent, failed, retried }`. Hit once a minute by the droplet crontab.
|
||||
- `getMyNotifications()` — the caller's own last 15 notifications (service client; `notifications` is admin-RLS, mirrors `getMyBilling`). Surfaced read-only on `/me`.
|
||||
- `getMyPayments()` — the caller's own last 50 payments (service client; `payments` SELECT is treasurer-scoped, so the self-view is strictly scoped to the caller's `space_id` + `member_id`, same convention as `getMyBilling`). Surfaced read-only in the `/me` Membership tab.
|
||||
|
||||
### Member self-serve portal (`/me`, product spine Phase 3)
|
||||
`/me` is a 3-tab portal (Profile / Membership / Activity); the server page fetches, a client component renders. Profile editing reuses `updateMyProfile` + `discloseAffiliations` (affiliations re-disclosed only when changed, so the COI timestamp is preserved). Class signups / reservations are cancellable inline via the existing `cancelMySignup` / `cancelReservation` (ownership + space scoping still enforced server-side).
|
||||
- `requestEmailChange({ email })` — member-initiated login-email change. Validates with `emailChangeSchema`, requires an active member, calls `supabase.auth.updateUser({ email }, { emailRedirectTo: {origin}/auth/confirm })`. With "Secure email change" on (recommended), Supabase double-confirms (old + new); the change applies to `auth.users` only after both links. Returns `{ success }` / `{ error }` (error includes "email already in use"). NEVER writes the denormalized email here.
|
||||
- `GET /auth/confirm` — email-change landing. Reads `token_hash` + `type`, calls `verifyOtp({ type: 'email_change', token_hash })`, and on success syncs `space_members.email` (service client, by `user_id`) to the now-authoritative `auth.users` email — idempotent, post-verification only. Whitelisted in `proxy.ts` (exact path; link is clicked with no session). Requires Supabase project config (see DEPLOYMENT).
|
||||
|
||||
Reference in New Issue
Block a user