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:
@@ -21,10 +21,32 @@ export const estimateVersionWorkflowProcedures = {
|
||||
|
||||
let estimate;
|
||||
try {
|
||||
estimate = await submitEstimateVersion(
|
||||
ctx.db as unknown as Parameters<typeof submitEstimateVersion>[0],
|
||||
input,
|
||||
);
|
||||
estimate = await ctx.db.$transaction(async (tx) => {
|
||||
const submitted = await submitEstimateVersion(
|
||||
tx as unknown as Parameters<typeof submitEstimateVersion>[0],
|
||||
input,
|
||||
);
|
||||
|
||||
await tx.auditLog.create({
|
||||
data: {
|
||||
entityType: "Estimate",
|
||||
entityId: submitted.id,
|
||||
action: "UPDATE",
|
||||
...(ctx.dbUser?.id ? { userId: ctx.dbUser.id } : {}),
|
||||
changes: {
|
||||
after: {
|
||||
id: submitted.id,
|
||||
status: submitted.status,
|
||||
submittedVersionId: submitted.versions.find(
|
||||
(version) => version.status === "SUBMITTED",
|
||||
)?.id,
|
||||
},
|
||||
} as Prisma.InputJsonValue,
|
||||
},
|
||||
});
|
||||
|
||||
return submitted;
|
||||
});
|
||||
} catch (error) {
|
||||
rethrowEstimateRouterError(error, [
|
||||
{
|
||||
@@ -41,24 +63,6 @@ export const estimateVersionWorkflowProcedures = {
|
||||
]);
|
||||
}
|
||||
|
||||
await ctx.db.auditLog.create({
|
||||
data: {
|
||||
entityType: "Estimate",
|
||||
entityId: estimate.id,
|
||||
action: "UPDATE",
|
||||
...(ctx.dbUser?.id ? { userId: ctx.dbUser.id } : {}),
|
||||
changes: {
|
||||
after: {
|
||||
id: estimate.id,
|
||||
status: estimate.status,
|
||||
submittedVersionId: estimate.versions.find(
|
||||
(version) => version.status === "SUBMITTED",
|
||||
)?.id,
|
||||
},
|
||||
} as Prisma.InputJsonValue,
|
||||
},
|
||||
});
|
||||
|
||||
return estimate;
|
||||
}),
|
||||
|
||||
@@ -69,10 +73,32 @@ export const estimateVersionWorkflowProcedures = {
|
||||
|
||||
let estimate;
|
||||
try {
|
||||
estimate = await approveEstimateVersion(
|
||||
ctx.db as unknown as Parameters<typeof approveEstimateVersion>[0],
|
||||
input,
|
||||
);
|
||||
estimate = await ctx.db.$transaction(async (tx) => {
|
||||
const approved = await approveEstimateVersion(
|
||||
tx as unknown as Parameters<typeof approveEstimateVersion>[0],
|
||||
input,
|
||||
);
|
||||
|
||||
await tx.auditLog.create({
|
||||
data: {
|
||||
entityType: "Estimate",
|
||||
entityId: approved.id,
|
||||
action: "UPDATE",
|
||||
...(ctx.dbUser?.id ? { userId: ctx.dbUser.id } : {}),
|
||||
changes: {
|
||||
after: {
|
||||
id: approved.id,
|
||||
status: approved.status,
|
||||
approvedVersionId: approved.versions.find(
|
||||
(version) => version.status === "APPROVED",
|
||||
)?.id,
|
||||
},
|
||||
} as Prisma.InputJsonValue,
|
||||
},
|
||||
});
|
||||
|
||||
return approved;
|
||||
});
|
||||
} catch (error) {
|
||||
rethrowEstimateRouterError(error, [
|
||||
{
|
||||
@@ -89,24 +115,6 @@ export const estimateVersionWorkflowProcedures = {
|
||||
]);
|
||||
}
|
||||
|
||||
await ctx.db.auditLog.create({
|
||||
data: {
|
||||
entityType: "Estimate",
|
||||
entityId: estimate.id,
|
||||
action: "UPDATE",
|
||||
...(ctx.dbUser?.id ? { userId: ctx.dbUser.id } : {}),
|
||||
changes: {
|
||||
after: {
|
||||
id: estimate.id,
|
||||
status: estimate.status,
|
||||
approvedVersionId: estimate.versions.find(
|
||||
(version) => version.status === "APPROVED",
|
||||
)?.id,
|
||||
},
|
||||
} as Prisma.InputJsonValue,
|
||||
},
|
||||
});
|
||||
|
||||
return estimate;
|
||||
}),
|
||||
|
||||
@@ -117,10 +125,33 @@ export const estimateVersionWorkflowProcedures = {
|
||||
|
||||
let estimate;
|
||||
try {
|
||||
estimate = await createEstimateRevision(
|
||||
ctx.db as unknown as Parameters<typeof createEstimateRevision>[0],
|
||||
input,
|
||||
);
|
||||
estimate = await ctx.db.$transaction(async (tx) => {
|
||||
const revision = await createEstimateRevision(
|
||||
tx as unknown as Parameters<typeof createEstimateRevision>[0],
|
||||
input,
|
||||
);
|
||||
|
||||
await tx.auditLog.create({
|
||||
data: {
|
||||
entityType: "Estimate",
|
||||
entityId: revision.id,
|
||||
action: "UPDATE",
|
||||
...(ctx.dbUser?.id ? { userId: ctx.dbUser.id } : {}),
|
||||
changes: {
|
||||
after: {
|
||||
id: revision.id,
|
||||
status: revision.status,
|
||||
latestVersionNumber: revision.latestVersionNumber,
|
||||
workingVersionId: revision.versions.find(
|
||||
(version) => version.status === "WORKING",
|
||||
)?.id,
|
||||
},
|
||||
} as Prisma.InputJsonValue,
|
||||
},
|
||||
});
|
||||
|
||||
return revision;
|
||||
});
|
||||
} catch (error) {
|
||||
rethrowEstimateRouterError(error, [
|
||||
{
|
||||
@@ -138,25 +169,6 @@ export const estimateVersionWorkflowProcedures = {
|
||||
]);
|
||||
}
|
||||
|
||||
await ctx.db.auditLog.create({
|
||||
data: {
|
||||
entityType: "Estimate",
|
||||
entityId: estimate.id,
|
||||
action: "UPDATE",
|
||||
...(ctx.dbUser?.id ? { userId: ctx.dbUser.id } : {}),
|
||||
changes: {
|
||||
after: {
|
||||
id: estimate.id,
|
||||
status: estimate.status,
|
||||
latestVersionNumber: estimate.latestVersionNumber,
|
||||
workingVersionId: estimate.versions.find(
|
||||
(version) => version.status === "WORKING",
|
||||
)?.id,
|
||||
},
|
||||
} as Prisma.InputJsonValue,
|
||||
},
|
||||
});
|
||||
|
||||
return estimate;
|
||||
}),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user