Core half of the public demo (docs/website/DEMO_MODE.md §10 step 1, website#222).
Nothing here runs unless DEMO_MODE=true.
- server/src/demo/: the lock at /api/v1, ahead of every router. It covers every
Tier A row of §3.3, the shared-login conditions, POST /auth/register (DM16),
DELETE /auth/me/sessions/:id on a demo account (DM17), and the PUT
/admin/settings keys that reach a Tier A lock. Any multipart request is refused
app-wide. noindex and robots.txt. A condition that cannot decide refuses.
- Loader: demoLocks in module.json (writes only, under the module's own mounts),
ctx.demoMode, and api.onDemoSeed (DM15). MODULE_API 1.18.0 on both halves.
- GET /public/settings carries demo: { resetsAt, logins } on the demo only.
- npm run seed:demo -- <fixture>: upserts the four accounts, sets the site live
with registration disabled, seeds content on the first run only, calls each
module's onDemoSeed, and stamps demo_state. It resolves $env: strings and
refuses to run off the demo.
- Client: the banner, one DEMO_LOCKED toast fed from the request primitive (so
module screens get it too), and the published logins on both sign-in pages.
Dashboard and register say the demo message instead of blaming a role.
- OpenAPI: DemoLocked schema, the demo block, and a note that any write may
answer DEMO_LOCKED on the demo.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
38 lines
1.4 KiB
JavaScript
38 lines
1.4 KiB
JavaScript
// ── Without DEMO_MODE, none of the demo is mounted (DEMO_MODE.md §2) ───────
|
|
//
|
|
// The other half of demoApp.test.js, in its own process because app.js reads
|
|
// the flag at require time. A normal shard must answer exactly as it did before
|
|
// demo mode existed: its own auth check, no noindex header, its own robots.txt.
|
|
process.env.DB_HOST = '127.0.0.1'
|
|
process.env.DB_PORT = '59999'
|
|
delete process.env.DEMO_MODE
|
|
process.env.MODULES_DIR = require('path').join(require('os').tmpdir(), 'rg-demo-app-no-modules')
|
|
|
|
const { test, after } = require('node:test')
|
|
const assert = require('node:assert/strict')
|
|
|
|
const app = require('../src/app')
|
|
const db = require('../src/utils/db')
|
|
|
|
after(() => db.close())
|
|
|
|
test('a route the demo locks answers its own 401 on a normal deployment', async () => {
|
|
const server = await new Promise((resolve) => {
|
|
const s = app.listen(0, '127.0.0.1', () => resolve(s))
|
|
})
|
|
try {
|
|
const base = `http://127.0.0.1:${server.address().port}`
|
|
const res = await fetch(`${base}/api/v1/admin/site-mode`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ mode: 'maintenance' }),
|
|
})
|
|
assert.equal(res.status, 401)
|
|
const health = await fetch(`${base}/api/health`)
|
|
assert.equal(health.headers.get('x-robots-tag'), null)
|
|
} finally {
|
|
server.closeAllConnections()
|
|
await new Promise((resolve) => server.close(resolve))
|
|
}
|
|
})
|