express-rate-limiter-ts
Version:
A powerful, flexible and modern rate limiter middleware for Express.js written in TypeScript.
183 lines (182 loc) • 6.45 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.startAdminPanel = startAdminPanel;
exports.getRateLimiterStats = getRateLimiterStats;
const express_1 = __importDefault(require("express"));
function startAdminPanel(store) {
const app = (0, express_1.default)();
app.use(express_1.default.json());
// JSON API endpoints
app.get('/api/stats', async (_req, res) => {
res.json(await store.getStats());
});
app.get('/api/banned', async (_req, res) => {
res.json(await store.getBanned());
});
app.post('/api/unban', async (req, res) => {
const { key } = req.body;
if (key) {
await store.unban(key);
res.json({ success: true });
}
else {
res.status(400).json({ success: false, message: 'Key not specified.' });
}
});
app.get('/', async (_req, res) => {
const stats = await getRateLimiterStats(store);
const bannedKeys = Object.keys(stats.banned);
res.send(`
<html lang=\"en\">
<head>
<meta charset=\"UTF-8\" />
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />
<title>WAFGuard - Rate Limiter - Management Panel</title>
<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">
<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>
<link href=\"https://fonts.googleapis.com/css2?family=Montserrat:wght@700;400&display=swap\" rel=\"stylesheet\">
<style>
body {
background: #fff;
color: #111;
font-family: 'Montserrat', Arial, sans-serif;
margin: 0;
padding: 0;
}
.header {
display: flex;
align-items: center;
background: #111;
color: #fff;
padding: 24px 32px 18px 32px;
font-size: 2.2rem;
font-weight: 700;
letter-spacing: 1px;
}
.logo {
width: 48px;
height: 48px;
margin-right: 18px;
background: #fff;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
font-weight: bold;
color: #111;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}
.container {
max-width: 900px;
margin: 32px auto;
padding: 24px;
background: #fff;
border-radius: 16px;
box-shadow: 0 2px 16px rgba(0,0,0,0.07);
}
h2 {
color: #111;
margin-top: 32px;
margin-bottom: 12px;
font-size: 1.3rem;
font-weight: 700;
letter-spacing: 0.5px;
}
.stat-box, .ban-box {
background: #f7f7f7;
border-radius: 8px;
padding: 16px;
font-size: 1rem;
margin-bottom: 24px;
font-family: 'Fira Mono', 'Consolas', monospace;
color: #222;
overflow-x: auto;
border: 1px solid #ccc;
}
.ban-box {
border-left: 4px solid #111;
}
.refresh-btn {
background: #111;
color: #fff;
border: none;
border-radius: 6px;
padding: 10px 22px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
margin-bottom: 18px;
transition: background 0.2s;
}
.refresh-btn:hover {
background: #333;
}
.unban-btn {
background: #e53935;
color: #fff;
border: none;
border-radius: 6px;
padding: 6px 18px;
font-size: 1rem;
font-weight: 700;
cursor: pointer;
margin-left: auto;
margin-bottom: 4px;
transition: background 0.2s, color 0.2s;
box-shadow: 0 2px 8px rgba(229,57,53,0.08);
display: inline-block;
}
.unban-btn:hover {
background: #b71c1c;
color: #fff;
}
</style>
</head>
<body>
<div class=\"header\">
<img class=\"logo\" src=\"https://wafguard.com/logo.png\" alt=\"WAFGuard Logo\" />
WAFGuard <span style=\"font-size:1.1rem;font-weight:400;margin-left:12px;opacity:0.7;\">Rate Limiter - Management Panel</span>
</div>
<div class=\"container\">
<button class=\"refresh-btn\" onclick=\"window.location.reload()\">Refresh</button>
<h2>Statistics</h2>
<div class=\"stat-box\">${Object.keys(stats.stats).length ? `<pre>${JSON.stringify(stats.stats, null, 2)}</pre>` : 'No data found.'}</div>
<h2>Banned</h2>
<div class=\"ban-box\">
${bannedKeys.length ? bannedKeys.map(key => `
<div style=\"margin-bottom:8px;display:flex;align-items:center;justify-content:space-between;\">
<pre style=\"margin:0;display:inline-block;\">${key}: ${JSON.stringify(stats.banned[key], null, 2)}</pre>
<button class=\"unban-btn\" onclick=\"unban('${key}')\">Unban</button>
</div>
`).join('') : 'There are no bans at the moment.'}
</div>
</div>
<script>
function unban(key) {
fetch('/api/unban', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key })
}).then(() => window.location.reload());
}
</script>
</body>
</html>
`);
});
app.listen(9222, () => {
// eslint-disable-next-line no-console
console.log('Rate Limiter Admin Panel is running at http://localhost:9222');
});
}
async function getRateLimiterStats(store) {
return {
stats: await store.getStats(),
banned: await store.getBanned(),
};
}