feat(demo): the public demo's module-side locks and seed hook
website/DEMO_MODE.md §3.2, §3.4, §3.5, §7.3 and §7.5 (step 2 of §10). Nothing changes off the demo; everything below sits behind ctx.demoMode. - demoLocks in module.json: PUT and DELETE /admin/rust/servers/:id. - The console-command deny list (DM11): quit, restart, oxide.unload, oxide.reload, rcon.*, fps.limit and server.tickrate, by first word, with global. stripped and o. read as oxide. Refused at save through core's demoRefuses (MODULE_API 1.19.0, DM18) and again at run, with retry: false, before the rig is asked. Teardown commands too. - The config editor (DM14): the bridge's files are left out of the list and refused for reading, writing and in the write history, by name (canonicalised) and by the catalogue's self. Other plugins' credential fields are redacted in the text and the form and made read-only; raw-JSON saves are refused; a file that does not parse is withheld. The admin server list leaves out the sidecar URL. - onDemoSeed (DM15): upserts the rig's server row with an encrypted token and demo-player's stand-in Steam link. - The release's install manifest carries protocol, read from PROTOCOL_VERSION, for the wipe's bundle match (DM12). coreApi is ^1.19.0; ci/core-ref.json pins website#224's head. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -41,6 +41,7 @@ const core = require('./core')
|
||||
const { serverFor, transportError, pluginError } = require('./eventLeases')
|
||||
const { BUDGET_MS } = require('./eventWorld')
|
||||
const rewards = require('./eventRewards')
|
||||
const demo = require('./demo')
|
||||
|
||||
const log = core.logger('commands')
|
||||
|
||||
@@ -115,6 +116,9 @@ function checkConfirm(params, sentence) {
|
||||
/** The first command that names a placeholder, or null: only a reward has someone to fill one with. */
|
||||
const withPlaceholder = (lines) => lines.find((line) => PLACEHOLDER.test(line)) || null
|
||||
|
||||
/** A `multiple` param as a list: a bare value is a list of one, nothing is none. */
|
||||
const asList = (raw) => (raw === undefined || raw === null || raw === '' ? [] : Array.isArray(raw) ? raw : [raw])
|
||||
|
||||
/** A command list's price: at least one, so the switchboard sees the dimension. */
|
||||
function pricedCommands(raw) {
|
||||
const checked = checkCommands(raw, { required: false })
|
||||
@@ -190,6 +194,11 @@ async function checkRun(params) {
|
||||
const teardown = checkCommands(params.teardown, { field: 'teardown commands', required: false })
|
||||
if (!teardown.ok) return { ok: false, retry: false, error: teardown.error }
|
||||
|
||||
// The demo's deny list (DEMO_MODE.md §3.4), at run: core asked at save
|
||||
// (demoRefuses), but a definition can predate the list.
|
||||
const denied = demo.deniedCommand([...commands.value, ...teardown.value])
|
||||
if (denied) return { ok: false, retry: false, error: denied }
|
||||
|
||||
const placeholder = withPlaceholder([...commands.value, ...teardown.value])
|
||||
if (placeholder) {
|
||||
return { ok: false, retry: false, error: `{steamid} and {name} are filled in only by Reward by console command, and "${placeholder}" has one` }
|
||||
@@ -324,6 +333,10 @@ const commandRun = {
|
||||
|
||||
revert: revertRun,
|
||||
|
||||
// The demo's deny list at save (MODULE_API 1.19.0, DM18). Core asks only on the
|
||||
// demo; null lets the step through.
|
||||
demoRefuses: (p) => demo.deniedCommand([...asList(p.commands), ...asList(p.teardown)]),
|
||||
|
||||
// The site cannot ask a server whether a command's effect is still there, so
|
||||
// a teardown not yet run is always in force: "I do not know" is never "gone".
|
||||
async reconcile({ resources }) {
|
||||
@@ -397,10 +410,15 @@ const commandReward = {
|
||||
|
||||
cost: (p) => ({ 'rust.commands': pricedCommands(p.commands) * pricedPeople(p) }),
|
||||
|
||||
demoRefuses: (p) => demo.deniedCommand(asList(p.commands)),
|
||||
|
||||
async perform({ runId, stepId, idempotencyKey, params, verify }) {
|
||||
const commands = checkCommands(params.commands)
|
||||
if (!commands.ok) return { ok: false, retry: false, error: commands.error }
|
||||
|
||||
const denied = demo.deniedCommand(commands.value)
|
||||
if (denied) return { ok: false, retry: false, error: denied }
|
||||
|
||||
const confirm = checkConfirm(params, REWARD_SURE)
|
||||
if (!confirm.ok) return { ok: false, retry: false, error: confirm.error }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user