fix(equipment): DB-enforced reservation no-overlap (D1, P0)
reserveEquipment was check-then-insert with no DB guard: two concurrent requests both passed the in-memory overlap check and double-booked a machine. Migration 042 adds btree_gist + a GiST EXCLUDE constraint (equipment_id =, tstzrange && ) WHERE status='reserved', making the DB the arbiter; the action maps the 23P01 violation to the existing friendly overlap message. App-side check kept as a fast pre-check. Schema + DB maps updated.
This commit is contained in:
@@ -290,7 +290,15 @@ export async function reserveEquipment(input: unknown) {
|
||||
})
|
||||
.select('id')
|
||||
.single()
|
||||
if (error) return { error: error.message }
|
||||
if (error) {
|
||||
// The GiST exclusion constraint is the concurrency-safe arbiter: a
|
||||
// simultaneous request that passed the in-memory pre-check still loses
|
||||
// here. Surface the same friendly message as the pre-check.
|
||||
if (/exclusion constraint|equipment_reservations_no_overlap|23P01/i.test(error.message)) {
|
||||
return { error: 'That time overlaps an existing reservation.' }
|
||||
}
|
||||
return { error: error.message }
|
||||
}
|
||||
|
||||
await logActivity(supabase, member, 'reserved', 'equipment', r.equipmentId)
|
||||
revalidatePath('/equipment')
|
||||
|
||||
Reference in New Issue
Block a user