express-rate-limiter-ts
Version:
A powerful, flexible and modern rate limiter middleware for Express.js written in TypeScript.
123 lines (122 loc) • 5.21 kB
JavaScript
"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.RedisStore = void 0;
const ioredis_1 = __importDefault(require("ioredis"));
// RedisStore: Production için tam çalışan, asenkron, TTL ve ban destekli bir store.
class RedisStore {
constructor(options) {
this.statsKey = 'rateLimiter:stats';
this.banPrefix = 'rateLimiter:ban:';
this.countPrefix = 'rateLimiter:count:';
this.windowMs = options.windowMs;
this.client = new ioredis_1.default(options.url);
}
// Sayaç artırma ve limiti kontrol etme (asenkron)
incr(key) {
return __awaiter(this, void 0, void 0, function* () {
const redisKey = this.countPrefix + key;
// Sayaç artırılır, anahtar yoksa TTL atanır
const current = yield this.client.incr(redisKey);
if (current === 1) {
yield this.client.pexpire(redisKey, this.windowMs);
}
const ttl = yield this.client.pttl(redisKey);
return {
current,
remaining: 0, // Kalan limit middleware'de hesaplanacak
resetTime: new Date(Date.now() + (ttl > 0 ? ttl : this.windowMs)),
exceeded: false,
};
});
}
// Sayaç okuma (asenkron)
get(key) {
return __awaiter(this, void 0, void 0, function* () {
const redisKey = this.countPrefix + key;
const count = parseInt((yield this.client.get(redisKey)) || '0', 10);
const ttl = yield this.client.pttl(redisKey);
return { count, resetTime: Date.now() + (ttl > 0 ? ttl : this.windowMs) };
});
}
// Sayaç sıfırlama
reset(key) {
return __awaiter(this, void 0, void 0, function* () {
yield this.client.del(this.countPrefix + key);
});
}
// İstatistik kaydı (isteğe bağlı)
recordStat(route) {
return __awaiter(this, void 0, void 0, function* () {
yield this.client.hincrby(this.statsKey, route, 1);
yield this.client.hset(this.statsKey + ':last', route, Date.now());
});
}
// İstatistikleri döndür
getStats() {
return __awaiter(this, void 0, void 0, function* () {
const counts = yield this.client.hgetall(this.statsKey);
const lasts = yield this.client.hgetall(this.statsKey + ':last');
const stats = {};
for (const route in counts) {
stats[route] = {
count: parseInt(counts[route], 10),
lastTriggered: new Date(Number(lasts[route]) || Date.now()),
};
}
return stats;
});
}
// Ban uygula
ban(key, seconds, reason) {
return __awaiter(this, void 0, void 0, function* () {
yield this.client.set(this.banPrefix + key, reason, 'EX', seconds);
});
}
// Ban kontrolü
isBanned(key) {
return __awaiter(this, void 0, void 0, function* () {
const reason = yield this.client.get(this.banPrefix + key);
if (reason) {
const ttl = yield this.client.ttl(this.banPrefix + key);
return { banned: true, banExpiresAt: new Date(Date.now() + ttl * 1000), reason };
}
return { banned: false };
});
}
// Banlı anahtarları döndür (tümünü tarar, büyük veri için optimize edilmeli)
getBanned() {
return __awaiter(this, void 0, void 0, function* () {
const keys = yield this.client.keys(this.banPrefix + '*');
const result = {};
for (const fullKey of keys) {
const key = fullKey.replace(this.banPrefix, '');
const reason = yield this.client.get(fullKey);
const ttl = yield this.client.ttl(fullKey);
if (reason && ttl > 0) {
result[key] = { banExpiresAt: new Date(Date.now() + ttl * 1000), reason };
}
}
return result;
});
}
// Ban kaldır
unban(key) {
return __awaiter(this, void 0, void 0, function* () {
yield this.client.del(this.banPrefix + key);
yield this.client.del(this.countPrefix + key);
});
}
}
exports.RedisStore = RedisStore;