UNPKG

express-rate-limiter-ts

Version:

A powerful, flexible and modern rate limiter middleware for Express.js written in TypeScript.

150 lines (149 loc) 6.37 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()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PostgresStore = void 0; const pg_1 = require("pg"); // PostgresStore: Production için tam çalışan, asenkron, sayaç ve ban destekli bir store. class PostgresStore { constructor(options) { this.windowMs = options.windowMs; this.client = new pg_1.Client({ connectionString: options.connectionString }); this.connect(); } // Bağlantıyı başlat connect() { return __awaiter(this, void 0, void 0, function* () { yield this.client.connect(); // Gerekli tabloları oluştur (idempotent) yield this.client.query(` CREATE TABLE IF NOT EXISTS rate_limiter_counters ( key TEXT PRIMARY KEY, count INTEGER NOT NULL, expires_at BIGINT NOT NULL ); CREATE TABLE IF NOT EXISTS rate_limiter_bans ( key TEXT PRIMARY KEY, reason TEXT, expires_at BIGINT NOT NULL ); CREATE TABLE IF NOT EXISTS rate_limiter_stats ( route TEXT PRIMARY KEY, count INTEGER NOT NULL, last_triggered BIGINT NOT NULL ); `); }); } // Sayaç artırma ve limiti kontrol etme incr(key) { return __awaiter(this, void 0, void 0, function* () { yield this.connect(); const now = Date.now(); const expiresAt = now + this.windowMs; // UPSERT ile sayaç artır const res = yield this.client.query(`INSERT INTO rate_limiter_counters (key, count, expires_at) VALUES ($1, 1, $2) ON CONFLICT (key) DO UPDATE SET count = rate_limiter_counters.count + 1, expires_at = $2 RETURNING count, expires_at`, [key, expiresAt]); return { current: res.rows[0].count, remaining: 0, // Kalan limit middleware'de hesaplanacak resetTime: new Date(Number(res.rows[0].expires_at)), exceeded: false, }; }); } // Sayaç okuma get(key) { return __awaiter(this, void 0, void 0, function* () { yield this.connect(); const res = yield this.client.query('SELECT count, expires_at FROM rate_limiter_counters WHERE key = $1', [key]); if (res.rows.length === 0) return { count: 0, resetTime: Date.now() + this.windowMs }; return { count: res.rows[0].count, resetTime: Number(res.rows[0].expires_at) }; }); } // Sayaç sıfırlama reset(key) { return __awaiter(this, void 0, void 0, function* () { yield this.connect(); yield this.client.query('DELETE FROM rate_limiter_counters WHERE key = $1', [key]); }); } // İstatistik kaydı recordStat(route) { return __awaiter(this, void 0, void 0, function* () { yield this.connect(); const now = Date.now(); yield this.client.query(`INSERT INTO rate_limiter_stats (route, count, last_triggered) VALUES ($1, 1, $2) ON CONFLICT (route) DO UPDATE SET count = rate_limiter_stats.count + 1, last_triggered = $2`, [route, now]); }); } // İstatistikleri döndür getStats() { return __awaiter(this, void 0, void 0, function* () { yield this.connect(); const res = yield this.client.query('SELECT route, count, last_triggered FROM rate_limiter_stats'); const stats = {}; for (const row of res.rows) { stats[row.route] = { count: row.count, lastTriggered: new Date(Number(row.last_triggered)), }; } return stats; }); } // Ban uygula ban(key, seconds, reason) { return __awaiter(this, void 0, void 0, function* () { yield this.connect(); const expiresAt = Date.now() + seconds * 1000; yield this.client.query(`INSERT INTO rate_limiter_bans (key, reason, expires_at) VALUES ($1, $2, $3) ON CONFLICT (key) DO UPDATE SET reason = $2, expires_at = $3`, [key, reason, expiresAt]); }); } // Ban kontrolü isBanned(key) { return __awaiter(this, void 0, void 0, function* () { yield this.connect(); const res = yield this.client.query('SELECT reason, expires_at FROM rate_limiter_bans WHERE key = $1', [key]); if (res.rows.length && res.rows[0].expires_at > Date.now()) { return { banned: true, banExpiresAt: new Date(Number(res.rows[0].expires_at)), reason: res.rows[0].reason }; } return { banned: false }; }); } // Banlı anahtarları döndür getBanned() { return __awaiter(this, void 0, void 0, function* () { yield this.connect(); const now = Date.now(); const res = yield this.client.query('SELECT key, reason, expires_at FROM rate_limiter_bans WHERE expires_at > $1', [now]); const result = {}; for (const row of res.rows) { result[row.key] = { banExpiresAt: new Date(Number(row.expires_at)), reason: row.reason }; } return result; }); } // Ban kaldır unban(key) { return __awaiter(this, void 0, void 0, function* () { yield this.connect(); yield this.client.query('DELETE FROM rate_limiter_bans WHERE key = $1', [key]); yield this.client.query('DELETE FROM rate_limiter_counters WHERE key = $1', [key]); }); } } exports.PostgresStore = PostgresStore;