diff --git a/artifacts/api-server/src/routes/services.ts b/artifacts/api-server/src/routes/services.ts index 1dbdedb0..b4e5e80f 100644 --- a/artifacts/api-server/src/routes/services.ts +++ b/artifacts/api-server/src/routes/services.ts @@ -153,6 +153,12 @@ router.delete("/services/:id", requireAdmin, async (req, res): Promise => return; } + // #195: keep the delete + audit insert in one transaction so we never + // end up with a removed service and no matching audit row (or vice versa). + // Forced deletes keep the dedicated `service.force_delete` action so the + // existing forced-only filter and metadata shape (orderCount) stay + // backwards compatible; plain deletes get a `service.delete` row mirroring + // the user.delete pattern. await db.transaction(async (tx) => { if (hasDeps) { // service_orders.service_id has ON DELETE RESTRICT, so we must clear @@ -162,36 +168,32 @@ router.delete("/services/:id", requireAdmin, async (req, res): Promise => .where(eq(serviceOrdersTable.serviceId, serviceId)); } await tx.delete(servicesTable).where(eq(servicesTable.id, serviceId)); - }); - // #195: write an audit row for every service deletion. Forced deletes keep - // the dedicated `service.force_delete` action so the existing forced-only - // filter and metadata shape (orderCount) stay backwards compatible; plain - // deletes get a `service.delete` row mirroring the user.delete pattern. - if (hasDeps && force) { - await db.insert(auditLogsTable).values({ - actorUserId: req.session.userId ?? null, - action: "service.force_delete", - targetType: "service", - targetId: serviceId, - metadata: { - nameEn: existing.nameEn, - nameAr: existing.nameAr, - orderCount, - }, - }); - } else { - await db.insert(auditLogsTable).values({ - actorUserId: req.session.userId ?? null, - action: "service.delete", - targetType: "service", - targetId: serviceId, - metadata: { - nameEn: existing.nameEn, - nameAr: existing.nameAr, - }, - }); - } + if (hasDeps && force) { + await tx.insert(auditLogsTable).values({ + actorUserId: req.session.userId ?? null, + action: "service.force_delete", + targetType: "service", + targetId: serviceId, + metadata: { + nameEn: existing.nameEn, + nameAr: existing.nameAr, + orderCount, + }, + }); + } else { + await tx.insert(auditLogsTable).values({ + actorUserId: req.session.userId ?? null, + action: "service.delete", + targetType: "service", + targetId: serviceId, + metadata: { + nameEn: existing.nameEn, + nameAr: existing.nameAr, + }, + }); + } + }); res.sendStatus(204); });