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
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:
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user