Files
iios/packages/iios-service/src/observability/audit.ts
T
maaz519 6548b40f17 feat(p9): iios_audit_link table + audit binding at decision points
Task O.3: IiosAuditLink (traceId/correlationId/scopeId/actorRefId/action/
resourceType/resourceId) + recordAudit() helper (reads the current trace from ALS,
best-effort). Written at the decision/mutation points — route approve/deny, AI
accept/reject, meeting summarize, outbound send — so the trace appears in DB and a
decision can be replayed / traced across services (mandated iios_audit_link).
Migration audit-link; resetDb truncates it. 2 tests bind approve/accept to a trace.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 01:08:55 +05:30

36 lines
1.1 KiB
TypeScript

import type { PrismaService } from '../prisma/prisma.service';
import { currentTraceId } from './trace-context';
export interface AuditInput {
action: string;
resourceType: string;
resourceId: string;
scopeId?: string;
actorRefId?: string;
correlationId?: string;
}
/**
* Write an audit link (P9): binds an action to its trace + actor + resource so a
* decision can be replayed / a request traced across services. Plain function — reads
* the current request's trace id from AsyncLocalStorage (no DI). Best-effort: never
* throws into the caller's happy path.
*/
export async function recordAudit(prisma: PrismaService, input: AuditInput): Promise<void> {
try {
await prisma.iiosAuditLink.create({
data: {
action: input.action,
resourceType: input.resourceType,
resourceId: input.resourceId,
scopeId: input.scopeId,
actorRefId: input.actorRefId,
traceId: currentTraceId(),
correlationId: input.correlationId ?? currentTraceId(),
},
});
} catch {
/* audit is best-effort; do not break the request */
}
}