| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- /**
- * @node form-trigger
- * @name Form
- * @category triggers
- * @version 1.0.0
- * @description Publishes an HTML form at a shareable URL. Submitting it starts the workflow.
- * @trigger
- * @icon clipboard-list
- */
- const configSchema = {
- type: 'object',
- properties: {
- title: {
- type: 'string',
- title: 'Form Title',
- description: 'Shown as the heading of the page',
- default: 'Untitled form'
- },
- description: {
- type: 'string',
- title: 'Description',
- description: 'Shown under the heading. Plain text - it is escaped, not rendered as HTML.',
- default: ''
- },
- fields: {
- type: 'array',
- title: 'Fields',
- description: 'The questions on the form, in order.',
- default: [],
- items: {
- type: 'object',
- properties: {
- name: { type: 'string', title: 'Name',
- description: 'The key the workflow reads, e.g. "image" for $node[\'Form\'].form.image. Letters, digits and underscore, not starting with a digit.' },
- label: { type: 'string', title: 'Label', description: 'Shown next to the input' },
- type: { type: 'string', title: 'Type',
- enum: ['text', 'textarea', 'number', 'select', 'checkbox', 'date', 'file'],
- default: 'text' },
- required: { type: 'boolean', title: 'Required', default: false },
- placeholder: { type: 'string', title: 'Placeholder', default: '' },
- options: { type: 'array', title: 'Options', description: 'Choices, for a Select field',
- items: { type: 'string' }, default: [] },
- accept: { type: 'string', title: 'Accept',
- description: 'For a File field, e.g. "image/*". A browser hint only - it is not enforced.',
- default: '' },
- maxSizeMb: { type: 'number', title: 'Max Size (MB)',
- description: 'For a File field. 0 uses the server limit.', default: 0 }
- }
- }
- },
- password: {
- type: 'string',
- title: 'Password',
- format: 'password',
- description: 'Leave empty for anyone with the link. This is stored readable in the workflow, so treat it as a gate against a forwarded link, not as a secret.',
- default: ''
- },
- responseMode: {
- type: 'string',
- title: 'After Submit',
- enum: ['immediate', 'wait'],
- description: 'Immediate returns a thank-you page at once and lets the run continue - needed for anything taking more than about 30 seconds. Wait holds the browser until the workflow finishes, so a Respond to Webhook node can answer.',
- default: 'immediate'
- },
- responseMessage: {
- type: 'string',
- title: 'Thank-you Message',
- default: 'Thanks, your answer was received.',
- 'x-if': 'responseMode'
- },
- path: {
- type: 'string',
- title: 'Path Filter',
- description: 'Optional path suffix. Leave empty to answer at the bare form URL.',
- default: ''
- }
- }
- };
- const inputSchema = { type: 'object', properties: {} };
- const outputSchema = {
- type: 'object',
- properties: {
- form: { type: 'object', description: 'The answers, keyed by field name. A file field holds a binary object.' },
- submittedAt: { type: 'number', description: 'Milliseconds since the epoch' },
- clientIp: { type: 'string' }
- }
- };
- async function execute(config, input, context) {
- // Every value here is built by the webhook controller, which has already
- // validated it. Executed directly - from the editor's Run button - there is
- // no submission, and an empty form is the honest answer rather than an error.
- // The runner hands the trigger node's payload to `input`, not `context` -
- // `context` only carries executionId/nodeId/workflowId.
- const triggerData = input || {};
- return {
- form: triggerData.form || {},
- submittedAt: triggerData.submittedAt || Date.now(),
- clientIp: triggerData.clientIp || ''
- };
- }
- module.exports = { configSchema, inputSchema, outputSchema, execute };
|