import { appendFile } from 'fs/promises';
import { getConfig, getOperatorInfo } from './config.js';
import { logger } from './logger.js';
type RiskLevel = 'high' | 'low';
type OperationType = 'plan' | 'execute' | 'query' | 'error';
interface AuditEntry {
correlationId: string;
timestamp: string;
operatorEmail?: string;
operatorName?: string;
operationType: OperationType;
command: string;
riskLevel: RiskLevel;
result: 'success' | 'failure' | 'pending';
metadata?: Record<string, unknown>;
errorDetails?: string;
}
const MAX_ENTRIES = 100;
const recentEntries: AuditEntry[] = [];
export function generateCorrelationId(): string {
return `az-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
}
export async function logAudit(entry: Omit<AuditEntry, 'timestamp' | 'operatorEmail' | 'operatorName'>): Promise<void> {
const config = getConfig();
const operator = getOperatorInfo();
const full: AuditEntry = {
...entry,
timestamp: new Date().toISOString(),
operatorEmail: operator.email,
operatorName: operator.name,
};
recentEntries.push(full);
if (recentEntries.length > MAX_ENTRIES) recentEntries.shift();
logger.info('Audit entry', {
correlationId: full.correlationId,
operationType: full.operationType,
command: full.command,
riskLevel: full.riskLevel,
result: full.result,
operatorEmail: full.operatorEmail,
});
if (config.enableAuditLog) {
try {
await appendFile(config.auditLogPath, JSON.stringify(full) + '\n', 'utf-8');
} catch (err) {
logger.error('Audit log write failed', err instanceof Error ? err : new Error(String(err)));
}
}
}
export function getRecentAuditEntries(email?: string, limit = 20): AuditEntry[] {
let entries = [...recentEntries].reverse();
if (email) entries = entries.filter(e => e.operatorEmail === email);
return entries.slice(0, limit);
}
export function createAuditContext(
command: string,
riskLevel: RiskLevel,
operationType: 'plan' | 'execute' | 'query'
): { correlationId: string; logSuccess: () => Promise<void>; logFailure: (error: string) => Promise<void> } {
const correlationId = generateCorrelationId();
return {
correlationId,
logSuccess: () => logAudit({ correlationId, operationType, command, riskLevel, result: 'success' }),
logFailure: (errorDetails: string) => logAudit({ correlationId, operationType, command, riskLevel, result: 'failure', errorDetails }),
};
}