Spaces:
Configuration error
Configuration error
import { Server, Socket } from 'socket.io' | |
import jwt from 'jsonwebtoken' | |
interface AuthenticatedSocket extends Socket { | |
userId?: string | |
user?: any | |
} | |
// Store active connections | |
const activeConnections = new Map<string, AuthenticatedSocket>() | |
export function setupSocketHandlers(io: Server) { | |
// Authentication middleware | |
io.use(async (socket: AuthenticatedSocket, next) => { | |
try { | |
const token = socket.handshake.auth.token || socket.handshake.headers.authorization?.replace('Bearer ', '') | |
if (!token) { | |
return next(new Error('Authentication error: No token provided')) | |
} | |
// TODO: Verify JWT token and get user | |
// For now, just mock the authentication | |
const decoded = { userId: 'user-1', email: 'user@example.com' } | |
socket.userId = decoded.userId | |
socket.user = decoded | |
next() | |
} catch (error) { | |
next(new Error('Authentication error: Invalid token')) | |
} | |
}) | |
io.on('connection', (socket: AuthenticatedSocket) => { | |
console.log(`User ${socket.userId} connected`) | |
// Store the connection | |
if (socket.userId) { | |
activeConnections.set(socket.userId, socket) | |
} | |
// Join user to their personal room | |
if (socket.userId) { | |
socket.join(`user:${socket.userId}`) | |
} | |
// Handle joining chat rooms | |
socket.on('join_chat', (chatId: string) => { | |
socket.join(`chat:${chatId}`) | |
console.log(`User ${socket.userId} joined chat ${chatId}`) | |
}) | |
// Handle leaving chat rooms | |
socket.on('leave_chat', (chatId: string) => { | |
socket.leave(`chat:${chatId}`) | |
console.log(`User ${socket.userId} left chat ${chatId}`) | |
}) | |
// Handle sending messages | |
socket.on('send_message', async (data: { | |
chatId: string | |
content: string | |
type?: string | |
replyTo?: string | |
}) => { | |
try { | |
// TODO: Save message to database | |
const message = { | |
id: `msg-${Date.now()}`, | |
chatId: data.chatId, | |
senderId: socket.userId, | |
content: data.content, | |
type: data.type || 'text', | |
replyTo: data.replyTo, | |
createdAt: new Date(), | |
sender: socket.user | |
} | |
// Broadcast to all users in the chat | |
socket.to(`chat:${data.chatId}`).emit('new_message', message) | |
// Send confirmation back to sender | |
socket.emit('message_sent', { messageId: message.id, status: 'sent' }) | |
console.log(`Message sent in chat ${data.chatId} by user ${socket.userId}`) | |
} catch (error) { | |
socket.emit('error', { message: 'Failed to send message' }) | |
} | |
}) | |
// Handle typing indicators | |
socket.on('typing_start', (chatId: string) => { | |
socket.to(`chat:${chatId}`).emit('user_typing', { | |
userId: socket.userId, | |
chatId, | |
isTyping: true | |
}) | |
}) | |
socket.on('typing_stop', (chatId: string) => { | |
socket.to(`chat:${chatId}`).emit('user_typing', { | |
userId: socket.userId, | |
chatId, | |
isTyping: false | |
}) | |
}) | |
// Handle message reactions | |
socket.on('add_reaction', async (data: { | |
messageId: string | |
emoji: string | |
}) => { | |
try { | |
// TODO: Save reaction to database | |
const reaction = { | |
messageId: data.messageId, | |
userId: socket.userId, | |
emoji: data.emoji, | |
createdAt: new Date() | |
} | |
// Broadcast to all connected users | |
io.emit('reaction_added', reaction) | |
console.log(`Reaction ${data.emoji} added to message ${data.messageId} by user ${socket.userId}`) | |
} catch (error) { | |
socket.emit('error', { message: 'Failed to add reaction' }) | |
} | |
}) | |
// Handle message status updates | |
socket.on('message_read', async (data: { | |
messageId: string | |
chatId: string | |
}) => { | |
try { | |
// TODO: Update message read status in database | |
// Notify sender that message was read | |
socket.to(`chat:${data.chatId}`).emit('message_status_updated', { | |
messageId: data.messageId, | |
status: 'read', | |
readBy: socket.userId, | |
readAt: new Date() | |
}) | |
console.log(`Message ${data.messageId} marked as read by user ${socket.userId}`) | |
} catch (error) { | |
socket.emit('error', { message: 'Failed to update message status' }) | |
} | |
}) | |
// Handle user presence | |
socket.on('update_presence', (status: 'online' | 'away' | 'busy' | 'offline') => { | |
// TODO: Update user presence in database | |
// Broadcast presence update to all connected users | |
socket.broadcast.emit('user_presence_updated', { | |
userId: socket.userId, | |
status, | |
lastSeen: new Date() | |
}) | |
console.log(`User ${socket.userId} presence updated to ${status}`) | |
}) | |
// Handle disconnection | |
socket.on('disconnect', (reason) => { | |
console.log(`User ${socket.userId} disconnected: ${reason}`) | |
// Remove from active connections | |
if (socket.userId) { | |
activeConnections.delete(socket.userId) | |
} | |
// Update user presence to offline | |
socket.broadcast.emit('user_presence_updated', { | |
userId: socket.userId, | |
status: 'offline', | |
lastSeen: new Date() | |
}) | |
}) | |
// Handle errors | |
socket.on('error', (error) => { | |
console.error(`Socket error for user ${socket.userId}:`, error) | |
}) | |
}) | |
// Helper function to send message to specific user | |
const sendToUser = (userId: string, event: string, data: any) => { | |
const userSocket = activeConnections.get(userId) | |
if (userSocket) { | |
userSocket.emit(event, data) | |
return true | |
} | |
return false | |
} | |
// Helper function to send message to chat | |
const sendToChat = (chatId: string, event: string, data: any) => { | |
io.to(`chat:${chatId}`).emit(event, data) | |
} | |
// Export helper functions for use in other parts of the application | |
return { | |
sendToUser, | |
sendToChat, | |
getActiveConnections: () => activeConnections, | |
getConnectionCount: () => activeConnections.size | |
} | |
} | |