n8n-nodes-nodey 0.1.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/LICENSE +21 -0
- package/README.md +126 -0
- package/dist/credentials/NodeyApi.credentials.d.ts +16 -0
- package/dist/credentials/NodeyApi.credentials.d.ts.map +1 -0
- package/dist/credentials/NodeyApi.credentials.js +36 -0
- package/dist/credentials/NodeyApi.credentials.js.map +1 -0
- package/dist/nodes/NodeyNfcTrigger/NodeyNfcTrigger.node.d.ts +6 -0
- package/dist/nodes/NodeyNfcTrigger/NodeyNfcTrigger.node.d.ts.map +1 -0
- package/dist/nodes/NodeyNfcTrigger/NodeyNfcTrigger.node.js +154 -0
- package/dist/nodes/NodeyNfcTrigger/NodeyNfcTrigger.node.js.map +1 -0
- package/dist/nodes/NodeyNfcTrigger/NodeyNfcTrigger.node.json +18 -0
- package/dist/nodes/NodeyNfcTrigger/nodey.svg +10 -0
- package/dist/nodes/NodeyNfcTrigger/normalize-payload.d.ts +14 -0
- package/dist/nodes/NodeyNfcTrigger/normalize-payload.d.ts.map +1 -0
- package/dist/nodes/NodeyNfcTrigger/normalize-payload.js +58 -0
- package/dist/nodes/NodeyNfcTrigger/normalize-payload.js.map +1 -0
- package/dist/nodes/NodeyNfcTrigger/verify-signature.d.ts +8 -0
- package/dist/nodes/NodeyNfcTrigger/verify-signature.d.ts.map +1 -0
- package/dist/nodes/NodeyNfcTrigger/verify-signature.js +31 -0
- package/dist/nodes/NodeyNfcTrigger/verify-signature.js.map +1 -0
- package/index.js +1 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hesham
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# n8n-nodes-nodey
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/n8n-nodes-nodey)
|
|
4
|
+
[](./LICENSE)
|
|
5
|
+
|
|
6
|
+
**n8n community node that fires a workflow when you tap an NFC tag with the [Nodey mobile app](https://getnodey.com).**
|
|
7
|
+
|
|
8
|
+
Verified-compatible — installable on n8n Cloud via *Settings → Community Nodes*. Accepts payloads from both iOS and Android Nodey clients and normalizes them into a single shape.
|
|
9
|
+
|
|
10
|
+
## How it works
|
|
11
|
+
|
|
12
|
+
1. You install **n8n-nodes-nodey** in your n8n instance.
|
|
13
|
+
2. You add a **Nodey NFC Trigger** node to a workflow. n8n gives you a Production webhook URL.
|
|
14
|
+
3. In the Nodey app, you create an NFC trigger pointing at that URL.
|
|
15
|
+
4. Every tap on the tag POSTs to the webhook and runs your workflow.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
In n8n Cloud or self-hosted:
|
|
20
|
+
|
|
21
|
+
1. **Settings → Community Nodes → Install**
|
|
22
|
+
2. Enter the package name: `n8n-nodes-nodey`
|
|
23
|
+
3. Click **Install**
|
|
24
|
+
|
|
25
|
+
## What you get
|
|
26
|
+
|
|
27
|
+
- **Webhook trigger** — one POST per NFC tap, no polling.
|
|
28
|
+
- **Cross-platform payload normalization** — iOS and Android send different JSON shapes; the node merges them into a consistent output.
|
|
29
|
+
- **Android `customData` parsing** — Nodey on Android currently sends `customData` as a JSON-encoded string. The node parses it transparently (toggleable).
|
|
30
|
+
- **Optional tag UID allowlist** — restrict the trigger to a specific set of NFC tags.
|
|
31
|
+
- **Forward-compatible HMAC signature verification** — Nodey does not sign requests today, but if you set a webhook secret the node will require an `X-Nodey-Signature` (or `X-Signature`, or `X-Hub-Signature-256`) header. Ready for whenever Nodey ships signing.
|
|
32
|
+
|
|
33
|
+
## Normalized output
|
|
34
|
+
|
|
35
|
+
The default **Normalized** output format produces:
|
|
36
|
+
|
|
37
|
+
```jsonc
|
|
38
|
+
{
|
|
39
|
+
"platform": "ios" | "android" | "unknown",
|
|
40
|
+
"triggerName": "Front Door Tag" | null,
|
|
41
|
+
"triggerType": "nfc",
|
|
42
|
+
"event": "tap",
|
|
43
|
+
"timestamp": "2026-05-16T12:30:45.123Z",
|
|
44
|
+
"source": "Nodey",
|
|
45
|
+
"triggerId": "8b3f9a7c-…" | null,
|
|
46
|
+
"customData": { … } | "raw text" | null,
|
|
47
|
+
"raw": { /* the original request body */ }
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Switch to **Raw Payload** in the node settings if you'd rather work with what Nodey sent directly.
|
|
52
|
+
|
|
53
|
+
## Payload shapes (as of Nodey v2.3.x)
|
|
54
|
+
|
|
55
|
+
### iOS
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"trigger_name": "Front Door Tag",
|
|
60
|
+
"trigger_type": "nfc",
|
|
61
|
+
"event": "tap",
|
|
62
|
+
"timestamp": "2026-05-16T12:30:45.123Z",
|
|
63
|
+
"source": "Nodey"
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Android (valid custom payload)
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"triggerId": "8b3f9a7c-2d1e-4b5a-9c8f-6a3d1e7b2f4c",
|
|
72
|
+
"timestamp": "2026-05-16T12:30:45.123Z",
|
|
73
|
+
"customData": "{\"door\":\"front\",\"action\":\"unlock\"}"
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Android (invalid custom payload fallback)
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"triggerId": "8b3f9a7c-2d1e-4b5a-9c8f-6a3d1e7b2f4c",
|
|
82
|
+
"timestamp": "2026-05-16T12:30:45.123Z",
|
|
83
|
+
"customPayload": "raw text the user entered"
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Delivery semantics
|
|
88
|
+
|
|
89
|
+
> Nodey attempts delivery **once per scan**. If the device is offline or the request fails, Nodey records the failed attempt locally but does not replay it. Workflows that need guaranteed delivery should handle acknowledgement, retry, or queueing inside n8n.
|
|
90
|
+
|
|
91
|
+
This matches what the Nodey iOS and Android devs confirmed: at-most-once, best-effort delivery, no retries from the app. NFC scans are intentional physical actions and a delayed replay would surprise users.
|
|
92
|
+
|
|
93
|
+
## Credentials
|
|
94
|
+
|
|
95
|
+
Credentials are **optional**. Skip them unless you need allowlisting or signing.
|
|
96
|
+
|
|
97
|
+
| Field | Purpose |
|
|
98
|
+
|---|---|
|
|
99
|
+
| **Webhook Secret** | Shared secret for HMAC verification. Leave blank — Nodey does not sign requests today. Setting it will reject unsigned requests, so configure once Nodey ships signing. |
|
|
100
|
+
| **Allowed Tag UIDs** | Comma-separated list of tag UIDs / trigger IDs. If set, scans from other tags are silently dropped (200 OK, no workflow run). |
|
|
101
|
+
|
|
102
|
+
## Local testing
|
|
103
|
+
|
|
104
|
+
Once installed, paste the **Test URL** from the node panel into curl:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# iOS payload
|
|
108
|
+
curl -X POST "<test-webhook-url>" \
|
|
109
|
+
-H "Content-Type: application/json" \
|
|
110
|
+
-d '{"trigger_name":"Front Door Tag","trigger_type":"nfc","event":"tap","timestamp":"2026-05-16T12:30:45.123Z","source":"Nodey"}'
|
|
111
|
+
|
|
112
|
+
# Android payload
|
|
113
|
+
curl -X POST "<test-webhook-url>" \
|
|
114
|
+
-H "Content-Type: application/json" \
|
|
115
|
+
-d '{"triggerId":"8b3f9a7c-2d1e-4b5a-9c8f-6a3d1e7b2f4c","timestamp":"2026-05-16T12:30:45.123Z","customData":"{\"door\":\"front\"}"}'
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## More tools for n8n users
|
|
119
|
+
|
|
120
|
+
- **[Nodey](https://getnodey.com)** — Mobile command-centre for n8n. NFC triggers, geo-fenced location triggers, AI workflow builder, and on-the-go workflow debugging.
|
|
121
|
+
- **[n8n-nodes-ghost-blocks-cloud](https://www.npmjs.com/package/n8n-nodes-ghost-blocks-cloud)** — Publish to Ghost CMS from n8n Cloud using a clean content-blocks format.
|
|
122
|
+
- **[n8n-nodes-ghost-blocks](https://www.npmjs.com/package/n8n-nodes-ghost-blocks)** — Full-features Ghost publishing for self-hosted n8n (image upload, oEmbed, OpenGraph).
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
MIT
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ICredentialType, INodeProperties, Icon } from 'n8n-workflow';
|
|
2
|
+
/**
|
|
3
|
+
* Optional credentials for the Nodey NFC Trigger node.
|
|
4
|
+
*
|
|
5
|
+
* Nodey does not authenticate webhook requests today. Both fields below are
|
|
6
|
+
* forward-compatible and have no effect unless populated. Configure later if
|
|
7
|
+
* Nodey adds HMAC signing, or use the tag UID allowlist immediately.
|
|
8
|
+
*/
|
|
9
|
+
export declare class NodeyApi implements ICredentialType {
|
|
10
|
+
name: string;
|
|
11
|
+
displayName: string;
|
|
12
|
+
documentationUrl: string;
|
|
13
|
+
icon: Icon;
|
|
14
|
+
properties: INodeProperties[];
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=NodeyApi.credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodeyApi.credentials.d.ts","sourceRoot":"","sources":["../../credentials/NodeyApi.credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEtE;;;;;;GAMG;AACH,qBAAa,QAAS,YAAW,eAAe;IAC9C,IAAI,SAAc;IAClB,WAAW,SAAe;IAC1B,gBAAgB,SACoF;IACpG,IAAI,EAAE,IAAI,CAAoB;IAE9B,UAAU,EAAE,eAAe,EAAE,CAmB3B;CACH"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NodeyApi = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Optional credentials for the Nodey NFC Trigger node.
|
|
6
|
+
*
|
|
7
|
+
* Nodey does not authenticate webhook requests today. Both fields below are
|
|
8
|
+
* forward-compatible and have no effect unless populated. Configure later if
|
|
9
|
+
* Nodey adds HMAC signing, or use the tag UID allowlist immediately.
|
|
10
|
+
*/
|
|
11
|
+
class NodeyApi {
|
|
12
|
+
name = 'nodeyApi';
|
|
13
|
+
displayName = 'Nodey API';
|
|
14
|
+
documentationUrl = 'https://github.com/TheFamousHesham/ghost-blocks/tree/master/packages/n8n-node-nodey#credentials';
|
|
15
|
+
icon = 'file:nodey.svg';
|
|
16
|
+
properties = [
|
|
17
|
+
{
|
|
18
|
+
displayName: 'Webhook Secret',
|
|
19
|
+
name: 'webhookSecret',
|
|
20
|
+
type: 'string',
|
|
21
|
+
typeOptions: { password: true },
|
|
22
|
+
default: '',
|
|
23
|
+
description: 'Optional shared secret for HMAC verification. Nodey does not sign requests today — leave blank until signing ships. When set, requests without a valid X-Nodey-Signature header will be rejected.',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
displayName: 'Allowed Tag UIDs',
|
|
27
|
+
name: 'allowedTagUids',
|
|
28
|
+
type: 'string',
|
|
29
|
+
default: '',
|
|
30
|
+
placeholder: 'uid-1,uid-2,uid-3',
|
|
31
|
+
description: 'Optional comma-separated allowlist of NFC tag UIDs / trigger IDs. If set, only scans matching one of these IDs trigger the workflow. Leave blank to accept all scans.',
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
exports.NodeyApi = NodeyApi;
|
|
36
|
+
//# sourceMappingURL=NodeyApi.credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodeyApi.credentials.js","sourceRoot":"","sources":["../../credentials/NodeyApi.credentials.ts"],"names":[],"mappings":";;;AAEA;;;;;;GAMG;AACH,MAAa,QAAQ;IACnB,IAAI,GAAG,UAAU,CAAC;IAClB,WAAW,GAAG,WAAW,CAAC;IAC1B,gBAAgB,GACd,iGAAiG,CAAC;IACpG,IAAI,GAAS,gBAAgB,CAAC;IAE9B,UAAU,GAAsB;QAC9B;YACE,WAAW,EAAE,gBAAgB;YAC7B,IAAI,EAAE,eAAe;YACrB,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;YAC/B,OAAO,EAAE,EAAE;YACX,WAAW,EACT,mMAAmM;SACtM;QACD;YACE,WAAW,EAAE,kBAAkB;YAC/B,IAAI,EAAE,gBAAgB;YACtB,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,EAAE;YACX,WAAW,EAAE,mBAAmB;YAChC,WAAW,EACT,uKAAuK;SAC1K;KACF,CAAC;CACH;AA3BD,4BA2BC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { INodeType, INodeTypeDescription, IWebhookFunctions, IWebhookResponseData } from 'n8n-workflow';
|
|
2
|
+
export declare class NodeyNfcTrigger implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
webhook(this: IWebhookFunctions): Promise<IWebhookResponseData>;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=NodeyNfcTrigger.node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodeyNfcTrigger.node.d.ts","sourceRoot":"","sources":["../../../nodes/NodeyNfcTrigger/NodeyNfcTrigger.node.ts"],"names":[],"mappings":"AAOA,OAAO,EAEL,SAAS,EACT,oBAAoB,EACpB,iBAAiB,EACjB,oBAAoB,EAErB,MAAM,cAAc,CAAC;AAqCtB,qBAAa,eAAgB,YAAW,SAAS;IAC/C,WAAW,EAAE,oBAAoB,CAsE/B;IAEI,OAAO,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAyDtE"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// n8n-nodes-nodey — verified-compatible NFC trigger node.
|
|
3
|
+
// Receives NFC tap events from the Nodey mobile app (iOS + Android) via a
|
|
4
|
+
// webhook and emits a normalized payload into the workflow.
|
|
5
|
+
//
|
|
6
|
+
// Uses only n8n-workflow types and node:crypto (via ./verify-signature). No
|
|
7
|
+
// fs, fetch, dns, or timers — passes the verified-community-node sandbox.
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.NodeyNfcTrigger = void 0;
|
|
10
|
+
const normalize_payload_1 = require("./normalize-payload");
|
|
11
|
+
const verify_signature_1 = require("./verify-signature");
|
|
12
|
+
const SIGNATURE_HEADERS = ['x-nodey-signature', 'x-signature', 'x-hub-signature-256'];
|
|
13
|
+
function parseUidAllowlist(value) {
|
|
14
|
+
if (!value)
|
|
15
|
+
return [];
|
|
16
|
+
return value
|
|
17
|
+
.split(',')
|
|
18
|
+
.map((s) => s.trim())
|
|
19
|
+
.filter(Boolean);
|
|
20
|
+
}
|
|
21
|
+
function extractUid(body) {
|
|
22
|
+
const candidates = [body.tagUid, body.tag_uid, body.triggerId, body.trigger_id];
|
|
23
|
+
for (const c of candidates) {
|
|
24
|
+
if (typeof c === 'string' && c.length > 0)
|
|
25
|
+
return c;
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
function pickSignatureHeader(headers) {
|
|
30
|
+
for (const name of SIGNATURE_HEADERS) {
|
|
31
|
+
const value = headers[name];
|
|
32
|
+
if (typeof value === 'string' && value.length > 0)
|
|
33
|
+
return value;
|
|
34
|
+
if (Array.isArray(value) && typeof value[0] === 'string')
|
|
35
|
+
return value[0];
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
class NodeyNfcTrigger {
|
|
40
|
+
description = {
|
|
41
|
+
displayName: 'Nodey NFC Trigger',
|
|
42
|
+
name: 'nodeyNfcTrigger',
|
|
43
|
+
icon: 'file:nodey.svg',
|
|
44
|
+
group: ['trigger'],
|
|
45
|
+
version: 1,
|
|
46
|
+
description: 'Triggered when an NFC tag is scanned via the Nodey mobile app',
|
|
47
|
+
defaults: { name: 'Nodey NFC Trigger' },
|
|
48
|
+
inputs: [],
|
|
49
|
+
outputs: ['main'],
|
|
50
|
+
credentials: [{ name: 'nodeyApi', required: false }],
|
|
51
|
+
webhooks: [
|
|
52
|
+
{
|
|
53
|
+
name: 'default',
|
|
54
|
+
httpMethod: 'POST',
|
|
55
|
+
responseMode: 'onReceived',
|
|
56
|
+
path: 'nodey-nfc',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
properties: [
|
|
60
|
+
{
|
|
61
|
+
displayName: 'Triggered when you tap an NFC tag using the <a href="https://getnodey.com" target="_blank">Nodey app</a> on iOS or Android. Copy the Production URL below into your Nodey NFC trigger configuration.',
|
|
62
|
+
name: 'nodeyPromoNotice',
|
|
63
|
+
type: 'notice',
|
|
64
|
+
default: '',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
displayName: 'Output Format',
|
|
68
|
+
name: 'outputFormat',
|
|
69
|
+
type: 'options',
|
|
70
|
+
options: [
|
|
71
|
+
{
|
|
72
|
+
name: 'Normalized',
|
|
73
|
+
value: 'normalized',
|
|
74
|
+
description: 'Merge iOS and Android shapes into one consistent object (platform, triggerName, triggerId, customData, ...).',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'Raw Payload',
|
|
78
|
+
value: 'raw',
|
|
79
|
+
description: 'Pass the request body straight through without normalization.',
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
default: 'normalized',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
displayName: 'Parse Android customData',
|
|
86
|
+
name: 'parseCustomData',
|
|
87
|
+
type: 'boolean',
|
|
88
|
+
default: true,
|
|
89
|
+
description: 'Whether to parse Android\'s customData JSON string into a nested object. iOS payloads are unaffected.',
|
|
90
|
+
displayOptions: { show: { outputFormat: ['normalized'] } },
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
displayName: 'Nodey delivers each scan at-most-once — if the phone is offline or the request fails, Nodey records the failure but does not retry. If your workflow needs guaranteed delivery, handle queueing or acknowledgement on the n8n side.',
|
|
94
|
+
name: 'deliveryNotice',
|
|
95
|
+
type: 'notice',
|
|
96
|
+
default: '',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
displayName: 'More from this author: <a href="https://www.npmjs.com/package/n8n-nodes-ghost-blocks-cloud" target="_blank">n8n-nodes-ghost-blocks-cloud</a> (Ghost CMS publishing, also verified).',
|
|
100
|
+
name: 'ecosystemNotice',
|
|
101
|
+
type: 'notice',
|
|
102
|
+
default: '',
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
};
|
|
106
|
+
async webhook() {
|
|
107
|
+
const body = (this.getBodyData() ?? {});
|
|
108
|
+
const headers = (this.getHeaderData() ?? {});
|
|
109
|
+
const outputFormat = this.getNodeParameter('outputFormat');
|
|
110
|
+
const parseCustomData = this.getNodeParameter('parseCustomData', true);
|
|
111
|
+
const credentials = (await this.getCredentials('nodeyApi').catch(() => null));
|
|
112
|
+
// Optional HMAC verification — only enforced if a secret is configured.
|
|
113
|
+
if (credentials?.webhookSecret) {
|
|
114
|
+
const signatureHeader = pickSignatureHeader(headers);
|
|
115
|
+
const req = this.getRequestObject();
|
|
116
|
+
// Express's body-parser stashes the raw body on req.rawBody when configured
|
|
117
|
+
// to do so. n8n's webhook handler does this for binary endpoints but not
|
|
118
|
+
// always for JSON — fall back to re-serializing if the raw bytes aren't
|
|
119
|
+
// available. This is best-effort until Nodey publishes their signing spec.
|
|
120
|
+
const rawBody = req.rawBody?.toString() ?? JSON.stringify(body);
|
|
121
|
+
const ok = (0, verify_signature_1.verifySignature)({
|
|
122
|
+
rawBody,
|
|
123
|
+
secret: credentials.webhookSecret,
|
|
124
|
+
signatureHeader,
|
|
125
|
+
});
|
|
126
|
+
if (!ok) {
|
|
127
|
+
return {
|
|
128
|
+
webhookResponse: {
|
|
129
|
+
status: 401,
|
|
130
|
+
body: { error: 'invalid signature' },
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Optional tag UID allowlist — silently 200 OK on mismatch so an attacker
|
|
136
|
+
// can't probe the allowlist by watching status codes.
|
|
137
|
+
const allowed = parseUidAllowlist(credentials?.allowedTagUids);
|
|
138
|
+
if (allowed.length > 0) {
|
|
139
|
+
const incoming = extractUid(body);
|
|
140
|
+
if (!incoming || !allowed.includes(incoming)) {
|
|
141
|
+
return {
|
|
142
|
+
webhookResponse: { status: 200, body: { status: 'ignored' } },
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const output = outputFormat === 'raw' ? body : (0, normalize_payload_1.normalize)(body, parseCustomData);
|
|
147
|
+
return {
|
|
148
|
+
workflowData: [[{ json: output }]],
|
|
149
|
+
webhookResponse: { status: 200, body: { status: 'ok' } },
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
exports.NodeyNfcTrigger = NodeyNfcTrigger;
|
|
154
|
+
//# sourceMappingURL=NodeyNfcTrigger.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodeyNfcTrigger.node.js","sourceRoot":"","sources":["../../../nodes/NodeyNfcTrigger/NodeyNfcTrigger.node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"node": "n8n-nodes-base.nodeyNfcTrigger",
|
|
3
|
+
"nodeVersion": "1.0",
|
|
4
|
+
"codexVersion": "1.0",
|
|
5
|
+
"categories": ["Mobile", "Trigger"],
|
|
6
|
+
"resources": {
|
|
7
|
+
"credentialDocumentation": [
|
|
8
|
+
{
|
|
9
|
+
"url": "https://github.com/TheFamousHesham/ghost-blocks/tree/master/packages/n8n-node-nodey#credentials"
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"primaryDocumentation": [
|
|
13
|
+
{
|
|
14
|
+
"url": "https://github.com/TheFamousHesham/ghost-blocks/tree/master/packages/n8n-node-nodey"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" width="60" height="60">
|
|
3
|
+
<rect width="60" height="60" rx="12" fill="#6D28D9"/>
|
|
4
|
+
<g fill="none" stroke="#FFFFFF" stroke-linecap="round" stroke-width="3">
|
|
5
|
+
<circle cx="18" cy="30" r="3" fill="#FFFFFF" stroke="none"/>
|
|
6
|
+
<path d="M26 22 a10 10 0 0 1 0 16"/>
|
|
7
|
+
<path d="M33 16 a18 18 0 0 1 0 28"/>
|
|
8
|
+
<path d="M40 10 a26 26 0 0 1 0 40"/>
|
|
9
|
+
</g>
|
|
10
|
+
</svg>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type Platform = 'ios' | 'android' | 'unknown';
|
|
2
|
+
export interface NormalizedPayload {
|
|
3
|
+
platform: Platform;
|
|
4
|
+
triggerName: string | null;
|
|
5
|
+
triggerType: string;
|
|
6
|
+
event: string;
|
|
7
|
+
timestamp: string;
|
|
8
|
+
source: string;
|
|
9
|
+
triggerId: string | null;
|
|
10
|
+
customData: unknown;
|
|
11
|
+
raw: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
export declare function normalize(body: Record<string, unknown>, parseCustomData: boolean): NormalizedPayload;
|
|
14
|
+
//# sourceMappingURL=normalize-payload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize-payload.d.ts","sourceRoot":"","sources":["../../../nodes/NodeyNfcTrigger/normalize-payload.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,SAAS,GAAG,SAAS,CAAC;AAErD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,QAAQ,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC9B;AAuCD,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,eAAe,EAAE,OAAO,GACvB,iBAAiB,CAenB"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Pure logic to merge iOS + Android NFC payloads into a consistent shape.
|
|
3
|
+
// No imports — keeps the function trivially testable and sandbox-friendly.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.normalize = normalize;
|
|
6
|
+
const isString = (v) => typeof v === 'string';
|
|
7
|
+
function detectPlatform(body) {
|
|
8
|
+
// iOS shape: has snake_case trigger fields and a "source" of "Nodey".
|
|
9
|
+
if ('trigger_name' in body || 'trigger_type' in body || body.source === 'Nodey') {
|
|
10
|
+
return 'ios';
|
|
11
|
+
}
|
|
12
|
+
// Android shape: has triggerId (UUID) plus customData OR customPayload.
|
|
13
|
+
if ('triggerId' in body && ('customData' in body || 'customPayload' in body)) {
|
|
14
|
+
return 'android';
|
|
15
|
+
}
|
|
16
|
+
// Looser Android detection: any triggerId without iOS fields.
|
|
17
|
+
if ('triggerId' in body) {
|
|
18
|
+
return 'android';
|
|
19
|
+
}
|
|
20
|
+
return 'unknown';
|
|
21
|
+
}
|
|
22
|
+
function extractCustomData(body, parseCustomData) {
|
|
23
|
+
// Android valid-JSON case: customData is a JSON-encoded string.
|
|
24
|
+
if ('customData' in body) {
|
|
25
|
+
const raw = body.customData;
|
|
26
|
+
if (!parseCustomData)
|
|
27
|
+
return raw;
|
|
28
|
+
if (!isString(raw))
|
|
29
|
+
return raw; // already an object (forward-compat)
|
|
30
|
+
try {
|
|
31
|
+
return JSON.parse(raw);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return raw;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Android invalid-JSON fallback: customPayload holds raw user text.
|
|
38
|
+
if ('customPayload' in body) {
|
|
39
|
+
return body.customPayload;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
function normalize(body, parseCustomData) {
|
|
44
|
+
const platform = detectPlatform(body);
|
|
45
|
+
const customData = extractCustomData(body, parseCustomData);
|
|
46
|
+
return {
|
|
47
|
+
platform,
|
|
48
|
+
triggerName: isString(body.trigger_name) ? body.trigger_name : null,
|
|
49
|
+
triggerType: isString(body.trigger_type) ? body.trigger_type : 'nfc',
|
|
50
|
+
event: isString(body.event) ? body.event : 'tap',
|
|
51
|
+
timestamp: isString(body.timestamp) ? body.timestamp : new Date().toISOString(),
|
|
52
|
+
source: isString(body.source) ? body.source : 'Nodey',
|
|
53
|
+
triggerId: isString(body.triggerId) ? body.triggerId : null,
|
|
54
|
+
customData,
|
|
55
|
+
raw: body,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=normalize-payload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize-payload.js","sourceRoot":"","sources":["../../../nodes/NodeyNfcTrigger/normalize-payload.ts"],"names":[],"mappings":";AAAA,0EAA0E;AAC1E,2EAA2E;;AAqD3E,8BAkBC;AAvDD,MAAM,QAAQ,GAAG,CAAC,CAAU,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC;AAEpE,SAAS,cAAc,CAAC,IAA6B;IACnD,sEAAsE;IACtE,IAAI,cAAc,IAAI,IAAI,IAAI,cAAc,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;QAChF,OAAO,KAAK,CAAC;IACf,CAAC;IACD,wEAAwE;IACxE,IAAI,WAAW,IAAI,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,IAAI,eAAe,IAAI,IAAI,CAAC,EAAE,CAAC;QAC7E,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,8DAA8D;IAC9D,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,iBAAiB,CAAC,IAA6B,EAAE,eAAwB;IAChF,gEAAgE;IAChE,IAAI,YAAY,IAAI,IAAI,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC;QAC5B,IAAI,CAAC,eAAe;YAAE,OAAO,GAAG,CAAC;QACjC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,CAAC,qCAAqC;QACrE,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,GAAG,CAAC;QACb,CAAC;IACH,CAAC;IACD,oEAAoE;IACpE,IAAI,eAAe,IAAI,IAAI,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAgB,SAAS,CACvB,IAA6B,EAC7B,eAAwB;IAExB,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,UAAU,GAAG,iBAAiB,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;IAE5D,OAAO;QACL,QAAQ;QACR,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI;QACnE,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK;QACpE,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK;QAChD,SAAS,EAAE,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC/E,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;QACrD,SAAS,EAAE,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;QAC3D,UAAU;QACV,GAAG,EAAE,IAAI;KACV,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface VerifyOptions {
|
|
2
|
+
rawBody: string;
|
|
3
|
+
secret: string;
|
|
4
|
+
signatureHeader: string | undefined;
|
|
5
|
+
algorithm?: 'sha256' | 'sha512';
|
|
6
|
+
}
|
|
7
|
+
export declare function verifySignature(opts: VerifyOptions): boolean;
|
|
8
|
+
//# sourceMappingURL=verify-signature.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-signature.d.ts","sourceRoot":"","sources":["../../../nodes/NodeyNfcTrigger/verify-signature.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,aAAa;IAG5B,OAAO,EAAE,MAAM,CAAC;IAEhB,MAAM,EAAE,MAAM,CAAC;IAEf,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IAEpC,SAAS,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;CACjC;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAmB5D"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// HMAC signature verification.
|
|
3
|
+
//
|
|
4
|
+
// Nodey does NOT sign webhook requests today — see the developer responses in
|
|
5
|
+
// the HANDOFF.md. This helper is here so the node can opt-in to verification
|
|
6
|
+
// the moment Nodey ships signing without a new package release.
|
|
7
|
+
//
|
|
8
|
+
// Uses node:crypto (allowed by n8n's verified-community-node sandbox).
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.verifySignature = verifySignature;
|
|
11
|
+
const node_crypto_1 = require("node:crypto");
|
|
12
|
+
function verifySignature(opts) {
|
|
13
|
+
const { rawBody, secret, signatureHeader } = opts;
|
|
14
|
+
if (!signatureHeader || !secret)
|
|
15
|
+
return false;
|
|
16
|
+
const algorithm = opts.algorithm ?? 'sha256';
|
|
17
|
+
const provided = signatureHeader.startsWith(`${algorithm}=`)
|
|
18
|
+
? signatureHeader.slice(algorithm.length + 1)
|
|
19
|
+
: signatureHeader;
|
|
20
|
+
const expected = (0, node_crypto_1.createHmac)(algorithm, secret).update(rawBody).digest('hex');
|
|
21
|
+
// timingSafeEqual requires equal-length buffers; bail early otherwise.
|
|
22
|
+
if (provided.length !== expected.length)
|
|
23
|
+
return false;
|
|
24
|
+
try {
|
|
25
|
+
return (0, node_crypto_1.timingSafeEqual)(Buffer.from(provided, 'hex'), Buffer.from(expected, 'hex'));
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=verify-signature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-signature.js","sourceRoot":"","sources":["../../../nodes/NodeyNfcTrigger/verify-signature.ts"],"names":[],"mappings":";AAAA,+BAA+B;AAC/B,EAAE;AACF,8EAA8E;AAC9E,6EAA6E;AAC7E,gEAAgE;AAChE,EAAE;AACF,uEAAuE;;AAgBvE,0CAmBC;AAjCD,6CAA0D;AAc1D,SAAgB,eAAe,CAAC,IAAmB;IACjD,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC;IAClD,IAAI,CAAC,eAAe,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAE9C,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,QAAQ,CAAC;IAC7C,MAAM,QAAQ,GAAG,eAAe,CAAC,UAAU,CAAC,GAAG,SAAS,GAAG,CAAC;QAC1D,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;QAC7C,CAAC,CAAC,eAAe,CAAC;IAEpB,MAAM,QAAQ,GAAG,IAAA,wBAAU,EAAC,SAAS,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAE7E,uEAAuE;IACvE,IAAI,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAEtD,IAAI,CAAC;QACH,OAAO,IAAA,6BAAe,EAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;IACrF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = {};
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-nodey",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "n8n community node that triggers workflows from NFC tag scans in the Nodey mobile app (iOS + Android). Verified-compatible, accepts payloads from both platforms and normalizes them into a single shape.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"n8n",
|
|
8
|
+
"trigger",
|
|
9
|
+
"nfc",
|
|
10
|
+
"webhook",
|
|
11
|
+
"nodey",
|
|
12
|
+
"mobile",
|
|
13
|
+
"automation",
|
|
14
|
+
"ios",
|
|
15
|
+
"android"
|
|
16
|
+
],
|
|
17
|
+
"homepage": "https://github.com/TheFamousHesham/ghost-blocks/tree/master/packages/n8n-node-nodey",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/TheFamousHesham/ghost-blocks.git",
|
|
21
|
+
"directory": "packages/n8n-node-nodey"
|
|
22
|
+
},
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/TheFamousHesham/ghost-blocks/issues"
|
|
25
|
+
},
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"author": "Hesham <hesham@betterbrainlab.org>",
|
|
28
|
+
"main": "index.js",
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc && npm run copy:assets",
|
|
31
|
+
"copy:assets": "node -e \"require('fs').cpSync('nodes', 'dist/nodes', { recursive: true, filter: (s) => !s.endsWith('.ts') })\"",
|
|
32
|
+
"lint": "tsc --noEmit",
|
|
33
|
+
"test": "node --test test/",
|
|
34
|
+
"clean": "rm -rf dist *.tsbuildinfo",
|
|
35
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist"
|
|
39
|
+
],
|
|
40
|
+
"n8n": {
|
|
41
|
+
"n8nNodesApiVersion": 1,
|
|
42
|
+
"credentials": [
|
|
43
|
+
"dist/credentials/NodeyApi.credentials.js"
|
|
44
|
+
],
|
|
45
|
+
"nodes": [
|
|
46
|
+
"dist/nodes/NodeyNfcTrigger/NodeyNfcTrigger.node.js"
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"n8n-workflow": "*"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^20.11.0",
|
|
54
|
+
"n8n-workflow": "^1.30.0",
|
|
55
|
+
"typescript": "^5.3.3"
|
|
56
|
+
},
|
|
57
|
+
"engines": {
|
|
58
|
+
"node": ">=18"
|
|
59
|
+
}
|
|
60
|
+
}
|