import { onMessage, sendToPeer, broadcast } from './peer.js'; import { getUserId } from './crypto.js'; import { getPollById, updatePollInStore, setRole, removeRole, setPollStatus } from './stores/polls.svelte.js'; import { canVote, canAddOption, canManageUsers, canManagePoll } from './permissions.js'; import type { Message, Poll, PollOption, Vote } from './types.js'; const processedCommands = new Set(); let revision = 0; export function startHosting(): () => void { const unsub = onMessage(async (msg, peerId) => { const userId = await getUserId(); switch (msg.type) { case 'sync:request': { const poll = getPollById(msg.payload.pollId); if (poll && poll.ownerId === userId) { sendToPeer(peerId, { type: 'poll:state', payload: poll }); } break; } case 'poll:vote': { if (processedCommands.has(msg.commandId)) { sendToPeer(peerId, { type: 'ack', payload: { commandId: msg.commandId, revision } }); break; } const poll = getPollById(msg.payload.pollId); if (!poll || poll.ownerId !== userId) break; if (!canVote(poll, peerId)) { sendToPeer(peerId, { type: 'error', payload: { commandId: msg.commandId, message: 'Not authorized to vote' } }); break; } const vote: Vote = { optionId: msg.payload.optionId, voterId: poll.anonymous ? null : peerId, timestamp: msg.payload.timestamp }; const filteredVotes = poll.anonymous ? poll.votes : poll.votes.filter((v) => v.voterId !== peerId); const updated: Poll = { ...poll, votes: [...filteredVotes, vote] }; await updatePollInStore(updated); revision++; processedCommands.add(msg.commandId); sendToPeer(peerId, { type: 'ack', payload: { commandId: msg.commandId, revision } }); broadcast({ type: 'poll:state', payload: updated }, peerId); break; } case 'poll:option:add': { if (processedCommands.has(msg.commandId)) { sendToPeer(peerId, { type: 'ack', payload: { commandId: msg.commandId, revision } }); break; } const poll2 = getPollById(msg.payload.pollId); if (!poll2 || poll2.ownerId !== userId) break; if (!canAddOption(poll2, peerId)) { sendToPeer(peerId, { type: 'error', payload: { commandId: msg.commandId, message: 'Not authorized to add options' } }); break; } const option: PollOption = { id: msg.payload.id, text: msg.payload.text, addedBy: peerId, addedAt: msg.payload.addedAt }; const updated2: Poll = { ...poll2, options: [...poll2.options, option] }; await updatePollInStore(updated2); revision++; processedCommands.add(msg.commandId); sendToPeer(peerId, { type: 'ack', payload: { commandId: msg.commandId, revision } }); broadcast({ type: 'poll:state', payload: updated2 }, peerId); break; } case 'poll:role:update': { if (processedCommands.has(msg.commandId)) { sendToPeer(peerId, { type: 'ack', payload: { commandId: msg.commandId, revision } }); break; } const poll3 = getPollById(msg.payload.pollId); if (!poll3 || poll3.ownerId !== userId) break; if (!canManageUsers(poll3, peerId)) { sendToPeer(peerId, { type: 'error', payload: { commandId: msg.commandId, message: 'Not authorized to manage users' } }); break; } await setRole(msg.payload.pollId, msg.payload.userId, msg.payload.role); revision++; processedCommands.add(msg.commandId); const refreshed = getPollById(msg.payload.pollId); sendToPeer(peerId, { type: 'ack', payload: { commandId: msg.commandId, revision } }); if (refreshed) broadcast({ type: 'poll:state', payload: refreshed }, peerId); break; } case 'poll:status:update': { if (processedCommands.has(msg.commandId)) { sendToPeer(peerId, { type: 'ack', payload: { commandId: msg.commandId, revision } }); break; } const poll4 = getPollById(msg.payload.pollId); if (!poll4 || poll4.ownerId !== userId) break; if (!canManagePoll(poll4, peerId)) { sendToPeer(peerId, { type: 'error', payload: { commandId: msg.commandId, message: 'Not authorized to manage poll' } }); break; } await setPollStatus(msg.payload.pollId, msg.payload.status); revision++; processedCommands.add(msg.commandId); const refreshed2 = getPollById(msg.payload.pollId); sendToPeer(peerId, { type: 'ack', payload: { commandId: msg.commandId, revision } }); if (refreshed2) broadcast({ type: 'poll:state', payload: refreshed2 }, peerId); break; } case 'user:profile': { // Store peer's profile for display purposes // Could be extended to a peer profile cache break; } } }); return unsub; }