Skip to main content

Admin & Internal Tooling

What it is​

Three modules serving three different audiences: admin (the admin dashboard's general-purpose API — user lookups, quickstats, support), tooling (one-off operational actions an admin might run — reset a PIN, reprocess a stuck payment — described by a self-service metadata registry rather than hardcoded dashboard UI), and internal (system-to-system endpoints triggered by Cloud Scheduler/Cloud Tasks/Pub/Sub, never called by a human).

Core concepts​

  • The tooling registry is metadata-driven, not a generic dispatcher. TOOLING_REGISTRY (tooling.registry.ts) is a static array of ~60 ToolInfo entries — each describing a real endpoint's method, endpoint path, form fields (with type, validation, help text), category, and a destructive flag. The admin dashboard reads this via GET /tooling/registry to render a form per tool without per-tool custom frontend code — but it still calls each tool's real endpoint directly; the registry only describes the shape, it doesn't route the call.
  • destructive: true is advisory, not enforced — it's metadata for the dashboard to show a confirmation prompt (e.g. before DELETE /tooling/gift-purchase/:id or a bulk payment reprocess). The backend doesn't re-check this flag; if you add a new destructive tool, you're relying on the dashboard reading the registry correctly, not a server-side safety net.
  • All three modules are separately gated: admin/tooling require adminMiddleware (a human admin's JWT with the admin role); internal endpoints have no user-auth middleware at all — they're protected by network/header-level trust (internal API tokens, Cloud Tasks headers) since there's no human session to check.

How it works​

Tooling registry-described actions​

GET /tooling/registry (all tools), GET /tooling/registry/by-category, GET /tooling/registry/:toolId — dashboard discovery endpoints. The actual tools span categories like users (reset PIN, wallet lookups), gift-purchases (create/update/delete/mark-paid), payment-tracking (reprocess-confirmed, retry-wallet-transfer — manual recovery for stuck payments, complementing the automatic reconciliation cron covered in Payments & Payouts), and one-off fixes (backfill-partybanks, reverse-wallet-transfers).

Admin dashboard API​

Broader, more conventional admin surface: GET /admin/all-users, GET /admin/users/:id, .../transactions, .../events, .../activities, .../quickstats for user-level support/investigation; GET /admin/gift-store/quickstats, .../drink-store/quickstats for platform-level dashboards; support-category management; ban-list operations (ban-list.service.ts — see the double ban-check in the Auth guide).

Admin Actions (GET /admin/actions, .../by-category, .../:actionType) is the same self-describing-registry pattern as the tooling registry above, applied to a smaller set of admin overrides — configuration lives in src/shared/data/admin-application-forms.data.ts. The two registries are separate systems that happen to share a design, not one shared implementation; don't assume adding to one exposes in the other. Current actions: Manual KYC Update (POST /admin/kyc/manual-verify) and Manual BVN Update (POST /admin/bvn/manual-verify) let an admin force-complete a stuck verification when the automated IdentityPass/AWS Rekognition flow fails or a user can't complete it themselves — this bypasses the normal verification pipeline entirely, sets every KYC flag to verified in one transaction, clears any soft-delete on the KYC record, and fires the same push+email notifications a real verification would. Resend Guest RSVP (POST /event/:eventId/guests/resend-rsvp) is also exposed here for support use, distinct from the guest-facing version (see RSVP & Guests).

App Config (GET /app/config, GET /app/config/:flagName, under internal) is a general-purpose feature-flag system, resolved in priority order: Firebase Realtime Database (appConfig/{flagName}, editable without a deploy) → environment variable fallback → hardcoded default. Flags are cached for 5 minutes. Only whitelisted flags are exposed through the API — a flag can exist in code and gate server-side behavior without ever being exposed to clients. This is the same Firebase-config pattern used for Paystack's platform-charge percentage (Payments & Payouts) and broadcast per-channel pricing (Broadcast) — a recurring convention for "change this in production without a deploy," not unique to any one feature.

Internal (system-to-system)​

Not admin-facing at all — these are the actual HTTP targets Cloud Scheduler/Cloud Tasks/Pub/Sub workers hit: RSVP batch processing (rsvp/process-batch, .../process-all-batches), reminder processing (events/process-reminder, .../schedule-pending-reminders — see Events & Event Types), broadcast delivery (broadcast/deliver, .../resend-failed/:broadcastId — see Broadcast), the weekly digest trigger, memory-sharing hourly processing, and infra health checks (database/health-check, database/pool-metrics, debug/cloud-tasks).

Key business rules & edge cases​

  • internal endpoints trust the network, not a user — don't add a new one without confirming it has some form of caller verification (internal token header, Cloud Tasks signature); there's no authMiddleware fallback protecting these by default.
  • Adding a new tooling action means registering it in TOOLING_REGISTRY and wiring the real route — the registry entry alone does nothing; forgetting the registry entry means the tool works but is invisible to the dashboard.
  • Several tooling actions are manual recovery paths for the same failure modes automatic crons already handle (e.g. retry-wallet-transfer alongside the payments reconciliation cron) — check whether the automated path already resolves an issue before reaching for the manual tool.

Tooling: GET /v1/tooling/registry, PUT /v1/tooling/reset-pin/{userId}, POST /v1/tooling/payment-tracking/reprocess-confirmed.

Admin: GET /v1/admin/all-users, GET /v1/admin/users/{id}, GET /v1/admin/actions, POST /v1/admin/kyc/manual-verify, POST /v1/admin/bvn/manual-verify.

Internal: POST /v1/internal/rsvp/process-batch, GET /v1/internal/health, GET /v1/internal/app/config, GET /v1/internal/app/config/{flagName}.

None are in the generated OpenAPI spec.

  • src/modules/tooling/tooling.registry.ts — the ToolInfo/TOOLING_REGISTRY metadata
  • src/modules/tooling/tooling.routes.ts, tooling.controller.ts
  • src/modules/admin/routes.ts, controller.ts, service.ts, ban-list.service.ts
  • src/shared/data/admin-application-forms.data.ts — Admin Actions registry
  • src/shared/utils/feature-flags.util.ts — App Config priority resolution
  • src/modules/internal/internal.routes.ts, internal.controller.ts