diff --git a/src/middlewares/RateLimit.ts b/src/middlewares/RateLimit.ts
index e610d55b..ab69113e 100644
--- a/src/middlewares/RateLimit.ts
+++ b/src/middlewares/RateLimit.ts
@@ -1,5 +1,6 @@
-import { db, MongooseCache } from "@fosscord/server-util";
+import { db, MongooseCache, Bucket } from "@fosscord/server-util";
import { NextFunction, Request, Response } from "express";
+import { API_PREFIX, API_PREFIX_TRAILING_SLASH } from "./Authentication";
const Cache = new MongooseCache(db.collection("ratelimits"), [{ $match: { blocked: true } }], { onlyEvents: false, array: true });
@@ -22,10 +23,66 @@ TODO: use config values
*/
-export default function RateLimit(opts: { bucket?: string; window: number; count: number }) {
+export default function RateLimit(opts: {
+ bucket?: string;
+ window: number;
+ count: number;
+ bot?: number;
+ error?: number;
+ webhook?: number;
+ oauth?: number;
+ GET?: number;
+ MODIFY?: number;
+}) {
Cache.init(); // will only initalize it once
return async (req: Request, res: Response, next: NextFunction) => {
+ const bucket_id = req.path.replace(API_PREFIX_TRAILING_SLASH, "");
+ const user_id = req.user_id;
+ const max_hits = req.user_bot ? opts.bot : opts.count;
+ const offender = Cache.data.find((x: Bucket) => x.user && x.id === bucket_id) as Bucket | null;
+
+ if (offender && offender.blocked) {
+ const reset = offender.created_at.getTime() + opts.window;
+ const resetAfterMs = reset - Date.now();
+ const resetAfterSec = resetAfterMs / 1000;
+ const global = bucket_id === "global";
+
+ return (
+ res
+ .status(429)
+ .set("X-RateLimit-Limit", `${max_hits}`)
+ .set("X-RateLimit-Remaining", "0")
+ .set("X-RateLimit-Reset", `${reset}`)
+ .set("X-RateLimit-Reset-After", `${resetAfterSec}`)
+ .set("X-RateLimit-Global", `${global}`)
+ .set("Retry-After", `${Math.ceil(resetAfterSec)}`)
+ .set("X-RateLimit-Bucket", `${bucket_id}`)
+ // TODO: error rate limit message translation
+ .send({ message: "You are being rate limited.", retry_after: resetAfterSec, global })
+ );
+ }
next();
+ console.log(req.route);
+
+ if (opts.error) {
+ res.once("finish", () => {
+ // check if error and increment error rate limit
+ });
+ }
+
+ db.collection("ratelimits").updateOne(
+ { bucket: bucket_id },
+ {
+ $set: {
+ id: bucket_id,
+ user_id,
+ created_at: new Date(),
+ $cond: { if: { $gt: ["$hits", max_hits] }, then: true, else: false }
+ },
+ $inc: { hits: 1 }
+ },
+ { upsert: true }
+ );
};
}
|