// ── SQL for zone presets, and what each server said about its zone mods ────── // // The presets are the module's own (PLAN_REDESIGNS §3.1, D195). What a server // has — its ZoneManager's flag list (D211), ZoneDomes and the domes helper (D194) // — is read out of the hello each server's state row keeps whole (`raw`), so the // preset form works while a server is off. const core = require('../../core') const PRESETS = 'rust_zone_presets' const PRESET_SERVERS = 'rust_zone_preset_servers' const SERVERS = 'rust_servers' const STATE = 'rust_server_state' /** The driver hands JSON_EXTRACT and TEXT back as strings. A bad value is no value. */ function json(value, fallback) { if (value === null || value === undefined) return fallback if (typeof value !== 'string') return value try { return JSON.parse(value) } catch { return fallback } } function shape(row, serverIds) { return { id: Number(row.id), name: row.name, flags: json(row.flags, []), settings: json(row.settings, {}), allServers: Boolean(row.allServers), servers: serverIds, updatedAt: row.updatedAt ? new Date(row.updatedAt).toISOString() : null, } } /** Every preset, with the servers each names, oldest first. */ async function listPresets() { const [rows, links] = await Promise.all([ core.query( `SELECT id, name, flags, settings, all_servers AS allServers, updated_at AS updatedAt FROM ${PRESETS} ORDER BY name ASC, id ASC`, ), core.query(`SELECT preset_id AS presetId, server_id AS serverId FROM ${PRESET_SERVERS} ORDER BY server_id ASC`), ]) const by = new Map() for (const l of links) { const id = Number(l.presetId) if (!by.has(id)) by.set(id, []) by.get(id).push(l.serverId) } return rows.map((r) => shape(r, by.get(Number(r.id)) || [])) } async function getPreset(id) { const rows = await core.query( `SELECT id, name, flags, settings, all_servers AS allServers, updated_at AS updatedAt FROM ${PRESETS} WHERE id = ?`, [id], ) if (!rows[0]) return null const links = await core.query(`SELECT server_id AS serverId FROM ${PRESET_SERVERS} WHERE preset_id = ? ORDER BY server_id ASC`, [id]) return shape(rows[0], links.map((l) => l.serverId)) } /** * Writes one preset and replaces its server list. The model has checked every * server exists first, so the inserts below have nothing left to refuse. The * list is written in one statement, so a preset is never left half-listed. */ async function savePreset({ id = null, name, flags, settings, allServers, servers }, userId = null) { let presetId = id if (presetId) { await core.query( `UPDATE ${PRESETS} SET name = ?, flags = ?, settings = ?, all_servers = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`, [name, JSON.stringify(flags), JSON.stringify(settings), allServers ? 1 : 0, presetId], ) await core.query(`DELETE FROM ${PRESET_SERVERS} WHERE preset_id = ?`, [presetId]) } else { const res = await core.query( `INSERT INTO ${PRESETS} (name, flags, settings, all_servers, created_by) VALUES (?, ?, ?, ?, ?)`, [name, JSON.stringify(flags), JSON.stringify(settings), allServers ? 1 : 0, userId], ) presetId = Number(res.insertId) } if (!allServers && servers.length > 0) { await core.query( `INSERT INTO ${PRESET_SERVERS} (preset_id, server_id) VALUES ${servers.map(() => '(?, ?)').join(', ')}`, servers.flatMap((serverId) => [presetId, serverId]), ) } return presetId } async function deletePreset(id) { await core.query(`DELETE FROM ${PRESETS} WHERE id = ?`, [id]) } /** * Each configured server, in the operator's order, with what its last hello said * about ZoneManager (`{ version, flags }`) and ZoneDomes (`{ loaded, version, * helper, ready }`). Either is null for a server that never said: no ZoneManager, * a plugin before protocol 13, or a server never reached. */ async function listZoneServers() { const rows = await core.query( `SELECT s.id, s.name, s.enabled, JSON_EXTRACT(st.raw, '$.zoneManager') AS zoneManager, JSON_EXTRACT(st.raw, '$.integrations.zoneDomes') AS zoneDomes FROM ${SERVERS} s LEFT JOIN ${STATE} st ON st.server_id = s.id ORDER BY s.sort_order ASC, s.id ASC`, ) return rows.map((r) => { const zm = json(r.zoneManager, null) const zd = json(r.zoneDomes, null) return { id: r.id, name: r.name, enabled: Boolean(r.enabled), zoneManager: zm && typeof zm === 'object' ? { version: typeof zm.version === 'string' ? zm.version : null, flags: Array.isArray(zm.flags) ? zm.flags.filter((f) => typeof f === 'string') : null, } : null, zoneDomes: zd && typeof zd === 'object' ? { loaded: zd.loaded === true, version: typeof zd.version === 'string' ? zd.version : null, helper: zd.helper && typeof zd.helper.state === 'string' ? zd.helper : null, ready: zd.ready === true, } : null, } }) } module.exports = { PRESETS, PRESET_SERVERS, listPresets, getPreset, savePreset, deletePreset, listZoneServers }