feat(admin): add Activity Log backend services (Phase 2)

Core services for activity logging system:

1. ActivityLog Service (backend/src/services/activityLog.js)
   - Centralized logging with fire-and-forget pattern
   - 18 action constants (auth, event, match, admin, chat)
   - Query interface with filtering (date, action, user, category)
   - Socket.IO emission for real-time streaming
   - Statistics and action types endpoints
   - Never throws - logging cannot crash app

2. Request Utility (backend/src/utils/request.js)
   - getClientIP() - Extract client IP from headers/socket
   - Handles X-Forwarded-For and X-Real-IP proxy headers
   - IPv6-mapped IPv4 conversion

3. Admin Middleware (backend/src/middleware/admin.js)
   - requireAdmin() - Protect admin routes
   - Fresh DB check of isAdmin flag
   - Returns 403 for non-admin users
   - Use after authenticate middleware

Next phases: logging integration points, API endpoints, frontend UI
This commit is contained in:
Radosław Gierwiało
2025-12-02 19:47:47 +01:00
parent f9cdf2aa98
commit c9beee9a4e
3 changed files with 447 additions and 0 deletions

View File

@@ -0,0 +1,52 @@
/**
* Request utility functions
* Helpers for extracting information from Express requests
*/
/**
* Extract client IP address from request
* Considers proxy headers (X-Forwarded-For, X-Real-IP)
*
* @param {Object} req - Express request object
* @returns {string|null} - Client IP address or null
*/
function getClientIP(req) {
try {
// Check X-Forwarded-For header (used by proxies/load balancers)
const forwardedFor = req.headers['x-forwarded-for'];
if (forwardedFor) {
// X-Forwarded-For can contain multiple IPs, take the first one (client IP)
const ips = forwardedFor.split(',').map(ip => ip.trim());
if (ips.length > 0 && ips[0]) {
return ips[0];
}
}
// Check X-Real-IP header (used by some proxies)
const realIP = req.headers['x-real-ip'];
if (realIP) {
return realIP;
}
// Fallback to req.ip (Express built-in)
if (req.ip) {
// Express sometimes returns IPv6-mapped IPv4 (::ffff:192.168.1.1)
// Convert to standard IPv4 format
return req.ip.replace(/^::ffff:/, '');
}
// Fallback to socket remote address
if (req.connection && req.connection.remoteAddress) {
return req.connection.remoteAddress.replace(/^::ffff:/, '');
}
return null;
} catch (error) {
console.error('Error extracting client IP:', error);
return null;
}
}
module.exports = {
getClientIP,
};