import { randomUUID } from 'node:crypto'; import { BadRequestException, Body, Controller, ForbiddenException, Headers, Param, Post } from '@nestjs/common'; import jwt from 'jsonwebtoken'; import { IIOS_EVENTS } from '@insignia/iios-contracts'; import { hmacSign } from '@insignia/iios-adapter-sdk'; import { InboundService } from '../adapters/inbound.service'; import { AdapterRegistry } from '../adapters/adapter.registry'; import { PrismaService } from '../prisma/prisma.service'; import { ActorResolver, type MessagePrincipal } from '../identity/actor.resolver'; import { SessionVerifier } from '../platform/session.verifier'; import { RetentionService } from '../retention/retention.service'; /** * Dev-only helpers (gated by IIOS_DEV_TOKENS=1). Never enable in production. */ @Controller('v1/dev') export class DevController { constructor( private readonly inbound: InboundService, private readonly registry: AdapterRegistry, private readonly prisma: PrismaService, private readonly actors: ActorResolver, private readonly session: SessionVerifier, private readonly retention: RetentionService, ) {} @Post('token') token(@Body() body: { appId: string; userId: string; name?: string; orgId?: string }): { token: string } { this.assertDev(); let secrets: Record; try { secrets = JSON.parse(process.env.APP_SECRETS ?? '{}'); } catch { secrets = {}; } const secret = secrets[body.appId]; if (!secret) throw new BadRequestException(`unknown app: ${body.appId}`); const token = jwt.sign( { sub: body.userId, name: body.name ?? body.userId, appId: body.appId, orgId: body.orgId ?? `org_${body.appId}` }, secret, { algorithm: 'HS256', expiresIn: '2h' }, ); return { token }; } /** Server signs a sample provider payload and feeds it to the inbound pipeline. */ @Post('webhook/:channelType') async injectWebhook(@Param('channelType') channelType: string, @Body() body?: { from?: string; text?: string }) { this.assertDev(); const reg = this.registry.get(channelType); if (!reg) throw new BadRequestException(`unknown channel type: ${channelType}`); const payload = { eventId: `dev-${randomUUID()}`, from: body?.from ?? 'sim@customer', displayName: 'Sim Customer', threadRef: `sim-${randomUUID().slice(0, 8)}`, subject: 'Simulated inbound', text: body?.text ?? 'Hello from a simulated provider', }; const raw = JSON.stringify(payload); return this.inbound.receive(channelType, raw, { 'x-iios-signature': hmacSign(raw, reg.config.secret), 'content-type': 'application/json', }); } /** * Inject a poison chaos event into the outbox. The relay publishes it, the * ChaosConsumer throws on first delivery → it is dead-lettered under the caller's * scope, then `POST /v1/dlq/:id/replay` resolves it. Drives the DLQ smoke. */ @Post('chaos/poison') async poison(@Headers('authorization') auth?: string): Promise<{ eventId: string; scopeId: string }> { this.assertDev(); const principal = this.principal(auth); const scope = await this.actors.resolveScope(principal); // find-or-create so GET /v1/dlq matches const id = `evt_chaos_${randomUUID()}`; const cloudEvent = { specversion: '1.0', id, type: IIOS_EVENTS.devChaos, source: `iios/dev/${principal.appId}`, subject: `chaos/${id}`, time: new Date().toISOString(), datacontenttype: 'application/json', insignia: { scopeSnapshotId: scope.id, idempotencyKey: id, dataClass: 'internal' }, data: { poison: true }, }; await this.prisma.iiosOutboxEvent.create({ data: { aggregateType: 'dev-chaos', aggregateId: id, eventType: IIOS_EVENTS.devChaos, cloudEvent: cloudEvent as unknown as object, partitionKey: `${principal.orgId}:${principal.appId}:chaos`, }, }); return { eventId: id, scopeId: scope.id }; } /** Run the retention sweep on demand (drives the retention smoke). */ @Post('retention/sweep') async retentionSweep() { this.assertDev(); return this.retention.sweep(); } private principal(authorization?: string): MessagePrincipal { const token = (authorization ?? '').replace(/^Bearer\s+/i, ''); if (!token) throw new BadRequestException('Authorization bearer token is required'); return this.session.verify(token); } private assertDev(): void { if (process.env.IIOS_DEV_TOKENS !== '1') throw new ForbiddenException('dev endpoints disabled'); } }