feat(rust): mod configuration from the site, and an editor that will not rewrite a float
R18's two tiers: a form generated from a config file's own values, and raw JSON for what a form cannot express. Admin → Rust mod config, one live round trip per action, nothing cached between a browser and a game host's disk. `configEdit.js` is the part that could not be done naively. JavaScript cannot tell `1` from `1.0`, and both mod frameworks deserialize a config into typed C# classes — so a read-modify-write silently rewrites every whole-numbered float as an integer on fields nobody touched, and a plugin that then throws at load does not come back. It never parses, mutates and re-serialises: it records the SOURCE SPAN of every value and splices literals into them, so an untouched `1.0` is still `1.0` and a number an admin types travels as text the whole way (D35/D36). The bridge's own config is editable with `Host`, `Port` and `ServerId` locked, in the form and in the raw tier, because either would cut the link carrying the edit or strand every row this site holds (D38). Credentials render masked with a reveal; the raw tier shows them (D37) and the audit trail never does. `rust_config_writes` records every save including the refused and the rolled back — an operator asking why a setting is not what they set needs to see that somebody tried. Three defects a browser walk found that 179 green tests did not: * every save of the bridge's own config was refused while the page said the opposite — a `<select>` whose value matches no `<option>` shows the first one, so the reload guess `RunicGateway` was on the wire and "nothing" was on the screen; * `btn ghost` is not a class this platform defines (`.btn-ghost` is), so every secondary button in this module has rendered as a primary one since phase 7 — here it made the open file and the active tier indistinguishable; * a save's refusal rendered at the top of a long form, far from the button. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PMH6bw1jXMgbyF3ZWGEzSM
This commit is contained in:
@@ -22,9 +22,10 @@ import Servers from './routes/public/Servers.jsx'
|
||||
import ServerDetail from './routes/public/ServerDetail.jsx'
|
||||
import Account from './routes/player/Account.jsx'
|
||||
import Permissions from './routes/admin/Permissions.jsx'
|
||||
import ModConfig from './routes/admin/ModConfig.jsx'
|
||||
import UserRustSections from './routes/admin/UserRustSections.jsx'
|
||||
import FooterStatus from './components/FooterStatus.jsx'
|
||||
import { IconKey, IconLink } from './icons.jsx'
|
||||
import { IconKey, IconLink, IconSliders } from './icons.jsx'
|
||||
|
||||
// The module id, exactly as `module.json` spells it. Core keys the registry by it
|
||||
// and prefixes every route path with it.
|
||||
@@ -82,7 +83,18 @@ registry.registerRoutes(ID, {
|
||||
{ path: 'servers/:id', element: <ServerDetail /> },
|
||||
],
|
||||
player: [{ path: '', element: <Account /> }],
|
||||
admin: [{ path: '', element: <Permissions /> }],
|
||||
admin: [
|
||||
{ path: '', element: <Permissions /> },
|
||||
// Phase 7b (R18). A second admin page rather than a tab on the first: the
|
||||
// permission mirror decides who may do what inside the game, and this edits
|
||||
// the game host's own files. They are neighbours, not halves of one screen,
|
||||
// and the nav says so with two rows.
|
||||
//
|
||||
// A static segment under the module's namespace, so it lands at
|
||||
// `/admin/rust/config` and core's admin gate applies to it exactly as it
|
||||
// does to the page above.
|
||||
{ path: 'config', element: <ModConfig /> },
|
||||
],
|
||||
})
|
||||
|
||||
// ── Nav ───────────────────────────────────────────────────────────────────
|
||||
@@ -127,7 +139,10 @@ registry.registerNav(ID, {
|
||||
// every sidebar row, and the one without is the only text in a column of glyphs.
|
||||
registry.registerNav(ID, {
|
||||
area: 'admin',
|
||||
items: [{ label: 'Rust permissions', to: '/admin/rust', icon: IconKey }],
|
||||
items: [
|
||||
{ label: 'Rust permissions', to: '/admin/rust', icon: IconKey },
|
||||
{ label: 'Rust mod config', to: '/admin/rust/config', icon: IconSliders },
|
||||
],
|
||||
})
|
||||
|
||||
// ── Extension slots ───────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user