add new module of queries limitation control

This commit is contained in:
fullgream 2025-02-24 18:08:55 +03:00
parent 2e4aa8b5bd
commit 5d32580580

109
modules/antiddos.js Normal file
View File

@ -0,0 +1,109 @@
const { NetHelperModule } = global.moduleApi;
const express = require("express");
const captcha = require("draw-captcha");
const HTTPServer = require("../components/_http");
const captchaPage = `<!DOCTYPE html><html><head><title>Captcha handling required</title><meta name="viewport" content="width=device-width, initial-scale=1"><meta charset="utf-8"></head><body>
<center>
<h1>Enter captcha</h1>
<p><img src=:captchaImg:></img></p>
<form action="/captcha-solve/:sid:/:from:" method="get">
<p><input type="label" name="solve"> <input type="submit" id="solve-captcha"></p>
</form>
</center>
</body></html>`;
function randint (min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
async function getCaptchaPage (fromUrl) {
const sidDict = "1234567890abcdef";
let sid = "";
for (let i = 0; i < 32; i++) {
sid += sidDict[randint(0, 15)];
}
const captchaObject = new captcha.MasterCaptcha();
getCaptchaPage.sids[sid] = await captchaObject.setupRandomCaptcha();
return captchaPage
.replace(new RegExp(":captchaImg:"), `/captcha/${sid}`)
.replace(new RegExp(":from:"), Buffer.from(fromUrl).toString("hex"))
.replace(new RegExp(":sid:"), sid);
}
getCaptchaPage.sids = new Object();
module.exports = class ExampleMdoule extends NetHelperModule {
constructor () {
super();
this.router = express.Router();
this.lastUptime = new Date(new Date().getTime() + 3600000);
this.queryCount = 0;
this.ipWhiteListed = new Object();
this.querylimit = new this.statics.Operator("querylimit", (argv) => {
if (argv.length !== 1) return false;
return !Number.isNaN(+argv[0]);
}, (argv, self, syntaxTree) => {
if (!(syntaxTree.Type.prototype instanceof HTTPServer)) {
throw new SyntaxError("Unsupported type of server");
}
let [ queriesLimit ] = argv;
queriesLimit = +queriesLimit;
const router = this.router;
router.use(async (req, res, next) => {
req.adsEx = req.adsEx ?? {};
if (this.lastUptime <= new Date()) {
this.lastUptime = new Date(this.lastUptime.getTime() + 3600000);
this.queryCount = 1;
this.ipWhiteListed = new Object();
} else
this.queryCount++;
if (this.queryCount > queriesLimit) {
//this.lastUptime = new Date(new Date().getTime() + 3600000);
req.adsEx.needsCaptcha = this.ipWhiteListed[req.realip] !== true;
}
return next();
});
router.get("/captcha/:sid", async (req, res, next) => {
if (!req.adsEx.needsCaptcha)
return next();
const sid = req.params.sid;
const content = getCaptchaPage.sids[sid];
if (content === undefined) {
res.set("Content-Type", "text/plain");
return res.status(400).send("Invalid SID. Captcha was solved or not found");
}
res.set("Content-Type", "image/jpeg");
res.send(content.buffer);
});
router.get("/captcha-solve/:sid/:redirectTo", async (req, res) => {
const sid = req.params.sid;
const content = getCaptchaPage.sids[sid];
const redirectTo = Buffer.from(req.params.redirectTo, "hex").toString("utf8");
const isSolved = req.query.solve?.toLowerCase() === content?.code;
this.ipWhiteListed[req.realip] = isSolved;
req.adsEx.needsCaptcha = !isSolved;
res.redirect(redirectTo);
});
router.use(async (req, res, next) => {
if (!req.adsEx.needsCaptcha)
return next();
res.send(await getCaptchaPage(req.originalUrl));
});
});
this.operators.push(this.querylimit);
}
bind (type) {
super.bind(type);
this.serverType.routelist.push(this.router);
}
};