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

106 lines
3.9 KiB
Markdown

# OpenSpec Change: enterprise-api-plugin-admin
- **Change ID**: enterprise-api-plugin-admin
- **Package**: `@suaveplan/api-plugin-admin`
- **Category**: api/auth
- **Target path**: `api/auth/api-plugin-admin`
- **Status**: PROPOSED
- **Date**: 2026-04-24
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/api-plugin-admin.md`
---
## Summary
Deliver `@suaveplan/api-plugin-admin` on the genesis branch from scratch at enterprise quality. The plugin mounts an admin-panel API under five route groups (`/admin/users`, `/admin/sites`, `/admin/stats`, `/admin/posts`, `/admin/audit`) behind `requireAuth()` + `requireRole("ADMIN")` from `@suaveplan/api-plugin-auth`. Backing `AdminService` owns user moderation (suspend/ban/promote/demote), post moderation, site metadata, stats, and audit queries against SurrealDB. Domain events (`admin:action:performed`) emitted via proper `AdminEventMap` declaration — no method rebinding, no `export * from "@suaveplan/api-schemas"`, no `@ts-expect-error`.
---
## Current-state audit (genesis)
Package does not exist on genesis — fresh build. Reference: `/mnt/s/web/suaveplan/enterprise/api-plugin-admin.md`.
---
## Available workspace deps on genesis
| Package | Role |
|---|---|
| `@suaveplan/api` (`workspace:^`) | `definePlugin` |
| `@suaveplan/api-shared` (`workspace:^`) | Response helpers, middleware |
| `@suaveplan/api-plugin-auth` (`workspace:^`) | `requireAuth`, `requireRole` |
| `@suaveplan/api-plugin-database` (`workspace:^`) | SurrealDB access |
| `@suaveplan/api-schemas` (`workspace:^`) | Imported types (never re-exported) |
| `@suaveplan/audit-logging` (`workspace:^`) | Audit-log queries + writes |
| `@suaveplan/events` (`workspace:^`) | Typed event bus |
| `@suaveplan/logger` (`workspace:^`) | Structured logging |
| `@suaveplan/rate-limiter` (`workspace:^`) | Rate limits on destructive actions |
| `@suaveplan/error` (`workspace:^`) | `AdminError` hierarchy |
| `hono` + `@hono/zod-openapi` (`catalog:server`) | Routes |
| `zod` (`catalog:forms`) | Schemas |
| `@opentelemetry/api` (`catalog:observability`) | OTel |
---
## Public API surface
```ts
function createAdminPlugin(config?: AdminConfig): PluginDescriptor<AdminConfig>
class AdminService {
suspendUser(userId: string, reason: string): Promise<void>
banUser(userId: string, reason: string): Promise<void>
promoteUser(userId: string, role: string): Promise<void>
demoteUser(userId: string, role: string): Promise<void>
approvePost(postId: string): Promise<void>
removePost(postId: string, reason: string): Promise<void>
getSiteMetadata(siteId: string): Promise<SiteMetadata>
getStats(): Promise<AdminStats>
queryAuditLog(query: AuditQuery): Promise<AuditEntry[]>
}
interface AdminConfig { basePath?: string; requireRole?: string }
interface AdminActionPerformedEvent { action: string; targetId: string; performedBy: string; reason?: string }
```
### Error codes
```ts
type AdminErrorCode =
| "ADMIN_USER_NOT_FOUND"
| "ADMIN_ACTION_FORBIDDEN"
| "ADMIN_INVALID_ROLE"
| "ADMIN_SITE_NOT_FOUND"
```
---
## Genesis additions vs main-branch
1. **Proper `AdminEventMap`** declared in `src/events.ts` (module augmentation) — no `@ts-expect-error`, no method rebinding.
2. **Remove `export * from "@suaveplan/api-schemas"`** — import specific types only.
3. **Rate limiting** on all destructive admin endpoints.
4. **Integration tests** against real SurrealDB.
5. **OTel spans** on every `AdminService` method.
---
## File layout
```text
api/auth/api-plugin-admin/
package.json tsconfig.json vitest.config.ts
surql/ (admin tables)
src/
index.ts events.ts
types/ errors/
services/admin/
routes/users/ routes/sites/ routes/stats/ routes/posts/ routes/audit/
middleware/
tests/integration.test.ts
```
---
## Impact
Prerequisites: `@suaveplan/api`, `@suaveplan/api-shared`, `@suaveplan/api-plugin-auth`, `@suaveplan/api-plugin-database`, `@suaveplan/audit-logging`, `@suaveplan/events`.