Skip to content

Commit 163b35d

Browse files
committed
fix(audit-log): lazily resolve actor name/email when missing
1 parent 3c470ab commit 163b35d

File tree

1 file changed

+53
-34
lines changed

1 file changed

+53
-34
lines changed

apps/sim/lib/audit/log.ts

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { auditLog, db } from '@sim/db'
2+
import { user } from '@sim/db/schema'
23
import { createLogger } from '@sim/logger'
4+
import { eq } from 'drizzle-orm'
35
import { nanoid } from 'nanoid'
46

57
const logger = createLogger('AuditLog')
@@ -185,41 +187,58 @@ interface AuditLogParams {
185187

186188
/**
187189
* Records an audit log entry. Fire-and-forget — never throws or blocks the caller.
190+
* If actorName and actorEmail are both undefined (not provided by the caller),
191+
* resolves them from the user table before inserting.
188192
*/
189193
export function recordAudit(params: AuditLogParams): void {
190-
try {
191-
const ipAddress =
192-
params.request?.headers.get('x-forwarded-for')?.split(',')[0].trim() ??
193-
params.request?.headers.get('x-real-ip') ??
194-
undefined
195-
const userAgent = params.request?.headers.get('user-agent') ?? undefined
196-
197-
db.insert(auditLog)
198-
.values({
199-
id: nanoid(),
200-
workspaceId: params.workspaceId || null,
201-
actorId: params.actorId,
202-
action: params.action,
203-
resourceType: params.resourceType,
204-
resourceId: params.resourceId,
205-
actorName: params.actorName ?? undefined,
206-
actorEmail: params.actorEmail ?? undefined,
207-
resourceName: params.resourceName,
208-
description: params.description,
209-
metadata: params.metadata ?? {},
210-
ipAddress,
211-
userAgent,
212-
})
213-
.then(() => {
214-
logger.debug('Audit log recorded', {
215-
action: params.action,
216-
resourceType: params.resourceType,
217-
})
218-
})
219-
.catch((error) => {
220-
logger.error('Failed to record audit log', { error, action: params.action })
221-
})
222-
} catch (error) {
223-
logger.error('Failed to initiate audit log', { error, action: params.action })
194+
insertAuditLog(params).catch((error) => {
195+
logger.error('Failed to record audit log', { error, action: params.action })
196+
})
197+
}
198+
199+
async function insertAuditLog(params: AuditLogParams): Promise<void> {
200+
const ipAddress =
201+
params.request?.headers.get('x-forwarded-for')?.split(',')[0].trim() ??
202+
params.request?.headers.get('x-real-ip') ??
203+
undefined
204+
const userAgent = params.request?.headers.get('user-agent') ?? undefined
205+
206+
let { actorName, actorEmail } = params
207+
208+
// Only look up if the caller didn't provide either field at all.
209+
// Explicit null (e.g. user with no display name) is respected as-is.
210+
if (actorName === undefined && actorEmail === undefined && params.actorId) {
211+
try {
212+
const [row] = await db
213+
.select({ name: user.name, email: user.email })
214+
.from(user)
215+
.where(eq(user.id, params.actorId))
216+
.limit(1)
217+
actorName = row?.name ?? null
218+
actorEmail = row?.email ?? null
219+
} catch {
220+
// Lookup failure is non-fatal — insert without actor info
221+
}
224222
}
223+
224+
await db.insert(auditLog).values({
225+
id: nanoid(),
226+
workspaceId: params.workspaceId || null,
227+
actorId: params.actorId,
228+
action: params.action,
229+
resourceType: params.resourceType,
230+
resourceId: params.resourceId,
231+
actorName: actorName ?? undefined,
232+
actorEmail: actorEmail ?? undefined,
233+
resourceName: params.resourceName,
234+
description: params.description,
235+
metadata: params.metadata ?? {},
236+
ipAddress,
237+
userAgent,
238+
})
239+
240+
logger.debug('Audit log recorded', {
241+
action: params.action,
242+
resourceType: params.resourceType,
243+
})
225244
}

0 commit comments

Comments
 (0)