feat(api): master-login stub + WorkspacePrismaPool (Frente E)
- Prisma 7: prisma.config.ts com datasource.url (API correta); schema gerado em CJS - WorkspacePrismaPool: LRU cache (max 10) de PrismaClient por workspace (ADR 0006) PrismaPg adapter + pg.Pool por workspace; getOrCreate/health/onModuleDestroy - JwtAuthGuard: global APP_GUARD, jose HS256, popula CLS com workspace_id/userId/prisma @Public() decorator marca ping/health/dev-auth como rotas abertas - DevAuthController: POST /auth/dev/token — emite JWT dev (404 em produção) - AuthTokenResponseSchema + DevTokenRequestSchema em @sar/api-interface - WorkspacePoolHealthIndicator: health/ready reporta amostra LRU top-3 (nunca O(N)) - .npmrc: hoist @prisma/client-runtime-utils (requerido pelo Prisma 7 isolated mode) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -5,10 +5,13 @@ import { randomUUID } from 'node:crypto';
|
||||
import type { Request, Response } from 'express';
|
||||
import type { WorkspaceClsStore } from './workspace.types';
|
||||
import type { Env } from '../config/env.schema';
|
||||
import { WorkspacePrismaPool } from './workspace-prisma-pool.service';
|
||||
|
||||
// CLS popula contexto por request. Hoje: requestId + DEFAULT_WORKSPACE_ID do env.
|
||||
// Amanhã: workspaceId vem do JWT (PGD-AUTHZ-002); `prisma` é resolvido pelo
|
||||
// WorkspacePrismaPool e injetado via cls.set('prisma', ...) aqui mesmo.
|
||||
// CLS middleware roda ANTES dos guards (ordem NestJS).
|
||||
// Aqui: apenas requestId + workspaceId default.
|
||||
// JwtAuthGuard atualiza workspaceId, userId e prisma após validar o token.
|
||||
// CODING-RULES PGD-DB-009: prisma via cls.get('prisma'), nunca singleton.
|
||||
// CODING-RULES PGD-AUTHZ-002: workspaceId real vem do JWT (guard), não do env.
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -21,28 +24,28 @@ import type { Env } from '../config/env.schema';
|
||||
mount: true,
|
||||
generateId: true,
|
||||
idGenerator: (req: Request) => {
|
||||
// Prioridade: req.id (pino-http já gerou/leu header) > header bruto > novo UUID.
|
||||
const fromPino = (req as Request & { id?: unknown }).id;
|
||||
if (typeof fromPino === 'string' && fromPino.length > 0) return fromPino;
|
||||
const headerVal = req.headers['x-request-id'];
|
||||
return typeof headerVal === 'string' && headerVal.length > 0
|
||||
? headerVal
|
||||
: randomUUID();
|
||||
return typeof headerVal === 'string' && headerVal.length > 0 ? headerVal : randomUUID();
|
||||
},
|
||||
setup: (cls, req: Request, res: Response) => {
|
||||
const store = cls as unknown as {
|
||||
set: <K extends keyof WorkspaceClsStore>(key: K, value: WorkspaceClsStore[K]) => void;
|
||||
getId: () => string;
|
||||
};
|
||||
|
||||
const requestId = store.getId();
|
||||
res.setHeader('x-request-id', requestId);
|
||||
store.set('requestId', requestId);
|
||||
// Fallback para rotas públicas (ping, health). Guard sobrescreve em rotas protegidas.
|
||||
store.set('workspaceId', config.get('DEFAULT_WORKSPACE_ID', { infer: true }));
|
||||
},
|
||||
},
|
||||
}),
|
||||
}),
|
||||
],
|
||||
exports: [ClsModule],
|
||||
providers: [WorkspacePrismaPool],
|
||||
exports: [ClsModule, WorkspacePrismaPool],
|
||||
})
|
||||
export class WorkspaceModule {}
|
||||
|
||||
Reference in New Issue
Block a user