docs(03-01): complete Module SDK + Registry plan summary
- Module SDK package with versioned interface contract - Database-driven module registry with per-tenant activation - ModuleGuard for tenant-scoped module access control
This commit is contained in:
@@ -0,0 +1,117 @@
|
|||||||
|
---
|
||||||
|
phase: 03-module-system-domaincheck
|
||||||
|
plan: 01
|
||||||
|
subsystem: module-registry
|
||||||
|
tags: [module-sdk, prisma, nestjs, multi-tenant, guard]
|
||||||
|
dependency_graph:
|
||||||
|
requires: [prisma-service, tenant-middleware, jwt-auth-guard, roles-guard]
|
||||||
|
provides: [module-sdk-types, module-registry-service, module-guard, module-registry-controller]
|
||||||
|
affects: [app-module]
|
||||||
|
tech_stack:
|
||||||
|
added: ["@tessera/module-sdk"]
|
||||||
|
patterns: [upsert-activation, tenant-scoped-guard, decorator-composition]
|
||||||
|
key_files:
|
||||||
|
created:
|
||||||
|
- packages/module-sdk/package.json
|
||||||
|
- packages/module-sdk/tsconfig.json
|
||||||
|
- packages/module-sdk/src/types.ts
|
||||||
|
- packages/module-sdk/src/index.ts
|
||||||
|
- apps/api/prisma/migrations/20260619103242_add_module_registry/migration.sql
|
||||||
|
- apps/api/src/module-registry/dto/activate-module.dto.ts
|
||||||
|
- apps/api/src/module-registry/module-registry.service.ts
|
||||||
|
- apps/api/src/module-registry/module-registry.controller.ts
|
||||||
|
- apps/api/src/module-registry/module.guard.ts
|
||||||
|
- apps/api/src/module-registry/module-registry.module.ts
|
||||||
|
modified:
|
||||||
|
- apps/api/prisma/schema.prisma
|
||||||
|
- apps/api/src/app.module.ts
|
||||||
|
- pnpm-lock.yaml
|
||||||
|
decisions:
|
||||||
|
- "Framework-agnostic ComponentType in SDK instead of React dependency to keep package backend-compatible"
|
||||||
|
- "Soft-delete activation records (isActive=false) rather than row deletion for audit trail"
|
||||||
|
- "ModuleGuard uses slug-based lookup via @UseModule() decorator for downstream modules"
|
||||||
|
metrics:
|
||||||
|
duration: 7 min
|
||||||
|
completed: 2026-06-19T10:37:24Z
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 03 Plan 01: Module SDK + Registry Service Summary
|
||||||
|
|
||||||
|
Module SDK package with versioned interface contract and database-driven module registry with per-tenant activation/deactivation via REST API and ModuleGuard for access control.
|
||||||
|
|
||||||
|
## What Was Built
|
||||||
|
|
||||||
|
### Task 1: Module SDK Package + Prisma Schema (8c24c1e)
|
||||||
|
|
||||||
|
**@tessera/module-sdk package** (`packages/module-sdk/`):
|
||||||
|
- `TesseraModule` interface: full module definition with id, name, version, slug, category, i18n descriptions, icon, routes, lazy-loaded components
|
||||||
|
- `ModuleRoute` interface: HTTP method, path, handler for module API routes
|
||||||
|
- `ModuleManifest` type: backend-only metadata (omits component/cardComponent)
|
||||||
|
- `ModuleCategory` type: string literal union for module grouping
|
||||||
|
- `ComponentType`: framework-agnostic component type for lazy-loaded UIs
|
||||||
|
|
||||||
|
**Prisma schema extensions:**
|
||||||
|
- `Module` model: id (UUID), slug (unique), name, version, category, description (JSON), icon, isSystem flag, timestamps
|
||||||
|
- `TenantModuleActivation` model: tenant-module join with isActive soft-delete, unique constraint on [tenantId, moduleId], index on tenantId
|
||||||
|
- Migration `20260619103242_add_module_registry` applied successfully
|
||||||
|
|
||||||
|
### Task 2: ModuleRegistry NestJS Module (fa15d35)
|
||||||
|
|
||||||
|
**ModuleRegistryService** — full CRUD + tenant activation:
|
||||||
|
- `findAll()`: list all registered modules
|
||||||
|
- `findBySlug(slug)`: lookup by slug
|
||||||
|
- `findActiveForTenant(tenantId)`: active modules for a tenant
|
||||||
|
- `activateForTenant(tenantId, moduleId)`: upsert activation record
|
||||||
|
- `deactivateForTenant(tenantId, moduleId)`: soft-delete (isActive=false)
|
||||||
|
- `isModuleActive(tenantId, moduleSlug)`: boolean check for guard
|
||||||
|
- `seedModule(manifest)`: upsert module by slug for startup seeding
|
||||||
|
|
||||||
|
**ModuleRegistryController** — REST endpoints:
|
||||||
|
- `GET /modules` — list all modules (any authenticated user)
|
||||||
|
- `GET /modules/active` — active modules for current tenant
|
||||||
|
- `POST /modules/:moduleId/activate` — ADMIN/SUPER_ADMIN only
|
||||||
|
- `POST /modules/:moduleId/deactivate` — ADMIN/SUPER_ADMIN only
|
||||||
|
|
||||||
|
**ModuleGuard + @UseModule() decorator:**
|
||||||
|
- CanActivate guard checking tenant module activation via slug
|
||||||
|
- Composed decorator: `@UseModule('domaincheck')` applies metadata + guard
|
||||||
|
- TenantId sourced from JWT/middleware (not user input) per T-03-04
|
||||||
|
|
||||||
|
**ActivateModuleDto** with `@IsUUID()` validation per T-03-02.
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
### Auto-fixed Issues
|
||||||
|
|
||||||
|
**1. [Rule 1 - Bug] React.ComponentType reference in SDK types**
|
||||||
|
- **Found during:** Task 1
|
||||||
|
- **Issue:** `types.ts` referenced `React.ComponentType` but the SDK package has no React dependency, causing TypeScript errors
|
||||||
|
- **Fix:** Defined a framework-agnostic `ComponentType` within the SDK instead of depending on React types
|
||||||
|
- **Files modified:** packages/module-sdk/src/types.ts
|
||||||
|
|
||||||
|
**2. [Rule 1 - Bug] DTO definite assignment in strict mode**
|
||||||
|
- **Found during:** Task 2
|
||||||
|
- **Issue:** `ActivateModuleDto.moduleId` property lacked definite assignment assertion, causing TS2564 with strict mode
|
||||||
|
- **Fix:** Added `!` definite assignment assertion (`moduleId!: string`) consistent with class-validator pattern
|
||||||
|
- **Files modified:** apps/api/src/module-registry/dto/activate-module.dto.ts
|
||||||
|
|
||||||
|
## Threat Mitigations Applied
|
||||||
|
|
||||||
|
| Threat ID | Mitigation | Implementation |
|
||||||
|
|-----------|------------|----------------|
|
||||||
|
| T-03-01 | @Roles(ADMIN, SUPER_ADMIN) on activate/deactivate | module-registry.controller.ts |
|
||||||
|
| T-03-02 | @IsUUID validator + Prisma parameterized queries | activate-module.dto.ts, module-registry.service.ts |
|
||||||
|
| T-03-03 | Module catalog accessible to all authenticated users | GET /modules has no role restriction |
|
||||||
|
| T-03-04 | tenantId from request (JWT/middleware), not user input | module.guard.ts uses req.tenantId |
|
||||||
|
|
||||||
|
## Verification Results
|
||||||
|
|
||||||
|
- API build: PASS
|
||||||
|
- Prisma migration status: up to date (3 migrations)
|
||||||
|
- SDK type-check: PASS
|
||||||
|
- Required exports present: TesseraModule, ModuleRoute, ModuleManifest, ModuleCategory
|
||||||
|
- No stubs or placeholders found
|
||||||
|
|
||||||
|
## Self-Check: PASSED
|
||||||
|
|
||||||
|
All 10 created files verified on disk. Both task commits (8c24c1e, fa15d35) verified in git log.
|
||||||
Reference in New Issue
Block a user