advplyr.audiobookshelf/server/utils/rateLimiterFactory.js

67 lines
1.9 KiB
JavaScript
Raw Normal View History

2025-07-07 16:23:15 -05:00
const { rateLimit, RateLimitRequestHandler } = require('express-rate-limit')
const Logger = require('../Logger')
const requestIp = require('../libs/requestIp')
2025-07-07 16:23:15 -05:00
/**
* Factory for creating authentication rate limiters
*/
class RateLimiterFactory {
constructor() {
this.authRateLimiter = null
}
/**
* Get the authentication rate limiter
* @returns {RateLimitRequestHandler}
*/
getAuthRateLimiter() {
if (this.authRateLimiter) {
return this.authRateLimiter
}
let windowMs = 10 * 60 * 1000 // 10 minutes default
if (parseInt(process.env.RATE_LIMIT_AUTH_WINDOW) > 0) {
windowMs = parseInt(process.env.RATE_LIMIT_AUTH_WINDOW)
}
let max = 40 // 40 attempts default
2025-07-07 16:23:15 -05:00
if (parseInt(process.env.RATE_LIMIT_AUTH_MAX) > 0) {
max = parseInt(process.env.RATE_LIMIT_AUTH_MAX)
}
let message = 'Too many authentication requests'
2025-07-07 16:23:15 -05:00
if (process.env.RATE_LIMIT_AUTH_MESSAGE) {
message = process.env.RATE_LIMIT_AUTH_MESSAGE
}
this.authRateLimiter = rateLimit({
windowMs,
max,
standardHeaders: true,
legacyHeaders: false,
keyGenerator: (req) => {
// Override keyGenerator to handle proxy IPs
return requestIp.getClientIp(req) || req.ip
},
2025-07-07 16:23:15 -05:00
handler: (req, res) => {
const userAgent = req.get('User-Agent') || 'Unknown'
const endpoint = req.path
const method = req.method
const ip = requestIp.getClientIp(req) || req.ip
2025-07-07 16:23:15 -05:00
Logger.warn(`[RateLimiter] Rate limit exceeded - IP: ${ip}, Endpoint: ${method} ${endpoint}, User-Agent: ${userAgent}`)
2025-07-07 16:23:15 -05:00
res.status(429).json({
error: message
2025-07-07 16:23:15 -05:00
})
}
})
Logger.debug(`[RateLimiterFactory] Created auth rate limiter: ${max} attempts per ${windowMs / 1000 / 60} minutes`)
return this.authRateLimiter
}
}
module.exports = new RateLimiterFactory()