Any member can destroy a room; observer sees highlighted whose turn it is
deploy / deploy (push) Canceled after 0s

- DELETE /api/rooms/:id + destroy button in the header of human pages
  (participant and observer views), with confirmation
- turn indicator now names and highlights the expected participant(s)
This commit is contained in:
2026-09-06 20:44:37 +03:00
parent 91fc74efc1
commit 1fa7e733ba
9 changed files with 164 additions and 18 deletions
+20 -1
View File
@@ -318,6 +318,7 @@ export class RendezvousService {
: null;
const status = room.status as 'open' | 'agreed' | 'expired';
const viewStatus = new Date(room.expires_at).getTime() < Date.now() ? 'expired' : status;
const turn = computeTurn(viewStatus, participants, openQuestions, contract);
return {
room: {
id: room.id,
@@ -334,7 +335,8 @@ export class RendezvousService {
resolved_questions: resolvedQuestions,
current_contract: contract,
room_status: viewStatus,
turn: computeTurn(viewStatus, participants, openQuestions, contract),
turn: turn.text,
turn_waiting_for: turn.waiting_for,
};
}
@@ -539,6 +541,23 @@ export class RendezvousService {
};
}
// ---------- destroy ----------
/**
* Immediately and irreversibly delete a room. Allowed for any room member:
* a participant (invite token) or the observer (observer token).
*/
destroyRoom(roomId: string, token: string): void {
const room = this.getRoomRow(roomId);
const isObserver = room.observer_token !== null && room.observer_token === token;
const isParticipant =
!!this.db.prepare('SELECT id FROM participants WHERE room_id = ? AND token = ?').get(roomId, token);
if (!isObserver && !isParticipant) {
throw new RendezvousError('forbidden', 'invalid token for this room');
}
this.db.prepare('DELETE FROM rooms WHERE id = ?').run(roomId); // cascade wipes everything
}
// ---------- TTL cleanup ----------
/** Deletes everything belonging to expired rooms. Returns number of rooms removed. */