feat(demo): refuse an event step at save with demoRefuses (MODULE_API 1.19.0)
DEMO_MODE.md §3.4 refuses a denied console command when the event is saved and when it runs. Saving is core's route, and core never asked a module about a step's params, so the save-time half had no seam (DM18). An event action may now declare demoRefuses(params), returning a sentence or null. With DEMO_MODE on, create, save and publish ask it for every step; the first sentence refuses the write 403 DEMO_LOCKED with that sentence as the message (and in errors). A hook that throws refuses too. Off the demo it is validated and never called, and the catalog strips it with the other callables. The events controller passes code and message through on create, update and publish, so the client's one DEMO_LOCKED toast fires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -42,6 +42,16 @@ const asId = (raw) => {
|
||||
return Number.isInteger(n) && n > 0 ? n : null
|
||||
}
|
||||
|
||||
/**
|
||||
* A refused save's body: the errors, plus `code` and `message` when the model
|
||||
* set them. The demo's step refusal does (DM18), so the client's one
|
||||
* `DEMO_LOCKED` toast fires as it does for a route lock.
|
||||
*/
|
||||
const refusal = (result) => ({
|
||||
errors: result.errors,
|
||||
...(result.code ? { code: result.code, message: result.message } : {}),
|
||||
})
|
||||
|
||||
/**
|
||||
* The shape a definition takes on the wire.
|
||||
*
|
||||
@@ -413,7 +423,7 @@ exports.listVersions = async (req, res) => {
|
||||
/** POST /api/v1/admin/events */
|
||||
exports.create = async (req, res) => {
|
||||
const result = await definitions.create(req.body, req.user.id, { role: req.user.role })
|
||||
if (!result.ok) return res.status(result.status || 400).json({ errors: result.errors })
|
||||
if (!result.ok) return res.status(result.status || 400).json(refusal(result))
|
||||
await activity.log({
|
||||
req,
|
||||
action: 'event.definition.created',
|
||||
@@ -427,7 +437,7 @@ exports.update = async (req, res) => {
|
||||
const id = asId(req.params.id)
|
||||
if (!id) return res.status(400).json({ error: 'bad event id' })
|
||||
const result = await definitions.save(id, req.body, req.user.id, { role: req.user.role })
|
||||
if (!result.ok) return res.status(result.status || 400).json({ errors: result.errors })
|
||||
if (!result.ok) return res.status(result.status || 400).json(refusal(result))
|
||||
await activity.log({
|
||||
req,
|
||||
action: 'event.definition.updated',
|
||||
@@ -441,7 +451,7 @@ exports.publish = async (req, res) => {
|
||||
const id = asId(req.params.id)
|
||||
if (!id) return res.status(400).json({ error: 'bad event id' })
|
||||
const result = await definitions.publish(id, req.user.id)
|
||||
if (!result.ok) return res.status(result.status || 400).json({ errors: result.errors })
|
||||
if (!result.ok) return res.status(result.status || 400).json(refusal(result))
|
||||
await activity.log({
|
||||
req,
|
||||
action: 'event.definition.published',
|
||||
|
||||
Reference in New Issue
Block a user