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.

203 lines 7.06 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.SequelizeStore = void 0; const Store_1 = require("./Store"); const sequelize_1 = __importDefault(require("sequelize")); const Time_1 = require("../Time"); const tableOption = [ { key: { type: sequelize_1.default.STRING(255), allowNull: false, primaryKey: true, unique: true, }, counter: { type: sequelize_1.default.INTEGER, allowNull: false, defaultValue: 0, }, date_end: { type: sequelize_1.default.DATE, allowNull: false, }, }, { indexes: [ { unique: true, fields: ["key"] }, { unique: false, fields: ["date_end"] }, ], underscored: true, createdAt: false, updatedAt: false, }, ]; const tableAbuseOption = [ { id: { allowNull: false, autoIncrement: true, primaryKey: true, type: sequelize_1.default.INTEGER, }, key: { type: sequelize_1.default.STRING(255), allowNull: false, }, prefix: { type: sequelize_1.default.STRING(255), allowNull: true, }, interval: { type: sequelize_1.default.INTEGER, allowNull: false, }, nb_max: { type: sequelize_1.default.INTEGER, allowNull: false, }, nb_hit: { type: sequelize_1.default.INTEGER, allowNull: false, defaultValue: 0, }, user_id: { allowNull: true, type: sequelize_1.default.INTEGER, }, ip: { type: sequelize_1.default.STRING(255), allowNull: true, }, date_end: { type: sequelize_1.default.DATE, allowNull: false, }, created_at: { allowNull: false, type: sequelize_1.default.DATE, defaultValue: sequelize_1.default.fn("NOW"), }, updated_at: { allowNull: false, type: sequelize_1.default.DATE, defaultValue: sequelize_1.default.fn("NOW"), }, }, { indexes: [{ unique: true, fields: ["key", "date_end"] }], underscored: true, }, ]; class SequelizeStore extends Store_1.Store { constructor(sequelize, options = {}) { super(); this.sequelize = sequelize; this.sequelize = sequelize; this.tableName = options.tableName || "ratelimits"; this.tableAbuseName = options.tableAbuseName || `${this.tableName}abuses`; this.table = null; this.tableAbuses = null; } _getTable() { return __awaiter(this, void 0, void 0, function* () { if (!this.table) { this.table = this.sequelize.define(this.tableName, tableOption[0], tableOption[1]); yield this.table.sync(); } return this.table; }); } _getTableAbuse() { return __awaiter(this, void 0, void 0, function* () { if (!this.tableAbuses) { this.tableAbuses = this.sequelize.define(this.tableAbuseName, tableAbuseOption[0], tableAbuseOption[1]); yield this.tableAbuses.sync(); } return this.tableAbuses; }); } _increment(table, where, nb = 1, field) { return __awaiter(this, void 0, void 0, function* () { return table.update({ [field]: global.sequelize.literal(`${field} + ${nb}`) }, { where }); }); } // remove all if time is passed _removeAll(table) { return __awaiter(this, void 0, void 0, function* () { const now = new Date(); yield table.destroy({ where: { date_end: { $lte: now.getTime() }, }, }); }); } incr(key, options, weight) { return __awaiter(this, void 0, void 0, function* () { const table = yield this._getTable(); yield this._removeAll(table); const now = new Date(); const data = yield table.findOrCreate({ where: { key }, defaults: { key, date_end: now.getTime() + Time_1.Time.toMs(options.interval), }, }); yield this._increment(table, { key }, weight, "counter"); return { counter: data[0].counter + weight, dateEnd: data[0].date_end, }; }); } decrement(key, options, weight) { return __awaiter(this, void 0, void 0, function* () { const table = yield this._getTable(); yield this._increment(table, { key }, -weight, "counter"); }); } saveAbuse(options) { return __awaiter(this, void 0, void 0, function* () { const table = yield this._getTable(); const ratelimit = yield table.findOne({ where: { key: options.key } }); if (ratelimit) { const tableAbuse = yield this._getTableAbuse(); // eslint-disable-next-line const date_end = ratelimit.date_end; // create if not exist yield tableAbuse .findOrCreate({ where: { key: options.key, date_end }, defaults: { key: options.key, prefix: options.prefixKey, interval: options.interval, nb_max: options.max, nb_hit: options.max, user_id: options.user_id, ip: options.ip, date_end, }, }) .catch(() => { }); yield this._increment(tableAbuse, { key: options.key, date_end }, 1, "nb_hit"); } }); } } exports.SequelizeStore = SequelizeStore; //# sourceMappingURL=SequelizeStore.js.map