Security page, admin quick controls, and whitelist reload self-healing
- RCON client retries once on a fresh connection after stale-socket timeouts (panel idle overnight held dead sockets, so 'whitelist reload' silently failed and approvals needed a server restart) - Auth watcher verifies pending players against the LIVE whitelist every 5s and re-writes/reloads if the server missed it, refunding lost window time - Log watcher records blocked join attempts (name + IP) with rate- limited notifications — scanner bots are now visible in the panel - New Security page: blocked-attempt log with one-click IP ban, banned players/IPs management, kick-everyone lockdown - Player detail modal: ban, op/deop, gamemode switching - Overview quick controls: time/weather/difficulty, gamerule toggles (keepInventory, mobGriefing, PvP...), broadcast box - Settings toggles for blocked-attempt notifications (panel + Discord) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4fe304c5af
commit
deeeaf0042
13 files changed
+809
-3
No files matched your search
@@ -8,6 +8,7 @@ import {
|
||||
whitelistAdd,
|
||||
whitelistRemove,
|
||||
whitelistList,
|
||||
whitelistLive,
|
||||
kickPlayer,
|
||||
easyauthList,
|
||||
easyauthRemove,
|
||||
@@ -174,6 +175,26 @@ async function reconcile() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Self-heal: make sure the server's LIVE whitelist actually contains every
|
||||
// pending player. A `whitelist reload` can get lost (stale RCON connection,
|
||||
// server restart mid-approval) — without this check the player sits at
|
||||
// "You are not white-listed" while their window burns down.
|
||||
try {
|
||||
const live = new Set((await whitelistLive()).map((n) => n.toLowerCase()));
|
||||
const missing = pending.filter((p) => !live.has(p.username.toLowerCase()));
|
||||
if (missing.length > 0) {
|
||||
for (const p of missing) await whitelistAdd(p.username); // rewrites file + reloads
|
||||
// They couldn't have joined yet — give the windows back the lost time.
|
||||
db()
|
||||
.prepare("UPDATE approved_players SET window_expires_at = window_expires_at + 5000 WHERE status = 'pending'")
|
||||
.run();
|
||||
logActivity("whitelist_heal", null, missing.map((m) => m.username).join(", "));
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
// whitelist list unavailable — fall through and try again next tick
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
for (const p of pending) {
|
||||
if (registered.has(p.username.toLowerCase())) {
|
||||
|
||||
Reference in new issue
Block a user