feat(scheduling): retry calendar writes for pending bookings

A failed Stalwart calendar write during confirmation no longer deletes the
booking + SlotLock. The booking stays 'pending' with its lock retained, and a
new @Cron worker (every 2 min, max 5 attempts by default) re-drives the write:
on success it promotes to 'confirmed' and sends the confirmation email; after
the cap it moves to the terminal 'calendar_failed' state and releases the lock.

Tracks calendarWriteAttempts + lastCalendarError on the Booking. The public
confirm endpoint still throws 503 on a failed first write (preserving the DoD:
never surface a confirmed booking without a calendar event); the pending row is
left for the background retry to finish.
This commit is contained in:
Ronni Baslund
2026-06-07 08:49:53 +02:00
parent 9e1defa946
commit 2cb13a1a14
4 changed files with 163 additions and 24 deletions
@@ -14,6 +14,7 @@ import { User, UserSchema } from '../schemas/user.schema.js'
import { TenantsModule } from '../tenants/tenants.module.js'
import { AvailabilityService } from './availability/availability.service.js'
import { BookingsService } from './bookings/bookings.service.js'
import { CalendarRetryWorker } from './bookings/calendar-retry.worker.js'
import { JmapMailer } from './email/jmap-mailer.service.js'
import { EventTypesService } from './event-types/event-types.service.js'
import { HostsService } from './hosts/hosts.service.js'
@@ -59,6 +60,7 @@ import { StalwartCalendarModule } from './stalwart-calendar/stalwart-calendar.mo
PublicSchedulingService,
JmapMailer,
BookingReminderWorker,
CalendarRetryWorker,
],
})
export class SchedulingModule {}