|
| 1 | +import { createLogger } from '@sim/logger' |
| 2 | +import type { AuthenticatedSocket } from '@/middleware/auth' |
| 3 | +import { verifyTableAccess } from '@/middleware/permissions' |
| 4 | +import { type IRoomManager, tableRoomName } from '@/rooms/types' |
| 5 | + |
| 6 | +const logger = createLogger('TableHandlers') |
| 7 | + |
| 8 | +/** |
| 9 | + * Wires `join-table` / `leave-table` socket events. Tables don't track presence |
| 10 | + * or last-modified state — joining is a thin wrapper around `socket.join` so the |
| 11 | + * Sim API → Realtime HTTP bridge can broadcast row updates back to subscribed clients. |
| 12 | + */ |
| 13 | +export function setupTableHandlers(socket: AuthenticatedSocket, _roomManager: IRoomManager) { |
| 14 | + socket.on('join-table', async ({ tableId }: { tableId?: string }) => { |
| 15 | + try { |
| 16 | + if (!tableId || typeof tableId !== 'string') { |
| 17 | + socket.emit('join-table-error', { |
| 18 | + tableId: tableId ?? null, |
| 19 | + error: 'tableId required', |
| 20 | + code: 'INVALID_TABLE_ID', |
| 21 | + retryable: false, |
| 22 | + }) |
| 23 | + return |
| 24 | + } |
| 25 | + |
| 26 | + const userId = socket.userId |
| 27 | + if (!userId) { |
| 28 | + socket.emit('join-table-error', { |
| 29 | + tableId, |
| 30 | + error: 'Authentication required', |
| 31 | + code: 'AUTHENTICATION_REQUIRED', |
| 32 | + retryable: false, |
| 33 | + }) |
| 34 | + return |
| 35 | + } |
| 36 | + |
| 37 | + const { hasAccess } = await verifyTableAccess(userId, tableId) |
| 38 | + if (!hasAccess) { |
| 39 | + socket.emit('join-table-error', { |
| 40 | + tableId, |
| 41 | + error: 'Access denied to table', |
| 42 | + code: 'ACCESS_DENIED', |
| 43 | + retryable: false, |
| 44 | + }) |
| 45 | + return |
| 46 | + } |
| 47 | + |
| 48 | + const room = tableRoomName(tableId) |
| 49 | + socket.join(room) |
| 50 | + socket.emit('join-table-success', { tableId, socketId: socket.id }) |
| 51 | + logger.debug(`Socket ${socket.id} (user ${userId}) joined ${room}`) |
| 52 | + } catch (error) { |
| 53 | + logger.error(`Error joining table room:`, error) |
| 54 | + socket.emit('join-table-error', { |
| 55 | + tableId: null, |
| 56 | + error: 'Failed to join table', |
| 57 | + code: 'JOIN_TABLE_FAILED', |
| 58 | + retryable: true, |
| 59 | + }) |
| 60 | + } |
| 61 | + }) |
| 62 | + |
| 63 | + socket.on('leave-table', async ({ tableId }: { tableId?: string }) => { |
| 64 | + try { |
| 65 | + if (!tableId || typeof tableId !== 'string') return |
| 66 | + const room = tableRoomName(tableId) |
| 67 | + socket.leave(room) |
| 68 | + logger.debug(`Socket ${socket.id} left ${room}`) |
| 69 | + } catch (error) { |
| 70 | + logger.error(`Error leaving table room:`, error) |
| 71 | + } |
| 72 | + }) |
| 73 | +} |
0 commit comments