Add Bot Activity admin panel: banned-IP view + recent events + emergency unban

Expose the botScore middleware's in-memory scoring/ban state to admins.
Previously state lived only in the store Map with no persistence or API — the
only visibility was tailing container logs.

- botScore: bounded ring buffer (300) recording scan/login-fail/honeypot and
  ban events (most-recent-first); listState() snapshot of all scored IPs;
  unban() to clear a single IP.
- New admin-only endpoints GET /admin/bot-activity and
  POST /admin/bot-activity/unban (RBAC admin gate, IP validated). Unban is
  activity-logged with the admin username.
- Bot Activity tab: currently-banned table with Unban, plus a recent-events
  feed, following the existing admin table patterns.
- Tests for the buffer, listState, and unban (guard lets an unbanned IP back
  through). README updated.

Read + emergency-unban only — no ban-add or weight-editing surface. Buffer is
in-memory, matching the store; not persisted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-03 02:31:25 -05:00
parent 58852a5078
commit 870971fc12
9 changed files with 341 additions and 6 deletions

View File

@@ -61,7 +61,7 @@ UOMSITE/
│ ├─ src/
│ │ ├─ routes/public/ Portal, Website, News, Screenshots, FiveOnFriday, Newsletter(+Issue), Status, About, Maintenance
│ │ ├─ routes/wiki/ Wiki landing + WikiArticle
│ │ ├─ routes/admin/ AdminLogin, AdminLayout, views/ (Dashboard, Posts, Wiki, Settings, Activity, Users, Account) + editors
│ │ ├─ routes/admin/ AdminLogin, AdminLayout, views/ (Dashboard, Posts, Wiki, Settings, Activity, Bot Activity, Users, Account) + editors
│ │ ├─ components/ SiteHeader, SiteFooter, layout, guards, Modal, …
│ │ ├─ contexts/ AuthContext, SiteContext
│ │ ├─ api/client.js fetch wrapper (sends cookies)
@@ -187,6 +187,7 @@ npm start # node server → serves API + SPA at http://localhost:3
| `/admin/wiki` | Wiki pages CRUD |
| `/admin/settings` | Site settings |
| `/admin/activity` | Activity log |
| `/admin/bot-activity` | Bot activity — banned IPs + recent scoring events, emergency unban (admin only) |
| `/admin/users` | User management |
| `/admin/account` | Account security (self-service TOTP two-factor) |
@@ -198,7 +199,7 @@ npm start # node server → serves API + SPA at http://localhost:3
|---|---|---|
| Auth | `/api/v1/auth` (`login`, `login/totp`, `logout`, `me`) | cookie |
| Public | `/api/v1/public` (`settings`, `status`, `posts/:category`, `posts/:category/:idOrSlug`, `wiki`, `wiki/:slug`, `contact`) | none |
| Admin | `/api/v1/admin` (`dashboard`, `site-mode`, `posts`, `posts/upload`, `wiki`, `settings`, `activity`, `users`, `account`, `account/totp/*`) | cookie (admin) |
| Admin | `/api/v1/admin` (`dashboard`, `site-mode`, `posts`, `posts/upload`, `wiki`, `settings`, `activity`, `bot-activity`, `bot-activity/unban`, `users`, `account`, `account/totp/*`) | cookie (admin) |
Post categories (URL form): `news`, `five-on-friday`, `newsletter`, `screenshots`.
See [BACKEND_DESIGN.md](BACKEND_DESIGN.md) §4 for the full contract.
@@ -254,7 +255,9 @@ Copy `.env.example` (Compose) or `server/.env.example` (local) and fill in. **`.
- **Honeypot** field on the login form; submissions that fill it are treated as bots.
- **Bot-scoring + automatic IP ban** — weighted scoring of CMS-scanner paths and junk 404s (with a
periodic sweep of stale entries) bans hostile scanners; failed logins and honeypot hits feed the
score.
score. Admins get visibility into this on the **Bot Activity** panel: currently banned IPs and a
recent-events feed (in-memory, most-recent-first), plus a logged emergency **unban** for false
positives — read + unban only, not a scoring-config surface.
**Uploads & input**