// ── The SQL half of the configuration audit ─────────────────────────────── // // One table, two questions: record what a save did, and show an operator what // has been done to a server lately. // // Nothing here talks to a game. The game half is `sidecarClient`, and the two // are deliberately not mixed: this file is what remains true after the plugin // has been reloaded, rolled back, or lost. const core = require('../../core') /** * Records one save attempt — including the ones that never reached a file. * * A refusal is written for the same reason a success is: an operator asking why * a setting is not what they set has to be able to see that somebody tried and * was told no, and a table that only holds successes answers that question with * silence. */ async function recordWrite(row) { await core.query( `INSERT INTO rust_config_writes (server_id, path, plugin, reload_target, tier, user_id, outcome, reloaded, changes, version_before, version_after, detail) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ row.serverId, row.path, row.plugin || null, row.reloadTarget || null, row.tier || 'form', row.userId || null, row.outcome, row.reloaded ? 1 : 0, row.changes ? JSON.stringify(row.changes) : null, row.versionBefore || null, row.versionAfter || null, row.detail ? String(row.detail).slice(0, 500) : null, ], ) } /** * The recent history for one server, newest first. * * `changes` comes back parsed, and a row whose JSON will not parse comes back * with `null` rather than throwing — a corrupt audit row must not be able to * break the page that displays the rest of them. */ async function recentWrites(serverId, limit = 50) { const rows = await core.query( `SELECT id, server_id AS serverId, path, plugin, reload_target AS reloadTarget, tier, user_id AS userId, outcome, reloaded, changes, version_before AS versionBefore, version_after AS versionAfter, detail, created_at AS createdAt FROM rust_config_writes WHERE server_id = ? ORDER BY id DESC LIMIT ?`, [serverId, Math.max(1, Math.min(Number(limit) || 50, 200))], ) return rows.map((row) => ({ ...row, reloaded: Boolean(row.reloaded), changes: parseChanges(row.changes), })) } function parseChanges(raw) { if (!raw) return null try { return JSON.parse(raw) } catch { return null } } module.exports = { recordWrite, recentWrites, parseChanges }