fix: Display user email instead of Auth0 UID in audit logs (refs #10)
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 4m40s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 37s
Deploy to Staging / Verify Staging (pull_request) Successful in 6s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 5s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped

- Add userEmail field to AuditLogEntry type in backend and frontend
- Update audit-log repository to LEFT JOIN with user_profiles table
- Update AdminLogsPage to show email with fallback to truncated userId
- Update AdminLogsMobileScreen with same display logic

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Eric Gullickson
2026-01-11 12:30:57 -06:00
parent fbde51b8fd
commit 911b7c0e3a
5 changed files with 38 additions and 20 deletions

View File

@@ -39,37 +39,37 @@ export class AuditLogRepository {
let paramIndex = 1;
if (filters.search) {
conditions.push(`action ILIKE $${paramIndex}`);
conditions.push(`al.action ILIKE $${paramIndex}`);
params.push(`%${this.escapeLikePattern(filters.search)}%`);
paramIndex++;
}
if (filters.category) {
conditions.push(`category = $${paramIndex}`);
conditions.push(`al.category = $${paramIndex}`);
params.push(filters.category);
paramIndex++;
}
if (filters.severity) {
conditions.push(`severity = $${paramIndex}`);
conditions.push(`al.severity = $${paramIndex}`);
params.push(filters.severity);
paramIndex++;
}
if (filters.userId) {
conditions.push(`user_id = $${paramIndex}`);
conditions.push(`al.user_id = $${paramIndex}`);
params.push(filters.userId);
paramIndex++;
}
if (filters.startDate) {
conditions.push(`created_at >= $${paramIndex}`);
conditions.push(`al.created_at >= $${paramIndex}`);
params.push(filters.startDate);
paramIndex++;
}
if (filters.endDate) {
conditions.push(`created_at <= $${paramIndex}`);
conditions.push(`al.created_at <= $${paramIndex}`);
params.push(filters.endDate);
paramIndex++;
}
@@ -86,7 +86,8 @@ export class AuditLogRepository {
const query = `
INSERT INTO audit_logs (category, severity, user_id, action, resource_type, resource_id, details)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING id, category, severity, user_id, action, resource_type, resource_id, details, created_at
RETURNING id, category, severity, user_id, action, resource_type, resource_id, details, created_at,
NULL::text as user_email
`;
try {
@@ -117,14 +118,17 @@ export class AuditLogRepository {
const { whereClause, params, nextParamIndex } = this.buildWhereClause(filters);
// Count query
const countQuery = `SELECT COUNT(*) as total FROM audit_logs ${whereClause}`;
const countQuery = `SELECT COUNT(*) as total FROM audit_logs al ${whereClause}`;
// Data query with pagination
// Data query with pagination - LEFT JOIN to get user email
const dataQuery = `
SELECT id, category, severity, user_id, action, resource_type, resource_id, details, created_at
FROM audit_logs
SELECT al.id, al.category, al.severity, al.user_id, al.action,
al.resource_type, al.resource_id, al.details, al.created_at,
up.email as user_email
FROM audit_logs al
LEFT JOIN user_profiles up ON al.user_id = up.auth0_sub
${whereClause}
ORDER BY created_at DESC
ORDER BY al.created_at DESC
LIMIT $${nextParamIndex} OFFSET $${nextParamIndex + 1}
`;
@@ -156,16 +160,19 @@ export class AuditLogRepository {
const { whereClause, params } = this.buildWhereClause(filters);
// First, count total matching records
const countQuery = `SELECT COUNT(*) as total FROM audit_logs ${whereClause}`;
const countQuery = `SELECT COUNT(*) as total FROM audit_logs al ${whereClause}`;
const countResult = await this.pool.query(countQuery, params);
const totalCount = parseInt(countResult.rows[0].total, 10);
const truncated = totalCount > MAX_EXPORT_RECORDS;
const query = `
SELECT id, category, severity, user_id, action, resource_type, resource_id, details, created_at
FROM audit_logs
SELECT al.id, al.category, al.severity, al.user_id, al.action,
al.resource_type, al.resource_id, al.details, al.created_at,
up.email as user_email
FROM audit_logs al
LEFT JOIN user_profiles up ON al.user_id = up.auth0_sub
${whereClause}
ORDER BY created_at DESC
ORDER BY al.created_at DESC
LIMIT ${MAX_EXPORT_RECORDS}
`;
@@ -222,6 +229,7 @@ export class AuditLogRepository {
category: row.category as AuditLogEntry['category'],
severity: row.severity as AuditLogEntry['severity'],
userId: row.user_id as string | null,
userEmail: (row.user_email as string | null) || null,
action: row.action as string,
resourceType: row.resource_type as string | null,
resourceId: row.resource_id as string | null,

View File

@@ -21,6 +21,7 @@ export interface AuditLogEntry {
category: AuditLogCategory;
severity: AuditLogSeverity;
userId: string | null;
userEmail: string | null;
action: string;
resourceType: string | null;
resourceId: string | null;