Add reversible administrator room archiving

Organize closed rooms with archive filters, live-authorized atomic receipts and exact saved retries. Preserve saved games, timestamps, credentials, discussion and AI deadlines. Unarchive leaves play closed; reopen requires unarchive, and removal/restoration preserves both flags.

Verified types, lint, 5379 offline tests, 52 HTTP tests and build. Twenty dedicated administrator HTTP groups and independent privacy/persistence review passed. Desktop/mobile browser QA confirmed a single archive after a lost response and refresh, readable recovery text, mobile unarchive and separate reopening, and saved-seat restoration. All 2,049 baseline rooms, 4,348 seats and related records preserved. Authenticated production admin acceptance awaits authorized QA access; full admin and rules gates remain open.
This commit is contained in:
Quintine committed 2026-09-22 12:55:44 +10:00
1 parent 98b1b3261b
commit e918e2e0c0
35 files changed
+3624 -38

No files matched your search

+8 -1
View File
@@ -13,7 +13,7 @@ void test('admin HTTP denies anonymous, forged admin, room-host and spoofed iden
assert.ok(cookie);
const room = (await created.json() as { code: string }).code;
for (const headers of [{}, { cookie }, { cookie: 'dune_admin_session=' + 'a'.repeat(64) }, { 'oai-authenticated-user-id': 'owner', 'oai-authenticated-user-email': 'owner@example.test' }] as Record<string, string>[]) {
for (const path of ['/api/admin/session', '/api/admin/rooms', `/api/admin/rooms/${room}/control`, `/api/admin/rooms/${room}/lobby`, `/api/admin/rooms/${room}/removal`, `/api/admin/rooms/${room}/closure`]) {
for (const path of ['/api/admin/session', '/api/admin/rooms', `/api/admin/rooms/${room}/control`, `/api/admin/rooms/${room}/lobby`, `/api/admin/rooms/${room}/removal`, `/api/admin/rooms/${room}/closure`, `/api/admin/rooms/${room}/archive`]) {
const result = await fetch(base + path, { headers, signal: AbortSignal.timeout(15000) });
assert.equal(result.status, 401, path);
assert.equal(result.headers.get('cache-control'), 'no-store');
@@ -50,6 +50,13 @@ void test('admin HTTP denies anonymous, forged admin, room-host and spoofed iden
assert.equal(closure.status, 401);
assert.equal(closure.headers.get('set-cookie'), null);
assert.deepEqual(Object.keys(await closure.json()), ['error']);
const archive = await fetch(`${base}/api/admin/rooms/${room}/archive`, {
method: 'POST', headers: { ...headers, origin: base, 'Content-Type': 'application/json' },
body: JSON.stringify({ operationId: crypto.randomUUID(), expectedVersion: 0, expectedRevision: 0, archived: true, reason: 'Denied archive QA' }),
});
assert.equal(archive.status, 401);
assert.equal(archive.headers.get('set-cookie'), null);
assert.deepEqual(Object.keys(await archive.json()), ['error']);
const create = await fetch(base + '/api/admin/rooms', {
method: 'POST', headers: { ...headers, origin: base, 'Content-Type': 'application/json' },
body: JSON.stringify({ operationId: crypto.randomUUID(), sessionToken: 'b'.repeat(64), name: 'Denied creation QA', faction: 'atreides', advanced: false, techTokens: false, strongholdCards: false, bots: [], reason: 'Must not create a room' }),