Surface raw RCON reply bytes + Probe RCON button
"unparseable reply (34 bytes)" told us Plutonium answered but hid the actual bytes, forcing another guess-and-check round. rconStatusResult now emits a formatRawReply() hex + ASCII dump of the raw UDP payload on every bad-reply, persisted in a new last_reply_snippet column so it survives page loads. Wrong-password detection widens beyond the "bad rcon" phrasing to "(bad|invalid|wrong)…(rcon|password|pass)"; print-body replies that don't match get their first non-header line surfaced verbatim so at least *some* server-supplied text reaches the UI. Probe RCON button (POST /api/servers/[name]/probe-rcon) fires the same code path as the periodic sweep on demand, cuts the RCON iteration loop from 30s per attempt to sub-second, and refreshes the header/roster/ health when it succeeds. Snippet <details> auto-opens on bad-reply so the bytes are one glance away. migrateServers() picks up the new column automatically via the introspection path added last commit. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -76,6 +76,7 @@ export interface ServerRow {
|
||||
last_roster: string | null; // JSON array of RconPlayer, or null
|
||||
last_seen_at: string | null; // last successful RCON reply
|
||||
last_error: string | null; // cleared on success
|
||||
last_reply_snippet: string | null; // hex+ASCII of last bad-reply payload
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
@@ -126,7 +127,8 @@ export function setServerField(
|
||||
'last_gametype',
|
||||
'last_roster',
|
||||
'last_seen_at',
|
||||
'last_error'
|
||||
'last_error',
|
||||
'last_reply_snippet'
|
||||
];
|
||||
if (!allowed.includes(field)) throw new Error(`refusing to set ${field}`);
|
||||
db().prepare(`UPDATE servers SET ${field} = ? WHERE name = ?`).run(value, name);
|
||||
|
||||
Reference in New Issue
Block a user