feat: enhance security logging and ensure rate limit blocks are properly tracked
Mirror to GitHub / mirror (push) Successful in 31s
Test and Lint / backend-test (push) Successful in 1m27s
continuous-integration/drone/push Build is passing
Test and Lint / frontend-test (push) Successful in 2m22s
Version and Release / version-bump (push) Successful in 38s
Version and Release / trigger-drone (push) Successful in 3s
Mirror to GitHub / mirror (push) Successful in 31s
Test and Lint / backend-test (push) Successful in 1m27s
continuous-integration/drone/push Build is passing
Test and Lint / frontend-test (push) Successful in 2m22s
Version and Release / version-bump (push) Successful in 38s
Version and Release / trigger-drone (push) Successful in 3s
- Add comprehensive logging for rate limit blocks with full request details - IP address (with proper proxy detection), user agent, headers, timestamps - Rate limit info (current count, limit, remaining, reset time) - Separate tracking for auth vs general endpoints - Enhance authentication failure logging - JWT validation failures with detailed error info - Admin auth attempts without token - Failed token validation with user context - All events include IP, path, method, user agent - Improve Winston logger configuration for production - Add automatic log rotation (10MB errors, 50MB combined) - Create separate security.log for auth/rate limit events - Ensure logs directory exists automatically - Add structured JSON format for log aggregation - Support container logging with LOG_TO_CONSOLE env var - Create comprehensive documentation - Security logging guide with examples - Monitoring recommendations - Configuration reference - Add test script to verify logging functionality All rate limit settings remain configurable via admin panel: - Window duration, max requests, auth limits - Skip authenticated requests option - Public endpoints only option 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -109,8 +109,9 @@ async function getRecipientLanguage(email, eventId = null) {
|
||||
|
||||
// Process email template with variables
|
||||
async function processTemplate(template, variables, language = 'en') {
|
||||
// Import date formatter
|
||||
// Import date formatter and text formatters
|
||||
const { formatDate } = require('../utils/dateFormatter');
|
||||
const { formatWelcomeMessage } = require('../utils/formatters');
|
||||
|
||||
// Get the appropriate language fields
|
||||
const subjectField = language === 'de' ? 'subject_de' : 'subject_en';
|
||||
@@ -142,6 +143,11 @@ async function processTemplate(template, variables, language = 'en') {
|
||||
if (processedVariables.archive_date) {
|
||||
processedVariables.archive_date = await formatDate(processedVariables.archive_date, language);
|
||||
}
|
||||
|
||||
// Format welcome message for HTML display (preserve line breaks)
|
||||
if (processedVariables.welcome_message) {
|
||||
processedVariables.welcome_message = formatWelcomeMessage(processedVariables.welcome_message);
|
||||
}
|
||||
|
||||
// Get branding settings for logo
|
||||
let logoUrl = '';
|
||||
|
||||
@@ -177,13 +177,33 @@ async function createRateLimiter() {
|
||||
return shouldSkipRateLimit(req, currentConfig);
|
||||
},
|
||||
handler: (req, res) => {
|
||||
const clientIp = req.headers['x-forwarded-for']?.split(',')[0]?.trim() || req.ip;
|
||||
const clientIp = req.headers['x-forwarded-for']?.split(',')[0]?.trim() ||
|
||||
req.headers['x-real-ip'] ||
|
||||
req.connection.remoteAddress ||
|
||||
req.ip;
|
||||
|
||||
// Enhanced logging for production analysis
|
||||
logger.warn('Rate limit exceeded', {
|
||||
ip: clientIp,
|
||||
path: req.path,
|
||||
method: req.method,
|
||||
authenticated: isAuthenticated(req),
|
||||
tokenType: req.tokenType
|
||||
tokenType: req.tokenType,
|
||||
userAgent: req.headers['user-agent'],
|
||||
referer: req.headers['referer'],
|
||||
origin: req.headers['origin'],
|
||||
timestamp: new Date().toISOString(),
|
||||
headers: {
|
||||
'x-forwarded-for': req.headers['x-forwarded-for'],
|
||||
'x-real-ip': req.headers['x-real-ip']
|
||||
},
|
||||
requestUrl: req.originalUrl,
|
||||
rateLimitInfo: {
|
||||
limit: req.rateLimit?.limit,
|
||||
current: req.rateLimit?.current,
|
||||
remaining: req.rateLimit?.remaining,
|
||||
resetTime: req.rateLimit?.resetTime ? new Date(req.rateLimit.resetTime).toISOString() : null
|
||||
}
|
||||
});
|
||||
|
||||
res.status(429).json({
|
||||
@@ -217,10 +237,30 @@ async function createAuthRateLimiter() {
|
||||
return !currentConfig.enabled;
|
||||
},
|
||||
handler: (req, res) => {
|
||||
const clientIp = req.headers['x-forwarded-for']?.split(',')[0]?.trim() || req.ip;
|
||||
const clientIp = req.headers['x-forwarded-for']?.split(',')[0]?.trim() ||
|
||||
req.headers['x-real-ip'] ||
|
||||
req.connection.remoteAddress ||
|
||||
req.ip;
|
||||
|
||||
// Enhanced logging for auth failures
|
||||
logger.warn('Auth rate limit exceeded', {
|
||||
ip: clientIp,
|
||||
path: req.path
|
||||
path: req.path,
|
||||
method: req.method,
|
||||
userAgent: req.headers['user-agent'],
|
||||
timestamp: new Date().toISOString(),
|
||||
headers: {
|
||||
'x-forwarded-for': req.headers['x-forwarded-for'],
|
||||
'x-real-ip': req.headers['x-real-ip'
|
||||
},
|
||||
requestUrl: req.originalUrl,
|
||||
authType: req.path.includes('admin') ? 'admin' : 'gallery',
|
||||
rateLimitInfo: {
|
||||
limit: req.rateLimit?.limit,
|
||||
current: req.rateLimit?.current,
|
||||
remaining: req.rateLimit?.remaining,
|
||||
resetTime: req.rateLimit?.resetTime ? new Date(req.rateLimit.resetTime).toISOString() : null
|
||||
}
|
||||
});
|
||||
|
||||
res.status(429).json({
|
||||
|
||||
Reference in New Issue
Block a user