fix(api): wrap audit log writes inside their parent transactions

Prevents mutations from committing without an audit trail if the
auditLog.create call fails after the main write already succeeded.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-09 16:40:10 +02:00
parent a01f99561d
commit 3c0179fcec
25 changed files with 758 additions and 656 deletions
@@ -159,36 +159,38 @@ export async function importCsv(ctx: ImportExportMutationContext, input: ImportC
return { ...results, message: `Dry run: ${input.rows.length} rows validated` };
}
for (let index = 0; index < input.rows.length; index += 1) {
const row = input.rows[index];
if (!row) {
continue;
}
try {
if (input.entityType === "resources") {
const outcome = await importResourceRow(ctx, row);
if (outcome.updated) {
results.updated += 1;
} else if (outcome.error) {
results.errors.push({ row: index + 1, message: outcome.error });
}
await ctx.db.$transaction(async (tx) => {
for (let index = 0; index < input.rows.length; index += 1) {
const row = input.rows[index];
if (!row) {
continue;
}
} catch (error) {
results.errors.push({
row: index + 1,
message: error instanceof Error ? error.message : "Unknown error",
});
}
}
await ctx.db.auditLog.create({
data: {
entityType: input.entityType,
entityId: "bulk-import",
action: "IMPORT",
changes: { summary: results },
},
try {
if (input.entityType === "resources") {
const outcome = await importResourceRow({ ...ctx, db: tx as unknown as typeof ctx.db }, row);
if (outcome.updated) {
results.updated += 1;
} else if (outcome.error) {
results.errors.push({ row: index + 1, message: outcome.error });
}
}
} catch (error) {
results.errors.push({
row: index + 1,
message: error instanceof Error ? error.message : "Unknown error",
});
}
}
await tx.auditLog.create({
data: {
entityType: input.entityType,
entityId: "bulk-import",
action: "IMPORT",
changes: { summary: results },
},
});
});
return results;