Prototype administrative discussion mute controls
Let owners/operators mute or unmute new public/private sends from a human seat while retaining history, incoming messages, gameplay and credentials. Fence metadata and durable audit writes with live authority, membership and versions. Preserve exact admin/message retries and inherit restrictions through seat recovery. Validation: types/lint, 5431 offline tests, build, 52 HTTP integration tests, 24 administrator HTTP groups, independent SQL/privacy/UI review and desktop/mobile refresh acceptance. All 2230 prior games and 4746 seats preserved. Additive migration 0016. Verified runtime fingerprint 588967fbb9c2e3b6abe2990c4fce96ee69e2256fb7e3ee416314b87593cebba9; documentation-only grammar/button-label review gives b5e57bdd26812debede91c588e38f247f1fa895ad23e7ae30bc8550eedcd90ec. New-revision deployed acceptance remains open; full admin/rules scope is unfinished.
This commit is contained in:
1 parent
2cddf03932
commit
160fdc1f30
26 files changed
+3008
-33
No files matched your search
@@ -363,6 +363,32 @@ export const roomEntryReceipts = sqliteTable('room_entry_receipts', {
|
||||
playerId: text('player_id').notNull(),
|
||||
});
|
||||
|
||||
// Discussion moderation follows the logical seat across recovery and handover.
|
||||
export const seatDiscussionControls = sqliteTable('seat_discussion_controls', {
|
||||
roomCode: text('room_code').notNull().references(() => rooms.code, { onDelete: 'cascade' }),
|
||||
playerId: text('player_id').notNull(),
|
||||
muted: integer('muted').notNull().default(0),
|
||||
revision: integer('revision').notNull().default(0),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
}, table => [primaryKey({ columns: [table.roomCode, table.playerId] }),
|
||||
check('seat_discussion_muted', sql`${table.muted} IN (0, 1)`),
|
||||
check('seat_discussion_revision', sql`${table.revision} >= 0`)]);
|
||||
|
||||
// Keep audit receipts independent of room/account deletion, like other admin operations.
|
||||
export const adminDiscussionOperations = sqliteTable('admin_discussion_operations', {
|
||||
operationId: text('operation_id').primaryKey(),
|
||||
actorAdminId: text('actor_admin_id').notNull(),
|
||||
roomCode: text('room_code').notNull(),
|
||||
requestHash: text('request_hash').notNull(),
|
||||
expectedVersion: integer('expected_version').notNull(),
|
||||
expectedRevision: integer('expected_revision').notNull(),
|
||||
appliedRevision: integer('applied_revision').notNull(),
|
||||
target: text('target').notNull(),
|
||||
muted: integer('muted').notNull(),
|
||||
reason: text('reason').notNull(),
|
||||
createdAt: integer('created_at').notNull(),
|
||||
}, table => [index('admin_discussion_room').on(table.roomCode, table.createdAt)]);
|
||||
|
||||
export const roomMessages = sqliteTable(
|
||||
'room_messages',
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user