From 1ebfd206ce7e6afadfebe4e6f06df234934e24db Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Sat, 15 Aug 2026 20:13:10 +0400 Subject: [PATCH] docs: platform super-admin Phase 1 design spec Co-Authored-By: Claude Sonnet 5 --- .../2026-08-15-platform-super-admin-design.md | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-15-platform-super-admin-design.md diff --git a/docs/superpowers/specs/2026-08-15-platform-super-admin-design.md b/docs/superpowers/specs/2026-08-15-platform-super-admin-design.md new file mode 100644 index 0000000..4913dbc --- /dev/null +++ b/docs/superpowers/specs/2026-08-15-platform-super-admin-design.md @@ -0,0 +1,137 @@ +# Platform Super-Admin — Phase 1 Design + +**Status:** Approved +**Date:** 2026-08-15 +**Audience:** Internal admin & risk team ("super puper user") + +## Purpose + +A cross-tenant view for internal admin/risk staff: see every project (store/tenant) on the +platform, drill into one, and review its access list, audit log, admin edit history, and +purchase history. Read-only in this phase. + +Editing project data / impersonating a store's admin ("edit all", with a per-change "notify +this store's admin" toggle) is explicitly **out of scope** for this phase — see +[Phase 2](#phase-2-out-of-scope-here) below. Phase 1 exists first because Phase 2's edit and +notify plumbing depends on the tenant-context switch this phase builds. + +## Non-goals (Phase 1) + +- No editing of any tenant's data. +- No impersonation of a store's admin. +- No "notify store admin" mechanism (that's a Phase 2 concern, tied to edit actions that + don't exist yet). +- No real backend — this repo is frontend-only; the backend contract is specified here for + whoever owns that service, not implemented here. + +## Architecture + +- New top-level feature module: `src/app/features/platform-admin/`. +- New route tree `/platform-admin/**`, own shell/layout. **Not** nested under any tenant's + `/admin/**` — a project is not "logged into" the way a store admin is. +- New `platformAdminAuthGuard` (parallel to, but sharing no state with, `adminAuthGuard` in + `core/admin-auth/admin-auth.guard.ts`). +- `PlatformAuthService` — session/login state for the super-admin, backed by a + `PlatformAuthGateway` interface: `login(credentials)`, `logout()`, `session()`. + - `PlatformAuthLocalGateway` — dev-only implementation. Reads the expected credential from + a **git-ignored** local file (`platform-auth.local-secret.ts`, added to `.gitignore`), + never committed, never present in a production build path. + - `PlatformAuthApiGateway` — later swap-in once the backend endpoint exists; same + interface, no caller changes needed. + +## Data model + +```ts +interface PlatformProjectSummary { + id: UUID; + name: string; + slug: string; + host: string; + status: 'active' | 'suspended'; + createdAt: number; + adminCount: number; + lastActivityAt: number | null; +} + +interface PlatformProjectAccessEntry { + userId: UUID; + displayName: string; + telegramUsername: string; + roleId: string; // maps to existing AdminRole / ROLE_PERMISSIONS +} + +type PlatformProjectHistoryEntry = + | { kind: 'access'; tenantId: UUID; actorLabel: string; timestamp: number; summary: string } + | { kind: 'edit'; tenantId: UUID; actorLabel: string; timestamp: number; summary: string } + | { kind: 'purchase'; tenantId: UUID; actorLabel: string; timestamp: number; summary: string }; +``` + +- `PlatformProjectSummary[]` is produced by `PlatformProjectsGateway.list()`, which aggregates + the existing `TenantConfig` fixture list plus derived stats. Mock gateway now; real + aggregation is a backend concern later. +- `PlatformProjectAccessEntry` reuses the existing `AdminRole` / `ROLE_PERMISSIONS` shape from + `core/auth/models/permission.model.ts` — no new role system. +- `PlatformProjectHistoryEntry` is a discriminated union covering all three history types the + user asked for (access/audit, admin edit history, purchase history). Mock gateway simulates + aggregation from existing per-tenant sources (e.g. the pattern in + `AdminDashboardHistoryService`, `admin-transactions`); real aggregation is a backend concern. +- Every super-admin **view** into a project also writes its own `kind: 'access'` entry + (`platform.viewedProject`) — the risk team needs to know who looked at what, not just what + changed. + +## Components / pages + +- `PlatformProjectsListPageComponent` — table of all projects: name, status, admin count, + last activity. Search/filter by status. +- `PlatformProjectDetailPageComponent` — project overview stats, then tabs: + - **Access** — `PlatformProjectAccessEntry[]` for that tenant. + - **Audit Log** — `history` filtered to `kind: 'access'`. + - **Edit History** — `history` filtered to `kind: 'edit'`. + - **Purchase History** — `history` filtered to `kind: 'purchase'`. + - All read-only in this phase. + +## Security + +- `platformAdminAuthGuard` denies unless the session carries `platform.superadmin`. Like the + existing `AdminPermissionsService`, the frontend check is defense-in-depth only — real + enforcement must happen server-side once the backend endpoint exists. This is called out + explicitly so it's never mistaken for the source of truth. +- No credential is ever hardcoded in committed source. Dev-only credential lives in a + git-ignored local file; production auth goes through the real backend endpoint below. +- Session timeout for platform-admin: 15 minutes idle (shorter than regular tenant-admin + sessions — higher-privilege session, smaller blast radius if a session is left open). +- Every super-admin action (including read-only views) is itself audit-logged. +- After implementation, run `/security-audit` on this feature specifically before it ships. + +### Backend contract (for whoever owns that service — not implemented in this repo) + +Add to `BACKEND-API-REFERENCE.md`: + +- `POST /platform-admin/auth` — verifies a hashed credential server-side, returns a session + token scoped to `platform.superadmin`. Never a plaintext credential check in a client-shipped + artifact. +- `GET /platform-admin/projects` — returns `PlatformProjectSummary[]`. +- `GET /platform-admin/projects/:id/history` — returns `PlatformProjectHistoryEntry[]` for + that tenant, paginated. + +## Testing + +- Unit tests: `platformAdminAuthGuard`, `PlatformProjectsGateway` (mock), history-aggregation + mapping logic. +- No E2E in this phase — no real backend to exercise end-to-end yet. + +## Phase 2 (out of scope here) + +A separate spec/plan cycle, once Phase 1 ships: + +- Full edit / impersonation: super-admin acts as a tenant's admin across every existing admin + module (products, orders, categories, settings, etc.), reusing those modules under a + tenant-context switch. +- Per-edit-action **"notify this store's admin about this change"** checkbox, **default + unchecked**. Uses the existing in-app notification pattern (the one behind + `admin-order-watcher.service.ts`'s unread-badge flow) so the affected tenant's admin sees it + in their notification feed. Unchecked-by-default matters: some super-admin edits are + discreet technical fixes where alerting the store admin would be noise or a reputational + concern, not every edit should ping them. +- This phase needs the tenant-context switch and audit-logging plumbing this Phase 1 spec + establishes, which is why it's sequenced after.