UNPKG

@yveskaufmann/koa2-ratelimit

Version:

IP rate-limiting middleware for Koajs 2. Use to limit repeated requests to APIs and/or endpoints such as password reset.

168 lines 5.72 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MongodbStore = void 0; const mongoose_1 = __importDefault(require("mongoose")); const Time_1 = require("../Time"); const Store_1 = require("./Store"); function findOrCreate({ where, defaults }) { return __awaiter(this, void 0, void 0, function* () { return this.collection.findOneAndUpdate(where, { $setOnInsert: defaults }, { upsert: true, returnDocument: "after" } // return new doc if one is upserted ); }); } const abuseSchema = new mongoose_1.default.Schema({ key: { type: String, required: true, index: { unique: true }, }, counter: { type: Number, required: true, default: 0, }, dateEnd: { type: Date, required: true, }, }); abuseSchema.statics.findOrCreate = findOrCreate; const abuseHistorySchema = new mongoose_1.default.Schema({ key: { type: String, required: true, }, prefix: { type: String, required: false, }, interval: { type: Number, required: true, }, nbMax: { type: Number, required: true, }, nbHit: { type: Number, required: true, default: 0, }, userId: { type: Number, required: false, }, ip: { type: String, required: false, }, dateEnd: { type: Date, required: true, }, createdAt: { type: Date, required: true, default: Date.now, }, updatedAt: { type: Date, required: true, default: Date.now, }, }); abuseHistorySchema.index({ key: 1, dateEnd: 1 }, { unique: true }); function beforSave(next) { this.updatedAt = Date.now(); next(); } abuseHistorySchema.pre("save", beforSave); abuseHistorySchema.pre("update", beforSave); abuseHistorySchema.pre("findOneAndUpdate", beforSave); abuseHistorySchema.statics.findOrCreate = findOrCreate; class MongodbStore extends Store_1.Store { constructor(mongodb, options = {}) { super(); this.mongodb = mongodb; this.collectionName = options.collectionName || "Ratelimits"; this.collectionAbuseName = options.collectionAbuseName || `${this.collectionName}Abuses`; this.Ratelimits = mongodb.model(this.collectionName, abuseSchema); this.Abuse = mongodb.model(this.collectionAbuseName, abuseHistorySchema); } _increment(model, where, nb = 1, field) { return __awaiter(this, void 0, void 0, function* () { return model.findOneAndUpdate(where, { $inc: { [field]: nb } }); }); } // remove all if time is passed _removeAll() { return __awaiter(this, void 0, void 0, function* () { yield this.Ratelimits.deleteMany({ dateEnd: { $lte: Date.now() } }); }); } incr(key, options, weight) { return __awaiter(this, void 0, void 0, function* () { yield this._removeAll(); const data = yield this.Ratelimits.findOrCreate({ where: { key }, defaults: { key, dateEnd: Date.now() + Time_1.Time.toMs(options.interval), counter: 0, }, }); yield this._increment(this.Ratelimits, { key }, weight, "counter"); return { counter: data.value.counter + weight, dateEnd: data.value.dateEnd, }; }); } decrement(key, options, weight) { return __awaiter(this, void 0, void 0, function* () { yield this._increment(this.Ratelimits, { key }, -weight, "counter"); }); } saveAbuse(options) { return __awaiter(this, void 0, void 0, function* () { const ratelimit = yield this.Ratelimits.findOne({ key: options.key, }).exec(); if (ratelimit) { // eslint-disable-next-line const dateEnd = ratelimit.dateEnd; // create if not exist yield this.Abuse.findOrCreate({ where: { key: options.key, dateEnd }, defaults: { key: options.key, prefix: options.prefixKey, interval: options.interval, nbMax: options.max, nbHit: options.max, userId: options.user_id, ip: options.ip, dateEnd, }, }).catch(() => { }); yield this._increment(this.Abuse, { key: options.key, dateEnd }, 1, "nbHit"); } }); } } exports.MongodbStore = MongodbStore; //# sourceMappingURL=MongodbStore.js.map