Files
2026-05-27 23:29:53 +01:00

4.2 KiB

Tasks: enterprise-api-plugin-admin

Change ID: enterprise-api-plugin-admin Package: @suaveplan/api-plugin-admin Target path: api/auth/api-plugin-admin Prerequisite packages on Verdaccio: @suaveplan/api, @suaveplan/api-shared, @suaveplan/api-plugin-auth, @suaveplan/api-plugin-database, @suaveplan/audit-logging, @suaveplan/events, @suaveplan/rate-limiter, @suaveplan/error, @suaveplan/logger


0. Pre-flight (non-negotiable)

  • 0.1 Confirm clean working tree (git status reports nothing) and no in-flight worktrees with uncommitted work (git worktree list then git status in each)
  • 0.2 If any worktrees exist under .claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
  • 0.3 Confirm the package set in MANIFEST.md matches find packages -maxdepth 3 -name package.json count. Drift here means a prior change archived without regenerating the manifest.

1. Scaffold

  • Create api/auth/api-plugin-admin/ with all subdirs.
  • package.json v0.0.0, "sideEffects": ["src/events.ts"], subpath exports.
  • tsconfig.json + vitest.config.ts.
  • Scripts: lint, typecheck, test, test:coverage, test:with-db, build, docs:check.

2. Types and errors

  • src/types/ — AdminConfig, AdminConfigSchema (Zod), SiteMetadata, AdminStats, AuditQuery, AuditEntry.
  • src/errors/admin-errors.ts — 4-code AdminErrorCode union, metadata, registration.
  • admin-errors.test.ts + admin-errors.md.

3. Events

  • src/events.ts — module augmentation declaring AdminEventMap (admin:action:performed) on global event bus. Export AdminActionPerformedEvent interface.
  • No as any casts, no @ts-expect-error.

4. SurrealQL migrations

  • surql/admin_action.surql, surql/admin_ban.surql — DEFINE TABLE + DEFINE FIELD + tenantId indexes.

5. AdminService

  • Implement all methods listed in proposal: suspendUser, banUser, promoteUser, demoteUser, approvePost, removePost, getSiteMetadata, getStats, queryAuditLog.
  • Each state-changing method: emit admin:action:performed via ctx.eventBus.emit(...) (typed, no cast); write audit entry via @suaveplan/audit-logging.
  • OTel span per method.
  • admin.service.test.ts (≤500 lines): each method happy path + not-found throws + forbidden throws.
  • admin.service.md.

6. Routes (each with .test.ts + .md)

  • /admin/users — list, get, suspend, ban, promote, demote (all behind requireAuth() + requireRole("ADMIN")).
  • /admin/sites — list, get, update site metadata.
  • /admin/stats — GET dashboard stats.
  • /admin/posts — list moderation queue, approve, remove.
  • /admin/audit — paginated audit log query.
  • Rate limiting on all destructive endpoints.
  • OpenAPI tags + descriptions on every route.
  • Import specific types from @suaveplan/api-schemas; no export * pass-through.

7. Plugin factory

  • createAdminPlugin(config?): definePlugin with dependencies: ["database", "auth"], migrationsPath: "./surql".
  • onInit: instantiate AdminService, register via ctx.registerService("admin", adminService).
  • onClose: no-op (no listeners to detach for this plugin).
  • onTenantCreate / onTenantDelete: bootstrap/teardown admin namespace in SurrealDB.

8. Integration tests (tests/integration.test.ts)

  • Suspend user → user cannot login → reinstate → login succeeds (against real SurrealDB via test:with-db).
  • Admin action emits event on bus.
  • Non-admin role → 403.

9. Verify and publish

  • bunx turbo lint typecheck test --filter=@suaveplan/api-plugin-admin — green.
  • bunx turbo test:coverage --filter=@suaveplan/api-plugin-admin — 100%.
  • No @ts-expect-error, export * of external deps, method rebinding, or .skip.
  • bun scripts/publish.ts --package @suaveplan/api-plugin-admin.
  • Archive → openspec/archive/2026-04-24-enterprise-api-plugin-admin/.