Team Ai
Apppublic

sanket3280/code-execution

sourceHugging Faceupdated 11mo agoView on Hugging Face
0likes
challengeLeaderboard.socket.js259 linesDownload Raw Back to sockets
1const Challenge = require('../models/Challenge');2const ChallengeLeaderboardService = require('../services/challengeLeaderboard.service');3const logger = require('../utils/logger');4 5// Store for batching updates6const updateBatches = {};7 8/**9 * Initialize challenge leaderboard WebSocket handlers10 * @param {SocketIO.Server} io - Socket.IO server instance11 */12function initializeChallengeLeaderboardSocket(io) {13  io.on('connection', (socket) => {14    logger.debug(`Socket connected: ${socket.id}`);15    16    // Join challenge leaderboard room17    socket.on('join-challenge-leaderboard', async ({ challengeId, userId }) => {18      try {19        logger.debug(`User ${userId} attempting to join leaderboard room: ${challengeId}`);20        21        // Validate challenge exists and is active22        const challenge = await Challenge.findById(challengeId);23        if (!challenge) {24          logger.warn(`Challenge not found: ${challengeId}`);25          return socket.emit('leaderboard-error', { 26            message: 'Challenge not found' 27          });28        }29        30        // Join the room31        const roomName = `challenge:leaderboard:${challengeId}`;32        socket.join(roomName);33        34        // Send initial leaderboard data35        const leaderboardData = await ChallengeLeaderboardService36          .getChallengeLeaderboard(challengeId, { limit: 50 });37        38        // Get user rank if userId provided39        let userRank = null;40        if (userId) {41          try {42            userRank = await ChallengeLeaderboardService.getUserRank(challengeId, userId);43          } catch (rankError) {44            logger.error('Error fetching user rank:', rankError);45          }46        }47        48        // Get recent activity49        let recentActivity = [];50        try {51          recentActivity = await ChallengeLeaderboardService.getRecentActivity(challengeId, { limit: 20 });52        } catch (activityError) {53          logger.error('Error fetching recent activity:', activityError);54        }55        56        socket.emit('leaderboard-initial-data', {57          ...leaderboardData,58          userRank,59          recentActivity,60          timestamp: new Date()61        });62        63        // Track connection64        logger.info(`User ${userId} joined leaderboard room: ${roomName}`);65      } catch (error) {66        logger.error('Error joining leaderboard room:', error);67        socket.emit('leaderboard-error', { 68          message: 'Failed to join leaderboard',69          error: error.message 70        });71      }72    });73    74    // Leave challenge leaderboard room75    socket.on('leave-challenge-leaderboard', ({ challengeId }) => {76      const roomName = `challenge:leaderboard:${challengeId}`;77      socket.leave(roomName);78      logger.debug(`Socket ${socket.id} left leaderboard room: ${roomName}`);79    });80    81    // Manual refresh request82    socket.on('request-leaderboard-update', async ({ challengeId, userId }) => {83      try {84        logger.debug(`Manual refresh requested for challenge: ${challengeId}`);85        86        const leaderboardData = await ChallengeLeaderboardService87          .getChallengeLeaderboard(challengeId, { limit: 50 });88        89        // Get user rank if userId provided90        let userRank = null;91        if (userId) {92          try {93            userRank = await ChallengeLeaderboardService.getUserRank(challengeId, userId);94          } catch (rankError) {95            logger.error('Error fetching user rank:', rankError);96          }97        }98        99        // Get recent activity100        let recentActivity = [];101        try {102          recentActivity = await ChallengeLeaderboardService.getRecentActivity(challengeId, { limit: 20 });103        } catch (activityError) {104          logger.error('Error fetching recent activity:', activityError);105        }106        107        socket.emit('leaderboard-update', {108          ...leaderboardData,109          userRank,110          recentActivity,111          timestamp: new Date()112        });113      } catch (error) {114        logger.error('Error fetching leaderboard:', error);115        socket.emit('leaderboard-error', { 116          message: 'Failed to fetch leaderboard',117          error: error.message 118        });119      }120    });121    122    // Handle disconnection123    socket.on('disconnect', () => {124      logger.debug(`Socket disconnected: ${socket.id}`);125    });126  });127}128 129/**130 * Broadcast leaderboard update to all participants131 * @param {SocketIO.Server} io - Socket.IO server instance132 * @param {string} challengeId - Challenge ID133 * @param {Object} updateData - Leaderboard update data134 */135async function broadcastLeaderboardUpdate(io, challengeId, updateData) {136  const roomName = `challenge:leaderboard:${challengeId}`;137  138  try {139    // Initialize batch if not exists140    if (!updateBatches[challengeId]) {141      updateBatches[challengeId] = {142        updates: [],143        timeout: null144      };145    }146    147    updateBatches[challengeId].updates.push(updateData);148    149    // Clear existing timeout150    if (updateBatches[challengeId].timeout) {151      clearTimeout(updateBatches[challengeId].timeout);152    }153    154    // Set new timeout to batch updates (200ms window)155    updateBatches[challengeId].timeout = setTimeout(async () => {156      const batch = updateBatches[challengeId].updates;157      delete updateBatches[challengeId];158      159      // Merge updates and broadcast160      const mergedUpdate = await mergeBatchUpdates(challengeId, batch);161      162      // Check message size (limit to 50KB)163      const messageSize = JSON.stringify(mergedUpdate).length;164      if (messageSize > 50000) {165        logger.warn(`Leaderboard update message too large: ${messageSize} bytes, truncating...`);166        // Truncate leaderboard to top 20 if too large167        if (mergedUpdate.leaderboard && mergedUpdate.leaderboard.length > 20) {168          mergedUpdate.leaderboard = mergedUpdate.leaderboard.slice(0, 20);169        }170      }171      172      io.to(roomName).emit('leaderboard-update', mergedUpdate);173      174      logger.debug(`Broadcasted batched update to ${roomName} (${batch.length} updates merged)`);175    }, 200);176  } catch (error) {177    logger.error('Error broadcasting leaderboard update:', error);178  }179}180 181/**182 * Merge multiple batch updates into a single update183 * @param {string} challengeId - Challenge ID184 * @param {Array} batch - Array of update objects185 * @returns {Promise<Object>} Merged update data186 */187async function mergeBatchUpdates(challengeId, batch) {188  try {189    // Get fresh leaderboard data190    const leaderboardData = await ChallengeLeaderboardService191      .getChallengeLeaderboard(challengeId, { limit: 50 });192    193    // Get challenge stats194    const stats = await ChallengeLeaderboardService.getChallengeStats(challengeId);195    196    // Get recent activity197    const recentActivity = await ChallengeLeaderboardService.getRecentActivity(challengeId, { limit: 20 });198    199    // Collect all affected user IDs from batch200    const affectedUserIds = [...new Set(batch.map(update => update.userId).filter(Boolean))];201    202    // Get updated ranks for affected users203    const userRanks = {};204    for (const userId of affectedUserIds) {205      try {206        userRanks[userId] = await ChallengeLeaderboardService.getUserRank(challengeId, userId);207      } catch (error) {208        logger.error(`Error getting rank for user ${userId}:`, error);209      }210    }211    212    return {213      ...leaderboardData,214      stats,215      recentActivity,216      userRanks,217      affectedUsers: affectedUserIds,218      timestamp: new Date(),219      batchSize: batch.length220    };221  } catch (error) {222    logger.error('Error merging batch updates:', error);223    // Return minimal update on error224    return {225      challengeId,226      timestamp: new Date(),227      error: 'Failed to merge updates'228    };229  }230}231 232/**233 * Broadcast recent activity event234 * @param {SocketIO.Server} io - Socket.IO server instance235 * @param {string} challengeId - Challenge ID236 * @param {Object} activityData - Activity event data237 */238function broadcastActivity(io, challengeId, activityData) {239  const roomName = `challenge:leaderboard:${challengeId}`;240  241  try {242    io.to(roomName).emit('activity-update', {243      challengeId,244      activity: activityData,245      timestamp: new Date()246    });247    248    logger.debug(`Broadcasted activity to ${roomName}:`, activityData.type);249  } catch (error) {250    logger.error('Error broadcasting activity:', error);251  }252}253 254module.exports = {255  initializeChallengeLeaderboardSocket,256  broadcastLeaderboardUpdate,257  broadcastActivity258};259