feat(matches): implement spam protection and socket notifications
S15.1-15.2: Rate Limiting & Spam Protection - Add max 20 pending outgoing match requests limit - Implement rate limiter: 10 match requests per minute per user - Return 429 status with clear error messages S16.1: Socket Notifications for New Suggestions - Emit 'recording_suggestions_created' event when matching creates suggestions - Notify only assigned recorders (not NOT_FOUND status) - Group suggestions by recorder for efficiency - Include event details and suggestion count Implementation: - backend/src/routes/matches.js: Rate limiter + pending limit check - backend/src/services/matching.js: Socket notifications in saveMatchingResults - backend/src/__tests__/spam-protection-notifications.test.js: 8 test cases Test coverage: - TC1-TC3: Max pending requests (spam protection) - TC4-TC5: Rate limiting (10/min) - TC6-TC8: Socket notifications for new suggestions
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
const express = require('express');
|
||||
const rateLimit = require('express-rate-limit');
|
||||
const { prisma } = require('../utils/db');
|
||||
const { authenticate } = require('../middleware/auth');
|
||||
const { getIO } = require('../socket');
|
||||
@@ -7,8 +8,20 @@ const matchingService = require('../services/matching');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Rate limiter for match creation: 10 requests per minute per user
|
||||
const matchRequestLimiter = rateLimit({
|
||||
windowMs: 60 * 1000, // 1 minute
|
||||
max: 10, // 10 requests per minute
|
||||
message: { success: false, error: 'Too many match requests. Please wait a minute before trying again.' },
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
// Use user ID as key (from authenticate middleware)
|
||||
keyGenerator: (req) => req.user?.id?.toString() || 'unauthenticated',
|
||||
skip: (req) => !req.user, // Skip if not authenticated (will fail at authenticate middleware)
|
||||
});
|
||||
|
||||
// POST /api/matches - Create a match request
|
||||
router.post('/', authenticate, async (req, res, next) => {
|
||||
router.post('/', authenticate, matchRequestLimiter, async (req, res, next) => {
|
||||
try {
|
||||
const { targetUserId, eventSlug } = req.body;
|
||||
const requesterId = req.user.id;
|
||||
@@ -28,6 +41,22 @@ router.post('/', authenticate, async (req, res, next) => {
|
||||
});
|
||||
}
|
||||
|
||||
// S15.1: Check max pending outgoing requests (spam protection)
|
||||
const pendingOutgoingCount = await prisma.match.count({
|
||||
where: {
|
||||
user1Id: requesterId, // user1 is the requester
|
||||
status: MATCH_STATUS.PENDING,
|
||||
},
|
||||
});
|
||||
|
||||
if (pendingOutgoingCount >= 20) {
|
||||
return res.status(429).json({
|
||||
success: false,
|
||||
error: 'You have too many pending match requests. Please wait for some to be accepted or rejected before sending more.',
|
||||
pendingCount: pendingOutgoingCount,
|
||||
});
|
||||
}
|
||||
|
||||
// Find event by slug
|
||||
const event = await prisma.event.findUnique({
|
||||
where: { slug: eventSlug },
|
||||
|
||||
Reference in New Issue
Block a user