Bans page: users-style table with badges, client-side pagination, manual ban modal (POST /api/bans with ip/reason/hours), unban row buttons
This commit is contained in:
@@ -77,14 +77,17 @@ function ipOf(req) {
|
||||
}
|
||||
|
||||
async function banIP(req, reason, ms) {
|
||||
const ip = ipOf(req);
|
||||
await banIpAddr(ipOf(req), reason, ms, req);
|
||||
}
|
||||
|
||||
async function banIpAddr(ip, reason, ms, actorReq) {
|
||||
const until = new Date(Date.now() + ms);
|
||||
banMemory.set(ip, { reason, banned_until: until.toISOString() });
|
||||
await pool.query(
|
||||
'INSERT INTO banned_ips (ip, reason, banned_until) VALUES ($1, $2, $3) ON CONFLICT (ip) DO UPDATE SET reason = $2, banned_until = $3',
|
||||
[ip, reason, until.toISOString()]
|
||||
);
|
||||
await logAudit(req, 'ip.ban', { ip, reason });
|
||||
await logAudit(actorReq, 'ip.ban', { ip, reason });
|
||||
console.log(`IP banned: ${ip} (${reason})`);
|
||||
}
|
||||
|
||||
@@ -703,6 +706,19 @@ app.get('/api/bans', requireAuth, requireAdmin, async (_, res) => {
|
||||
res.json(rows);
|
||||
});
|
||||
|
||||
app.post('/api/bans', requireAuth, requireAdmin, async (req, res) => {
|
||||
const ip = String(req.body?.ip || '').trim();
|
||||
if (!ip || ip.length > 64 || !/^[0-9a-fA-F:.]+$/.test(ip)) {
|
||||
return res.status(400).json({ error: 'Некорректный IP' });
|
||||
}
|
||||
const reason = (typeof req.body?.reason === 'string' && req.body.reason.trim())
|
||||
? req.body.reason.trim().slice(0, 100)
|
||||
: 'manual';
|
||||
const hours = Math.min(Math.max(parseInt(req.body?.hours, 10) || 24, 1), 24 * 30);
|
||||
await banIpAddr(ip, reason, hours * 60 * 60 * 1000, req);
|
||||
res.json({ ok: true, ip, reason, banned_until: banMemory.get(ip).banned_until });
|
||||
});
|
||||
|
||||
app.delete('/api/bans/:ip', requireAuth, requireAdmin, async (req, res) => {
|
||||
const ip = String(req.params.ip || '').trim();
|
||||
if (!ip || ip.length > 64 || !/^[0-9a-fA-F:.]+$/.test(ip)) {
|
||||
|
||||
Reference in New Issue
Block a user