import type { ServerWebSocket } from "bun"; import type { WsData } from "./channel"; import { hasPermission, findUserById } from "./db"; import { config } from "./config"; import { state } from "./state"; import { broadcastChannelList } from "./broadcast"; export const websocketHandlers = { open(ws: ServerWebSocket) { const channel = state.channels.get(ws.data.channelId); if (channel) { channel.addClient(ws); broadcastChannelList(); } // Track connection by user ID const userId = ws.data.userId; if (userId) { if (!state.userConnections.has(userId)) { state.userConnections.set(userId, new Set()); } state.userConnections.get(userId)!.add(ws); } }, close(ws: ServerWebSocket) { const channel = state.channels.get(ws.data.channelId); if (channel) { channel.removeClient(ws); broadcastChannelList(); } // Remove from user connections tracking const userId = ws.data.userId; if (userId && state.userConnections.has(userId)) { state.userConnections.get(userId)!.delete(ws); if (state.userConnections.get(userId)!.size === 0) { state.userConnections.delete(userId); } } }, message(ws: ServerWebSocket, message: string | Buffer) { try { const data = JSON.parse(String(message)); // Handle channel switching if (data.action === "switch" && data.channelId) { const oldChannel = state.channels.get(ws.data.channelId); const newChannel = state.channels.get(data.channelId); if (!newChannel) { ws.send(JSON.stringify({ type: "error", message: "Channel not found" })); return; } if (oldChannel) oldChannel.removeClient(ws); ws.data.channelId = data.channelId; newChannel.addClient(ws); ws.send(JSON.stringify({ type: "switched", channelId: data.channelId })); broadcastChannelList(); return; } const channel = state.channels.get(ws.data.channelId); if (!channel) { console.log("[WS] No channel found for:", ws.data.channelId); return; } // Check permission for control actions const userId = ws.data.userId; if (!userId) { console.log("[WS] No userId on connection"); return; } const user = findUserById(userId); if (!user) { console.log("[WS] User not found:", userId); return; } // Guests can never control playback if (user.is_guest) { console.log("[WS] Guest cannot control playback"); return; } // Check default permissions or user-specific permissions const canControl = user.is_admin || config.defaultPermissions?.includes("control") || hasPermission(userId, "channel", ws.data.channelId, "control"); if (!canControl) { console.log("[WS] User lacks control permission:", user.username); return; } console.log("[WS] Control action:", data.action, "from", user.username); if (data.action === "pause") channel.pause(); else if (data.action === "unpause") channel.unpause(); else if (data.action === "seek" && typeof data.timestamp === "number") channel.seek(data.timestamp); else if (data.action === "jump" && typeof data.index === "number") channel.jumpTo(data.index); } catch {} }, };