save-server
Version:
A powerful ShareX image and URL server, with support for multiple users.
84 lines (75 loc) • 2.18 kB
JavaScript
const { errorGenerator } = require("../util/errors");
// limitNo: The number of requests that can be made per "timePer". By default this is per minute.
// timePer (Seconds): The time for rate limit bucket to last
// Rate limiting is based on wherever it's called
module.exports = function makeMiddleware(limitNo = 60, timePer = 60) {
const bucket = new Map();
addAutoClear(bucket);
return function rateLimit(req, res, next) {
const ip = getIp(req);
const userLimit = bucket.get(ip);
if (userLimit) {
// Check times
const timeSince = Date.now() - userLimit.first;
if (timeSince < timePer * 1000) {
// Check count
if (userLimit.no + 1 > limitNo) {
// Rate limit hit. Ouch!
// Tell them to cool it. As they've hit a cooldown, give a timeout.
increment(bucket, ip);
const retryAfter = (timePer * 1000) - timeSince;
res.set("Retry-After", retryAfter);
console.log(`Excessive requests. ${userLimit.no} - ${ip}: ${req.user ? req.user.username : "Unauthenticated"}`);
return res.status(429).send(errorGenerator(429, "Too many requests. Slow down!", { retryAfter }));
}
increment(bucket, ip);
} else {
// It's expired
bucket.delete(ip);
increment(bucket, ip);
}
} else {
increment(bucket, ip);
}
return next();
};
};
function increment(bucket, ip) {
const curr = bucket.get(ip);
if (curr) {
bucket.set(ip, {
no: curr.no + 1,
first: curr.first
});
} else {
bucket.set(ip, {
no: 1,
first: Date.now()
});
}
}
// Check for out of date every 10 minutes
const buckets = [];
const addAutoClear = i => buckets.push(i);
setInterval(() => {
for (const bucket of buckets) {
// If it's more than 10 minutes remove
bucket.forEach((value, key) => {
if (Date.now() - value.start > 600000) {
bucket.delete(key);
}
});
}
}, 600000);
function getIp(req) {
const ip = req.get("CF-Connecting-IP");
if (!ip) {
const cloudflare = process.env.cloudflare_limiting;
if (process.env.NODE_ENV === "production" && (cloudflare === "true" || cloudflare === true)) {
throw new Error("No Cloudflare IP available");
} else {
return req.ip;
}
}
return ip;
}