Add administrator pause and joining-lock controls

Provide authenticated owner/operator room controls with live account binding, durable audit and exact saved retries. Pause gates human, bot and automatic writes while preserving recovery, discussion and taking back control. Resume keeps decisions and restores AI pacing. Add directory availability filters and player notices.

Verified types, lint, 5223 offline tests, 52 HTTP tests and build. Independent persistence/privacy review and authenticated HTTP acceptance passed. Desktop/mobile browser QA confirmed lost-response refresh/retry makes one audit row and resume restores controls. Existing games preserved. Authenticated deployed admin acceptance remains pending explicit QA access confirmation.
This commit is contained in:
Quintine committed 2026-09-22 10:41:09 +10:00
1 parent f29d05620c
commit 8c922b446b
39 files changed
+3196 -104

No files matched your search

+15
View File
@@ -66,3 +66,18 @@ void test('admin directory rechecks authority inside its snapshot after concurre
await assert.rejects(pending, (e: unknown) => e instanceof AdminError && e.status === 401);
} finally { f.sqlite.close(); }
});
void test('directory projects and filters operational flags without private audit reasons', async () => {
const f = await fixture();
try {
insert(f.sqlite, 'ROOMAAAA', state()); insert(f.sqlite, 'ROOMBBBB', state());
f.sqlite.prepare('INSERT INTO room_controls(room_code,paused,join_locked,revision,updated_at) VALUES (?,1,1,2,999)').run('ROOMAAAA');
const read = (query: string) => readAdminDirectory(f.database, f.identity, new URLSearchParams(query));
const paused = await read('availability=paused');
assert.equal(paused.total, 1);
assert.deepEqual(paused.rooms[0].control, { paused: true, joinLocked: true, revision: 2, updatedAt: 999 });
assert.equal((await read('availability=running')).rooms[0].code, 'ROOMBBBB');
assert.equal((await read('availability=locked')).rooms[0].code, 'ROOMAAAA');
await assert.rejects(read('availability=unsupported'), (e: unknown) => e instanceof AdminError && e.status === 400);
} finally { f.sqlite.close(); }
});