feat(workflows): approval gates — email confirm/deny + token resume

gate_setup action creates a workflow_approvals row (single-use token stored
as SHA-256 hash) and emails the admin confirm/deny links immediately
(internal mail, no business-hours floor). actByToken / actById finalize the
approval and resume the run down the matching confirm/deny edge; both are
idempotent (a second click → 'already recorded') and respect expiry. Public
GET /api/public/workflow-approvals/:token/:action returns a small HTML
confirmation page (clickable from email, single-use so prefetch can't
double-act). listPending backs the webview inbox (wired in the CRUD phase).
Test covers gate→approval→email→token-confirm→resume + idempotency.
This commit is contained in:
Luca
2026-06-23 02:37:20 +02:00
parent cc0ba5347d
commit b48d8c2eb8
5 changed files with 226 additions and 0 deletions
+1
View File
@@ -718,6 +718,7 @@ app.use('/api/admin/dev', require('./src/routes/adminDev'));
app.use('/api/public/quotes', require('./src/routes/publicQuotes'));
app.use('/api/public/contracts', require('./src/routes/publicContracts'));
app.use('/api/public/payment-check', require('./src/routes/publicPaymentCheck'));
app.use('/api/public/workflow-approvals', require('./src/routes/publicWorkflowApprovals'));
app.use('/api/admin/event-types', require('./src/routes/adminEventTypes'));
app.use('/api/admin/api-tokens', require('./src/routes/adminApiTokens'));
app.use('/api/admin/webhooks', require('./src/routes/adminWebhooks'));