import { Server } from 'socket.io' import { PrismaClient } from '@prisma/client' import type { Server as HTTPServer } from 'http' const prisma = new PrismaClient() export function setupSocketServer(httpServer: HTTPServer) { const io = new Server(httpServer, { cors: { origin: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000', methods: ['GET', 'POST'], credentials: true, }, }) io.on('connection', (socket) => { console.log('Client connected:', socket.id) // Join a stream room socket.on('join:stream', ({ streamId, userId }) => { console.log(`User ${userId} joined stream ${streamId}`) socket.join(`stream:${streamId}`) // Broadcast viewer count update const roomSize = io.sockets.adapter.rooms.get(`stream:${streamId}`)?.size || 0 io.to(`stream:${streamId}`).emit('viewers:update', { streamId, viewerCount: roomSize, }) // Update viewer count in database (throttled) prisma.stream .update({ where: { id: streamId }, data: { viewerCount: roomSize }, }) .catch(console.error) }) // Leave stream room socket.on('leave:stream', ({ streamId, userId }) => { console.log(`User ${userId} left stream ${streamId}`) socket.leave(`stream:${streamId}`) // Broadcast viewer count update const roomSize = io.sockets.adapter.rooms.get(`stream:${streamId}`)?.size || 0 io.to(`stream:${streamId}`).emit('viewers:update', { streamId, viewerCount: roomSize, }) // Update viewer count in database prisma.stream .update({ where: { id: streamId }, data: { viewerCount: roomSize }, }) .catch(console.error) }) // Send chat message socket.on('chat:message', async ({ streamId, userId, message }) => { try { // Get user details const user = await prisma.user.findUnique({ where: { id: userId }, select: { id: true, displayName: true, avatarUrl: true, verified: true, isCreator: true, }, }) if (!user) { socket.emit('chat:error', { message: 'User not found' }) return } // Save message to database const chatMessage = await prisma.chatMessage.create({ data: { streamId, userId, message: message.trim(), badges: user.isCreator ? ['creator'] : [], }, }) // Broadcast to all in room io.to(`stream:${streamId}`).emit('chat:message', { id: chatMessage.id, message: chatMessage.message, badges: chatMessage.badges, user: { id: user.id, displayName: user.displayName, avatarUrl: user.avatarUrl, verified: user.verified, }, createdAt: chatMessage.createdAt, }) } catch (error) { console.error('Error sending chat message:', error) socket.emit('chat:error', { message: 'Failed to send message' }) } }) // Delete chat message (moderator only) socket.on('chat:delete', async ({ messageId, userId }) => { try { // TODO: Check if user is a moderator await prisma.chatMessage.update({ where: { id: messageId }, data: { isDeleted: true, deletedReason: 'Deleted by moderator' }, }) // Notify all clients io.emit('chat:deleted', { messageId }) } catch (error) { console.error('Error deleting chat message:', error) } }) // Notification for donations socket.on('donation:alert', ({ streamId, donation }) => { io.to(`stream:${streamId}`).emit('donation:received', donation) }) // Disconnect socket.on('disconnect', () => { console.log('Client disconnected:', socket.id) }) }) return io }