Implemented audit log system

This commit is contained in:
2026-02-12 14:48:19 -05:00
parent ab9bb99987
commit 34ce7d1e14
5 changed files with 122 additions and 1 deletions

View File

@@ -0,0 +1,50 @@
import pool from "../../db";
import { logger } from "./logger";
export type AuditArea = 'member' | 'calendar' | 'unit' | 'auth' | 'admin' | 'application';
export interface AuditContext {
actorId: number; // The person doing the action (created_by)
targetId?: number; // The ID of the thing being changed (target_id)
}
class AuditLogger {
async record(
area: AuditArea,
action: string,
context: AuditContext,
data: Record<string, any> = {} // Already optional with default {}
) {
const actionType = `${area}.${action}`;
try {
await pool.query(
`INSERT INTO audit_log (action_type, payload, target_id, created_by)
VALUES (?, ?, ?, ?)`, // Fixed: removed extra comma/placeholder
[
actionType,
JSON.stringify(data),
context.targetId || null,
context.actorId,
]
);
} catch (err) {
logger.error('audit', `AUDIT_FAILURE: Failed to log ${actionType}`, { error: err });
}
}
// Making data optional using '?' and default parameter
member(action: 'update_rank' | 'status_change' | 'create', context: AuditContext, data: any = {}) {
return this.record('member', action, context, data);
}
calendar(action: 'event_signup' | 'event_create' | 'attendance', context: AuditContext, data: any = {}) {
return this.record('calendar', action, context, data);
}
application(action: 'created' | 'approved' | 'denied' | 'restarted', context: AuditContext, data: any = {}) {
return this.record('application', action, context, data);
}
}
export const audit = new AuditLogger();