100 lines
6.0 KiB
Markdown
100 lines
6.0 KiB
Markdown
# admin — platform config, holidays, audit, support alerts
|
|
|
|
> Client seam `client/src/services/admin/` · `USE_ADMIN_MOCK = true` (**mock is primary**) · 14 server ops
|
|
> Last verified: 2026-07-30 against commit `d3ec723` and swagger.v1.json (2026-07-29).
|
|
|
|
The ops console's cross-cutting reads and writes. Domain-specific admin surfaces live with their domain —
|
|
verification admin in [verification.md](verification.md), refunds in [refunds.md](refunds.md), payouts in
|
|
[payouts.md](payouts.md), catalog in [catalog.md](catalog.md), geo in [geography.md](geography.md),
|
|
partner centers in [partner-center.md](partner-center.md), the ticket queue in [tickets.md](tickets.md).
|
|
|
|
Every endpoint here is `[Authorize(ConstantPolicies.DynamicPermission)]` + `sensitive` rate limit
|
|
(20/min), **except** the three `platform_config`/`holidays`/`audit`/`support_alerts` controllers, which
|
|
carry the dynamic-permission policy without the sensitive limit — they fall to the 100/min global limiter.
|
|
|
|
## Endpoints
|
|
|
|
| Method | Path | Verdict |
|
|
| --- | --- | --- |
|
|
| GET | `/api/v1/platform_config/get_platform_configs` | wired · paginated |
|
|
| POST | `/api/v1/platform_config/update_platform_config` | wired |
|
|
| GET | `/api/v1/platform_config/get_config_change_history` | wired · paginated |
|
|
| GET | `/api/v1/holidays/get_holidays` | wired · paginated |
|
|
| POST | `/api/v1/holidays/upsert_holiday` | wired |
|
|
| POST | `/api/v1/holidays/delete_holiday` | **unwired** — no real client caller; the console offers no delete |
|
|
| GET | `/api/v1/audit/get_audit_trail` | wired · paginated |
|
|
| GET | `/api/v1/support_alerts/get_support_alerts` | wired · paginated |
|
|
| POST | `/api/v1/support_alerts/assign_support_alert` | wired |
|
|
| POST | `/api/v1/support_alerts/resolve_support_alert` | wired |
|
|
| GET | `/api/v1/admin_cancellation_policies/list` | **unwired** — the tier table is read through [refunds.md](refunds.md)'s policy preview instead |
|
|
| POST | `/api/v1/admin_cancellation_policies/upsert` | **unwired** — no console screen edits tiers |
|
|
| POST | `/api/v1/admin_search/rebuild_index` | **unwired** — an ops one-shot, no UI |
|
|
| POST | `/api/v1/admin_booking_requests/expire` | **unwired** — an ops one-shot; the scheduler does this unattended |
|
|
|
|
### Phantom — 5
|
|
|
|
The client's real `clientApi.ts` calls five routes the server does not expose. Both groups are
|
|
**deliberately written real-shaped** so flipping the seam is one line once they ship.
|
|
|
|
| Client call | REQ | Note |
|
|
| --- | --- | --- |
|
|
| `GET /api/v1/admin_roles/list_roles` | REQ-031 | **Deferred** in refinement-phase-3. The admin sub-role vocabulary and phone-OTP admins are seeded, not managed |
|
|
| `POST /api/v1/admin_roles/grant_role` | REQ-031 | |
|
|
| `POST /api/v1/admin_roles/revoke_role` | REQ-031 | |
|
|
| `GET /api/v1/admin_users/search` | **REQ-061 — never filed** | Backs `UserPicker`/`NursePicker` |
|
|
| `POST /api/v1/admin_users/lookup` | **REQ-061 — never filed** | Batch id→label resolve for `AuditLogRow` |
|
|
|
|
> **REQ-061 does not exist in the ledger.** `ui-phase-11-report.md` records "REQ-061…064 appended", but
|
|
> only 062/063/064 were. Ten live client files cite REQ-061 for the admin user directory. Phase 4 must
|
|
> file it rather than assume it is tracked.
|
|
|
|
## Two live drifts
|
|
|
|
**1. The client sends `page_size`; these endpoints declare `PageSize`.** `admin/apis/clientApi.ts`'s
|
|
`pageQuery()` builds `page` + `page_size` "per b1 api-conventions". Model binding is case-*insensitive*,
|
|
not separator-insensitive, so `page_size` does **not** bind to `PageSize` — every admin list would
|
|
silently fall back to the server's default page size. Invisible today because the mock is primary; it
|
|
becomes a real defect the moment `USE_ADMIN_MOCK` flips. See
|
|
[../api-contract.md](../api-contract.md#pagination).
|
|
|
|
**2. `updatedAt`/`updatedBy` and the audit filters *are* on the wire.** REQ-029 (config audit fields) and
|
|
REQ-030 (`actorId`/`action`/`from`/`to` filters on `audit/get_audit_trail`) were both **delivered** in
|
|
refinement-phase-3. `admin/constants.ts` still gives them as reasons the mock is primary. The only
|
|
remaining reason is REQ-031 + REQ-061.
|
|
|
|
## Shape rules the JSON does not express
|
|
|
|
- **Platform config is rows read at compute time**, never hardcoded, and **a rate change is never
|
|
retroactive** — the effective rate is snapshotted onto the row when the amount is computed.
|
|
- `platform_fee_rate` and `vat_rate` are **rates in `[0, 1)`** — the console validates the closed-open
|
|
interval before writing (`RATE_CONFIG_KEYS` in `admin/constants.ts`). The canonical values are
|
|
`0.15` fee / `0.10` VAT (refinement-phase-3).
|
|
- The audit trail's `changedFieldsJson` is a **string containing JSON**, not an object:
|
|
`{"Field": {"old": …, "new": …}}`. The client parses it defensively and yields `null` on malformed input.
|
|
- `POST update_platform_config` and the holiday/alert writes go through self-committing facades that call
|
|
`SaveChanges` on the shared scoped context — they run **after** the handler's own `CommitAsync`.
|
|
- Holidays drive **payout date shifting**: the server resolves a bank-closure-safe payout date from this
|
|
calendar and the client never computes one.
|
|
|
|
## Enums
|
|
|
|
| Vocabulary | Values |
|
|
| --- | --- |
|
|
| `ConfigDataType` | `string` `int` `decimal` `bool` `json` |
|
|
| `AuditAction` | `created` `updated` `deleted` |
|
|
| `HolidayType` | `official` `religious` `national` |
|
|
| `SupportAlertType` | `low_rating` `evv_no_show` `evv_location_mismatch` `verification_expired` `shared_sim` `payment_anomaly` `fraud_signal` `nurse_clawback` `emergency` |
|
|
| `SupportAlertSeverity` | `low` `medium` `high` |
|
|
| `SupportAlertStatus` | `open` `assigned` `resolved` |
|
|
| `AdminRole` *(phantom surface)* | `super_admin` `admin` `support` `finance` `moderation` |
|
|
| `DirectoryUserRole` *(phantom surface)* | `customer` `nurse` `admin` `partner` |
|
|
|
|
The last two describe the REQ-031/REQ-061 shapes and are **not on the wire**.
|
|
|
|
## Open REQs
|
|
|
|
| REQ | Status | Effect |
|
|
| --- | --- | --- |
|
|
| REQ-031 | deferred | No RBAC console. Admin roles are seeded |
|
|
| **REQ-061** | **never filed** | No admin user directory. `AuditLogRow` shows `#id` instead of a name |
|