Close: make side-effects best-effort so none can abort the commit+delete

runFinalClose ran the transcript archive, creator DM, close log, and closure
email in the same try as the close transition and channel delete, with the
transcript posted *before* the commit. A failure in any of them (notably a
DiscordAPIError 50001 Missing Access when posting the transcript to the archive
channel) aborted the whole close: the customer saw 'ticket closed' but the DB
stayed open and the channel was never deleted.

Rewrite so the close transition + pendingDelete commit FIRST, each side-effect is
individually best-effort via a closeStep wrapper, and scheduleTicketChannelDelete
always runs. finalizeForceClose was already commit-first; wrap its remaining
unguarded archiving send too.
This commit is contained in:
2026-06-05 11:27:45 +00:00
parent b0e8d15273
commit 6a7dee679c
2 changed files with 98 additions and 80 deletions

View File

@@ -94,7 +94,8 @@ async function finalizeForceClose(channelRef, clientRef, _TicketModel, _recordAc
.catch(err => logError('gmailLabels: resolved move', err).catch(() => {}));
}
await enqueueSend(channelRef, 'Ticket force-closed. Archiving...');
// Both best-effort — a failure here must not skip the channel delete below.
await enqueueSend(channelRef, 'Ticket force-closed. Archiving...').catch(() => {});
await postTranscript(channelRef, clientRef, freshTicket).catch(tErr =>
console.error('Transcript error (force-close):', tErr)
);