n8n-nodes-formbase 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -3
- package/dist/nodes/Formbase/FormbaseTrigger.node.js +69 -9
- package/dist/nodes/Formbase/FormbaseTrigger.node.js.map +1 -1
- package/dist/nodes/Formbase/FormbaseWebhookSignature.d.ts +3 -0
- package/dist/nodes/Formbase/FormbaseWebhookSignature.js +46 -0
- package/dist/nodes/Formbase/FormbaseWebhookSignature.js.map +1 -0
- package/dist/nodes/Formbase/constants.d.ts +15 -0
- package/dist/nodes/Formbase/constants.js +12 -1
- package/dist/nodes/Formbase/constants.js.map +1 -1
- package/dist/package.json +3 -2
- package/examples/formbase-submission.json +83 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -5,9 +5,10 @@ Community node for [n8n](https://n8n.io) that starts workflows when a [formbase]
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- Trigger on completed submissions.
|
|
8
|
-
- Trigger on abandoned submissions
|
|
8
|
+
- Trigger on abandoned submissions after a selected 12-hour, 1-day, 3-day, or 1-week idle window.
|
|
9
9
|
- Load forms dynamically from every workspace available to the credential.
|
|
10
10
|
- Register and remove formbase webhook subscriptions with the n8n workflow lifecycle.
|
|
11
|
+
- Verify every webhook with HMAC-SHA256 and reject stale or forged requests.
|
|
11
12
|
- Connect through workspace-scoped OAuth 2.1 with PKCE and automatic refresh-token rotation.
|
|
12
13
|
|
|
13
14
|
## Install
|
|
@@ -36,12 +37,20 @@ Self-hosted n8n must use configured HTTPS public URL for OAuth callback. Loopbac
|
|
|
36
37
|
## Use trigger
|
|
37
38
|
|
|
38
39
|
1. Add **formbase Trigger** to a workflow.
|
|
39
|
-
2. Select form and event.
|
|
40
|
+
2. Select form and event. For an abandoned-submission event, select how long the response must remain unchanged.
|
|
40
41
|
3. For a test execution, select **Listen for Test Event**, then submit the selected form.
|
|
41
42
|
4. Activate the workflow. n8n registers its production webhook with formbase and removes it when the workflow is deactivated or deleted.
|
|
42
43
|
|
|
43
44
|
n8n webhook URL must be publicly reachable over HTTPS. For reverse-proxy or tunnel deployments, configure n8n's `WEBHOOK_URL` so generated webhook URLs use public origin.
|
|
44
45
|
|
|
46
|
+
n8n generates a separate 256-bit signing secret for each registration. Incoming requests must contain a valid `X-formbase-Signature` header with a timestamp no more than five minutes old. Missing, stale, or invalid signatures receive `401 Unauthorized` and do not start the workflow.
|
|
47
|
+
|
|
48
|
+
Abandoned-submission timing is enforced by formbase, not n8n. formbase checks incomplete responses hourly and calls the registered n8n webhook after the selected idle window, so delivery can occur up to about one hour after the threshold.
|
|
49
|
+
|
|
50
|
+
## Example workflow
|
|
51
|
+
|
|
52
|
+
Import [`examples/formbase-submission.json`](examples/formbase-submission.json), connect formbase credential, select form, then activate workflow. Example maps event ID, event type, submission ID, respondent email, and form name into stable output fields.
|
|
53
|
+
|
|
45
54
|
## Output
|
|
46
55
|
|
|
47
56
|
Each webhook produces one n8n item containing formbase payload:
|
|
@@ -76,7 +85,15 @@ Each webhook produces one n8n item containing formbase payload:
|
|
|
76
85
|
}
|
|
77
86
|
```
|
|
78
87
|
|
|
79
|
-
|
|
88
|
+
Delivered payloads use these `eventType` values:
|
|
89
|
+
|
|
90
|
+
| `eventType` | Meaning |
|
|
91
|
+
| ------------------ | ---------------------------------------------------------- |
|
|
92
|
+
| `SUBMIT_RESPONSE` | A respondent completed a new response. |
|
|
93
|
+
| `UPDATE_RESPONSE` | An existing completed response changed. |
|
|
94
|
+
| `ABANDON_RESPONSE` | An incomplete response reached the configured idle window. |
|
|
95
|
+
|
|
96
|
+
Webhook registration events (`submission_created` and `submission_abandoned`) select which deliveries trigger the workflow. Payload `eventType` identifies what happened to the response. Use `eventId` to deduplicate retries.
|
|
80
97
|
|
|
81
98
|
Full contracts: [formbase API methods](https://docs.formbase.so/developers/rest-api) and [webhook reference](https://docs.formbase.so/developers/webhooks-reference).
|
|
82
99
|
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.FormbaseTrigger = void 0;
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
5
|
const constants_1 = require("./constants");
|
|
6
|
+
const FormbaseWebhookSignature_1 = require("./FormbaseWebhookSignature");
|
|
6
7
|
const GenericFunctions_1 = require("./GenericFunctions");
|
|
7
8
|
const FORMS_PAGE_SIZE = 100;
|
|
8
9
|
async function listWorkspaceForms(context, workspace) {
|
|
@@ -24,8 +25,29 @@ async function listWorkspaceForms(context, workspace) {
|
|
|
24
25
|
} while (cursor);
|
|
25
26
|
return forms;
|
|
26
27
|
}
|
|
27
|
-
function findWebhookSubscription(subscriptions, targetUrl, eventType) {
|
|
28
|
-
return subscriptions.find((subscription) => subscription.targetUrl === targetUrl &&
|
|
28
|
+
function findWebhookSubscription(subscriptions, targetUrl, eventType, idleWindow) {
|
|
29
|
+
return subscriptions.find((subscription) => subscription.targetUrl === targetUrl &&
|
|
30
|
+
subscription.provider === 'n8n' &&
|
|
31
|
+
subscription.eventType === eventType &&
|
|
32
|
+
subscription.idleWindow === idleWindow);
|
|
33
|
+
}
|
|
34
|
+
function findWebhookSubscriptionsByIdentity(subscriptions, targetUrl, eventType) {
|
|
35
|
+
return subscriptions.filter((subscription) => subscription.targetUrl === targetUrl && subscription.provider === 'n8n' && subscription.eventType === eventType);
|
|
36
|
+
}
|
|
37
|
+
function getIdleWindow(context, eventType) {
|
|
38
|
+
if (eventType === constants_1.FORMBASE_WEBHOOK_EVENTS.submissionCreated)
|
|
39
|
+
return undefined;
|
|
40
|
+
const idleWindow = context.getNodeParameter('idleWindow');
|
|
41
|
+
if (!(0, constants_1.isFormbaseIdleWindow)(idleWindow)) {
|
|
42
|
+
throw new n8n_workflow_1.NodeApiError(context.getNode(), {
|
|
43
|
+
message: `Idle window must be one of: ${constants_1.FORMBASE_IDLE_WINDOW_OPTIONS.map((option) => option.value).join(', ')}`,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return idleWindow;
|
|
47
|
+
}
|
|
48
|
+
function clearWebhookRegistration(webhookData) {
|
|
49
|
+
delete webhookData.subscriptionId;
|
|
50
|
+
delete webhookData.webhookSecret;
|
|
29
51
|
}
|
|
30
52
|
class FormbaseTrigger {
|
|
31
53
|
constructor() {
|
|
@@ -37,7 +59,6 @@ class FormbaseTrigger {
|
|
|
37
59
|
version: 1,
|
|
38
60
|
subtitle: '={{ $parameter["event"] === "submission_created" ? "On submission created" : "On submission abandoned" }}',
|
|
39
61
|
description: 'Starts the workflow when a formbase form receives a submission',
|
|
40
|
-
usableAsTool: true,
|
|
41
62
|
defaults: {
|
|
42
63
|
name: 'formbase Trigger',
|
|
43
64
|
},
|
|
@@ -98,6 +119,20 @@ class FormbaseTrigger {
|
|
|
98
119
|
default: 'submission_created',
|
|
99
120
|
description: 'Event to subscribe to. Abandoned submissions require partial submission tracking.',
|
|
100
121
|
},
|
|
122
|
+
{
|
|
123
|
+
displayName: 'Consider Abandoned After',
|
|
124
|
+
name: 'idleWindow',
|
|
125
|
+
type: 'options',
|
|
126
|
+
displayOptions: {
|
|
127
|
+
show: {
|
|
128
|
+
event: [constants_1.FORMBASE_WEBHOOK_EVENTS.submissionAbandoned],
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
options: [...constants_1.FORMBASE_IDLE_WINDOW_OPTIONS],
|
|
132
|
+
default: '12h',
|
|
133
|
+
required: true,
|
|
134
|
+
description: 'Runs after the response has no saved changes for this long. The hourly sweep can add up to about one hour.',
|
|
135
|
+
},
|
|
101
136
|
],
|
|
102
137
|
};
|
|
103
138
|
this.methods = {
|
|
@@ -126,15 +161,29 @@ class FormbaseTrigger {
|
|
|
126
161
|
if (!formId)
|
|
127
162
|
return false;
|
|
128
163
|
const eventType = this.getNodeParameter('event');
|
|
164
|
+
const idleWindow = getIdleWindow(this, eventType);
|
|
129
165
|
const webhookData = this.getWorkflowStaticData('node');
|
|
130
166
|
const result = (await GenericFunctions_1.formbaseApiRequest.call(this, 'webhooks.list', {
|
|
131
167
|
formId,
|
|
132
168
|
}));
|
|
133
|
-
const match = findWebhookSubscription(result.items, webhookUrl, eventType);
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
169
|
+
const match = findWebhookSubscription(result.items, webhookUrl, eventType, idleWindow);
|
|
170
|
+
const staleMatches = findWebhookSubscriptionsByIdentity(result.items, webhookUrl, eventType).filter((subscription) => subscription.subscriptionId !== (match === null || match === void 0 ? void 0 : match.subscriptionId));
|
|
171
|
+
for (const staleMatch of staleMatches) {
|
|
172
|
+
await GenericFunctions_1.formbaseApiRequest.call(this, 'webhooks.delete', {
|
|
173
|
+
subscriptionId: staleMatch.subscriptionId,
|
|
174
|
+
});
|
|
137
175
|
}
|
|
176
|
+
if (!match) {
|
|
177
|
+
clearWebhookRegistration(webhookData);
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
const registrationMatchesStaticData = webhookData.subscriptionId === match.subscriptionId && typeof webhookData.webhookSecret === 'string';
|
|
181
|
+
if (registrationMatchesStaticData)
|
|
182
|
+
return true;
|
|
183
|
+
await GenericFunctions_1.formbaseApiRequest.call(this, 'webhooks.delete', {
|
|
184
|
+
subscriptionId: match.subscriptionId,
|
|
185
|
+
});
|
|
186
|
+
clearWebhookRegistration(webhookData);
|
|
138
187
|
return false;
|
|
139
188
|
},
|
|
140
189
|
async create() {
|
|
@@ -143,21 +192,28 @@ class FormbaseTrigger {
|
|
|
143
192
|
return false;
|
|
144
193
|
const formId = this.getNodeParameter('formId');
|
|
145
194
|
const eventType = this.getNodeParameter('event');
|
|
195
|
+
const idleWindow = getIdleWindow(this, eventType);
|
|
196
|
+
const webhookSecret = (0, FormbaseWebhookSignature_1.createFormbaseWebhookSecret)();
|
|
146
197
|
const created = (await GenericFunctions_1.formbaseApiRequest.call(this, 'webhooks.create', {
|
|
147
198
|
formId,
|
|
148
199
|
targetUrl: webhookUrl,
|
|
149
200
|
provider: 'n8n',
|
|
150
201
|
eventType,
|
|
202
|
+
...(idleWindow !== undefined ? { idleWindow } : {}),
|
|
203
|
+
signingSecret: webhookSecret,
|
|
151
204
|
}));
|
|
152
205
|
const webhookData = this.getWorkflowStaticData('node');
|
|
153
206
|
webhookData.subscriptionId = created.subscriptionId;
|
|
207
|
+
webhookData.webhookSecret = webhookSecret;
|
|
154
208
|
return true;
|
|
155
209
|
},
|
|
156
210
|
async delete() {
|
|
157
211
|
const webhookData = this.getWorkflowStaticData('node');
|
|
158
212
|
const subscriptionId = webhookData.subscriptionId;
|
|
159
|
-
if (typeof subscriptionId !== 'string')
|
|
213
|
+
if (typeof subscriptionId !== 'string') {
|
|
214
|
+
clearWebhookRegistration(webhookData);
|
|
160
215
|
return true;
|
|
216
|
+
}
|
|
161
217
|
try {
|
|
162
218
|
await GenericFunctions_1.formbaseApiRequest.call(this, 'webhooks.delete', {
|
|
163
219
|
subscriptionId,
|
|
@@ -168,13 +224,17 @@ class FormbaseTrigger {
|
|
|
168
224
|
return false;
|
|
169
225
|
}
|
|
170
226
|
}
|
|
171
|
-
|
|
227
|
+
clearWebhookRegistration(webhookData);
|
|
172
228
|
return true;
|
|
173
229
|
},
|
|
174
230
|
},
|
|
175
231
|
};
|
|
176
232
|
}
|
|
177
233
|
async webhook() {
|
|
234
|
+
if (!(0, FormbaseWebhookSignature_1.verifyFormbaseWebhookSignature)(this)) {
|
|
235
|
+
this.getResponseObject().status(401).send('Unauthorized').end();
|
|
236
|
+
return { noWebhookResponse: true };
|
|
237
|
+
}
|
|
178
238
|
const body = this.getBodyData();
|
|
179
239
|
return {
|
|
180
240
|
workflowData: [this.helpers.returnJsonArray(body)],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormbaseTrigger.node.js","sourceRoot":"","sources":["../../../nodes/Formbase/FormbaseTrigger.node.ts"],"names":[],"mappings":";;;AAUA,+CAAgE;AAEhE,
|
|
1
|
+
{"version":3,"file":"FormbaseTrigger.node.js","sourceRoot":"","sources":["../../../nodes/Formbase/FormbaseTrigger.node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createFormbaseWebhookSecret = createFormbaseWebhookSecret;
|
|
4
|
+
exports.verifyFormbaseWebhookSignature = verifyFormbaseWebhookSignature;
|
|
5
|
+
const node_crypto_1 = require("node:crypto");
|
|
6
|
+
const SIGNATURE_HEADER_PATTERN = /^t=(\d+),sha256=([a-f0-9]{64})$/;
|
|
7
|
+
const SIGNATURE_MAX_AGE_SECONDS = 5 * 60;
|
|
8
|
+
function getRawBody(request) {
|
|
9
|
+
if (!('rawBody' in request))
|
|
10
|
+
return undefined;
|
|
11
|
+
const rawBody = request.rawBody;
|
|
12
|
+
if (Buffer.isBuffer(rawBody))
|
|
13
|
+
return rawBody;
|
|
14
|
+
if (typeof rawBody === 'string')
|
|
15
|
+
return Buffer.from(rawBody);
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
function createFormbaseWebhookSecret() {
|
|
19
|
+
return `whsec_${(0, node_crypto_1.randomBytes)(32).toString('hex')}`;
|
|
20
|
+
}
|
|
21
|
+
function verifyFormbaseWebhookSignature(context) {
|
|
22
|
+
const webhookData = context.getWorkflowStaticData('node');
|
|
23
|
+
const secret = webhookData.webhookSecret;
|
|
24
|
+
if (typeof secret !== 'string' || secret.length === 0)
|
|
25
|
+
return false;
|
|
26
|
+
const signatureHeader = context.getHeaderData()['x-formbase-signature'];
|
|
27
|
+
if (typeof signatureHeader !== 'string')
|
|
28
|
+
return false;
|
|
29
|
+
const match = SIGNATURE_HEADER_PATTERN.exec(signatureHeader);
|
|
30
|
+
if (!match)
|
|
31
|
+
return false;
|
|
32
|
+
const [, timestamp, signatureHex] = match;
|
|
33
|
+
const timestampSeconds = Number(timestamp);
|
|
34
|
+
if (!Number.isSafeInteger(timestampSeconds))
|
|
35
|
+
return false;
|
|
36
|
+
const currentTimestampSeconds = Math.floor(Date.now() / 1000);
|
|
37
|
+
if (Math.abs(currentTimestampSeconds - timestampSeconds) > SIGNATURE_MAX_AGE_SECONDS)
|
|
38
|
+
return false;
|
|
39
|
+
const rawBody = getRawBody(context.getRequestObject());
|
|
40
|
+
if (!rawBody)
|
|
41
|
+
return false;
|
|
42
|
+
const expectedSignature = (0, node_crypto_1.createHmac)('sha256', secret).update(timestamp).update('.').update(rawBody).digest();
|
|
43
|
+
const receivedSignature = Buffer.from(signatureHex, 'hex');
|
|
44
|
+
return expectedSignature.length === receivedSignature.length && (0, node_crypto_1.timingSafeEqual)(expectedSignature, receivedSignature);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=FormbaseWebhookSignature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormbaseWebhookSignature.js","sourceRoot":"","sources":["../../../nodes/Formbase/FormbaseWebhookSignature.ts"],"names":[],"mappings":";;AAgBA,kEAEC;AAED,wEAyBC;AA7CD,6CAAsE;AAItE,MAAM,wBAAwB,GAAG,iCAAiC,CAAA;AAClE,MAAM,yBAAyB,GAAG,CAAC,GAAG,EAAE,CAAA;AAExC,SAAS,UAAU,CAAC,OAA0D;IAC5E,IAAI,CAAC,CAAC,SAAS,IAAI,OAAO,CAAC;QAAE,OAAO,SAAS,CAAA;IAE7C,MAAM,OAAO,GAAY,OAAO,CAAC,OAAO,CAAA;IACxC,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAA;IAC5C,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC5D,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,SAAgB,2BAA2B;IACzC,OAAO,SAAS,IAAA,yBAAW,EAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAA;AACnD,CAAC;AAED,SAAgB,8BAA8B,CAAC,OAA0B;IACvE,MAAM,WAAW,GAAG,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAA;IACzD,MAAM,MAAM,GAAG,WAAW,CAAC,aAAa,CAAA;IACxC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAEnE,MAAM,eAAe,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,sBAAsB,CAAC,CAAA;IACvE,IAAI,OAAO,eAAe,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAErD,MAAM,KAAK,GAAG,wBAAwB,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;IAC5D,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IAExB,MAAM,CAAC,EAAE,SAAS,EAAE,YAAY,CAAC,GAAG,KAAK,CAAA;IACzC,MAAM,gBAAgB,GAAG,MAAM,CAAC,SAAS,CAAC,CAAA;IAC1C,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,gBAAgB,CAAC;QAAE,OAAO,KAAK,CAAA;IAEzD,MAAM,uBAAuB,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;IAC7D,IAAI,IAAI,CAAC,GAAG,CAAC,uBAAuB,GAAG,gBAAgB,CAAC,GAAG,yBAAyB;QAAE,OAAO,KAAK,CAAA;IAElG,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAA;IACtD,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAA;IAE1B,MAAM,iBAAiB,GAAG,IAAA,wBAAU,EAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAA;IAC7G,MAAM,iBAAiB,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAA;IAE1D,OAAO,iBAAiB,CAAC,MAAM,KAAK,iBAAiB,CAAC,MAAM,IAAI,IAAA,6BAAe,EAAC,iBAAiB,EAAE,iBAAiB,CAAC,CAAA;AACvH,CAAC"}
|
|
@@ -7,3 +7,18 @@ export declare const FORMBASE_WEBHOOK_EVENTS: {
|
|
|
7
7
|
readonly submissionAbandoned: "submission_abandoned";
|
|
8
8
|
};
|
|
9
9
|
export type FormbaseWebhookEvent = (typeof FORMBASE_WEBHOOK_EVENTS)[keyof typeof FORMBASE_WEBHOOK_EVENTS];
|
|
10
|
+
export declare const FORMBASE_IDLE_WINDOW_OPTIONS: readonly [{
|
|
11
|
+
readonly name: "1 Day";
|
|
12
|
+
readonly value: "1d";
|
|
13
|
+
}, {
|
|
14
|
+
readonly name: "1 Week";
|
|
15
|
+
readonly value: "1w";
|
|
16
|
+
}, {
|
|
17
|
+
readonly name: "12 Hours";
|
|
18
|
+
readonly value: "12h";
|
|
19
|
+
}, {
|
|
20
|
+
readonly name: "3 Days";
|
|
21
|
+
readonly value: "3d";
|
|
22
|
+
}];
|
|
23
|
+
export type FormbaseIdleWindow = (typeof FORMBASE_IDLE_WINDOW_OPTIONS)[number]['value'];
|
|
24
|
+
export declare function isFormbaseIdleWindow(value: unknown): value is FormbaseIdleWindow;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FORMBASE_WEBHOOK_EVENTS = exports.FORMBASE_OAUTH2_CREDENTIAL_NAME = exports.FORMBASE_API_RESOURCE_URL = exports.FORMBASE_API_PATH = exports.DEFAULT_FORMBASE_API_URL = void 0;
|
|
3
|
+
exports.FORMBASE_IDLE_WINDOW_OPTIONS = exports.FORMBASE_WEBHOOK_EVENTS = exports.FORMBASE_OAUTH2_CREDENTIAL_NAME = exports.FORMBASE_API_RESOURCE_URL = exports.FORMBASE_API_PATH = exports.DEFAULT_FORMBASE_API_URL = void 0;
|
|
4
|
+
exports.isFormbaseIdleWindow = isFormbaseIdleWindow;
|
|
4
5
|
exports.DEFAULT_FORMBASE_API_URL = 'https://api.formbase.so';
|
|
5
6
|
exports.FORMBASE_API_PATH = '/api/v1';
|
|
6
7
|
exports.FORMBASE_API_RESOURCE_URL = `${exports.DEFAULT_FORMBASE_API_URL}${exports.FORMBASE_API_PATH}`;
|
|
@@ -9,4 +10,14 @@ exports.FORMBASE_WEBHOOK_EVENTS = {
|
|
|
9
10
|
submissionCreated: 'submission_created',
|
|
10
11
|
submissionAbandoned: 'submission_abandoned',
|
|
11
12
|
};
|
|
13
|
+
exports.FORMBASE_IDLE_WINDOW_OPTIONS = [
|
|
14
|
+
{ name: '1 Day', value: '1d' },
|
|
15
|
+
{ name: '1 Week', value: '1w' },
|
|
16
|
+
{ name: '12 Hours', value: '12h' },
|
|
17
|
+
{ name: '3 Days', value: '3d' },
|
|
18
|
+
];
|
|
19
|
+
const FORMBASE_IDLE_WINDOW_VALUES = new Set(exports.FORMBASE_IDLE_WINDOW_OPTIONS.map((option) => option.value));
|
|
20
|
+
function isFormbaseIdleWindow(value) {
|
|
21
|
+
return typeof value === 'string' && FORMBASE_IDLE_WINDOW_VALUES.has(value);
|
|
22
|
+
}
|
|
12
23
|
//# sourceMappingURL=constants.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../nodes/Formbase/constants.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../nodes/Formbase/constants.ts"],"names":[],"mappings":";;;AA4BA,oDAEC;AA9BY,QAAA,wBAAwB,GAAG,yBAAyB,CAAA;AAEpD,QAAA,iBAAiB,GAAG,SAAS,CAAA;AAE7B,QAAA,yBAAyB,GAAG,GAAG,gCAAwB,GAAG,yBAAiB,EAAE,CAAA;AAE7E,QAAA,+BAA+B,GAAG,mBAAmB,CAAA;AAErD,QAAA,uBAAuB,GAAG;IACrC,iBAAiB,EAAE,oBAAoB;IACvC,mBAAmB,EAAE,sBAAsB;CACnC,CAAA;AAIG,QAAA,4BAA4B,GAAG;IAC1C,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE;IAC9B,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE;IAC/B,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE;IAClC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE;CACvB,CAAA;AAIV,MAAM,2BAA2B,GAAwB,IAAI,GAAG,CAC9D,oCAA4B,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAC3D,CAAA;AAED,SAAgB,oBAAoB,CAAC,KAAc;IACjD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,2BAA2B,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;AAC5E,CAAC"}
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-formbase",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "n8n community node for formbase — trigger workflows from form submissions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n-community-node-package",
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
"prepublishOnly": "npm test && npm run build && npm run lint"
|
|
33
33
|
},
|
|
34
34
|
"files": [
|
|
35
|
-
"dist"
|
|
35
|
+
"dist",
|
|
36
|
+
"examples"
|
|
36
37
|
],
|
|
37
38
|
"engines": {
|
|
38
39
|
"node": ">=22"
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Handle formbase submission",
|
|
3
|
+
"nodes": [
|
|
4
|
+
{
|
|
5
|
+
"parameters": {
|
|
6
|
+
"formId": "",
|
|
7
|
+
"event": "submission_created"
|
|
8
|
+
},
|
|
9
|
+
"type": "n8n-nodes-formbase.formbaseTrigger",
|
|
10
|
+
"typeVersion": 1,
|
|
11
|
+
"position": [0, 0],
|
|
12
|
+
"id": "16e911f0-6385-4acc-8286-1afb15eb035c",
|
|
13
|
+
"name": "formbase Trigger"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"parameters": {
|
|
17
|
+
"assignments": {
|
|
18
|
+
"assignments": [
|
|
19
|
+
{
|
|
20
|
+
"id": "cdcb57d7-377e-45e2-8da9-63e52f4690b4",
|
|
21
|
+
"name": "eventId",
|
|
22
|
+
"value": "={{ $json.eventId }}",
|
|
23
|
+
"type": "string"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "9da94c68-fcf8-4f79-b78e-5bb78c043aec",
|
|
27
|
+
"name": "eventType",
|
|
28
|
+
"value": "={{ $json.eventType }}",
|
|
29
|
+
"type": "string"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"id": "b27b42be-fc5d-491f-a93b-02a7fa4e63f8",
|
|
33
|
+
"name": "submissionId",
|
|
34
|
+
"value": "={{ $json.submission.id }}",
|
|
35
|
+
"type": "string"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "2b171786-cb32-4e73-96e5-d60f39060fe1",
|
|
39
|
+
"name": "respondentEmail",
|
|
40
|
+
"value": "={{ $json.submission.respondentEmail }}",
|
|
41
|
+
"type": "string"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id": "a6366cdf-c3b9-4bdc-be3c-723d2bfab7a4",
|
|
45
|
+
"name": "formName",
|
|
46
|
+
"value": "={{ $json.form.name }}",
|
|
47
|
+
"type": "string"
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
"options": {}
|
|
52
|
+
},
|
|
53
|
+
"type": "n8n-nodes-base.set",
|
|
54
|
+
"typeVersion": 3.4,
|
|
55
|
+
"position": [240, 0],
|
|
56
|
+
"id": "27abc7e3-21e9-4654-9078-b12b60420f6d",
|
|
57
|
+
"name": "Prepare submission"
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
"pinData": {},
|
|
61
|
+
"connections": {
|
|
62
|
+
"formbase Trigger": {
|
|
63
|
+
"main": [
|
|
64
|
+
[
|
|
65
|
+
{
|
|
66
|
+
"node": "Prepare submission",
|
|
67
|
+
"type": "main",
|
|
68
|
+
"index": 0
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"active": false,
|
|
75
|
+
"settings": {
|
|
76
|
+
"executionOrder": "v1"
|
|
77
|
+
},
|
|
78
|
+
"versionId": "0f0cc0ea-543e-43ac-bf55-9256b1ee5f85",
|
|
79
|
+
"meta": {
|
|
80
|
+
"templateCredsSetupCompleted": false
|
|
81
|
+
},
|
|
82
|
+
"tags": []
|
|
83
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-formbase",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "n8n community node for formbase — trigger workflows from form submissions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n-community-node-package",
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
"prepublishOnly": "npm test && npm run build && npm run lint"
|
|
33
33
|
},
|
|
34
34
|
"files": [
|
|
35
|
-
"dist"
|
|
35
|
+
"dist",
|
|
36
|
+
"examples"
|
|
36
37
|
],
|
|
37
38
|
"engines": {
|
|
38
39
|
"node": ">=22"
|