n8n-nodes-elearning-magic 0.1.3 → 0.1.5
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
CHANGED
|
@@ -11,11 +11,10 @@ Restart n8n after install.
|
|
|
11
11
|
|
|
12
12
|
## Configure
|
|
13
13
|
1) Add the **eLearning Magic** trigger node.
|
|
14
|
-
2)
|
|
15
|
-
3) Create an **eLearning Magic Signing Secret** credential with your secret.
|
|
16
|
-
4)
|
|
17
|
-
5)
|
|
18
|
-
6) Activate the workflow. Incoming events appear with request body, headers, query, and meta flags for signature/Node ID checks.
|
|
14
|
+
2) Webhook Path auto-fills with a workflow-based value; you can leave it or change it. After you save/activate, the node shows copyable Test/Production URLs (like the n8n Webhook node).
|
|
15
|
+
3) Create an **eLearning Magic Signing Secret** credential with your secret (signature is required).
|
|
16
|
+
4) Copy the webhook URL from the node and paste it into the eLearning Magic app when creating a connection (use the same secret).
|
|
17
|
+
5) Activate the workflow. Incoming events appear with the JSON body plus headers/query and a signature status flag.
|
|
19
18
|
|
|
20
19
|
## Headers expected
|
|
21
20
|
- `X-EM-Signature`: HMAC-SHA256 of the raw body using your signing secret.
|
|
@@ -43,7 +43,6 @@ class ElearningMagicTrigger {
|
|
|
43
43
|
httpMethod: 'POST',
|
|
44
44
|
responseMode: 'onReceived',
|
|
45
45
|
path: '={{$parameter["path"]}}',
|
|
46
|
-
isFullPath: true,
|
|
47
46
|
restartWebhook: true,
|
|
48
47
|
},
|
|
49
48
|
],
|
|
@@ -52,57 +51,8 @@ class ElearningMagicTrigger {
|
|
|
52
51
|
displayName: 'Webhook Path',
|
|
53
52
|
name: 'path',
|
|
54
53
|
type: 'string',
|
|
55
|
-
default: 'elearning-magic',
|
|
56
|
-
description: 'Unique path segment to
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
displayName: 'Node ID (optional)',
|
|
60
|
-
name: 'nodeId',
|
|
61
|
-
type: 'string',
|
|
62
|
-
default: '',
|
|
63
|
-
description: 'If set, the trigger expects the X-EM-Node-Id header to match this value',
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
displayName: 'Enforce Node ID Match',
|
|
67
|
-
name: 'enforceNodeId',
|
|
68
|
-
type: 'boolean',
|
|
69
|
-
default: false,
|
|
70
|
-
description: 'Reject requests whose X-EM-Node-Id header does not match the configured Node ID',
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
displayName: 'Require Signature',
|
|
74
|
-
name: 'requireSignature',
|
|
75
|
-
type: 'boolean',
|
|
76
|
-
default: true,
|
|
77
|
-
description: 'Reject requests without a valid X-EM-Signature HMAC (SHA-256) header',
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
displayName: 'Include Raw Body',
|
|
81
|
-
name: 'includeRawBody',
|
|
82
|
-
type: 'boolean',
|
|
83
|
-
default: false,
|
|
84
|
-
description: 'Attach the raw request body to the emitted item for debugging',
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
displayName: 'Response Code',
|
|
88
|
-
name: 'responseCode',
|
|
89
|
-
type: 'number',
|
|
90
|
-
default: 200,
|
|
91
|
-
typeOptions: {
|
|
92
|
-
minValue: 100,
|
|
93
|
-
maxValue: 599,
|
|
94
|
-
},
|
|
95
|
-
description: 'HTTP status returned to the caller after processing',
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
displayName: 'Response Body',
|
|
99
|
-
name: 'responseBody',
|
|
100
|
-
type: 'string',
|
|
101
|
-
typeOptions: {
|
|
102
|
-
rows: 3,
|
|
103
|
-
},
|
|
104
|
-
default: '{"received":true}',
|
|
105
|
-
description: 'JSON string or plain text to return to the caller',
|
|
54
|
+
default: '={{$workflow.id}}-elearning-magic',
|
|
55
|
+
description: 'Unique path segment; defaults to a workflow-scoped value. Copy the Test/Production URLs after saving.',
|
|
106
56
|
},
|
|
107
57
|
],
|
|
108
58
|
};
|
|
@@ -110,50 +60,28 @@ class ElearningMagicTrigger {
|
|
|
110
60
|
async webhook() {
|
|
111
61
|
const req = this.getRequestObject();
|
|
112
62
|
const res = this.getResponseObject();
|
|
113
|
-
const nodeId = (this.getNodeParameter('nodeId') || '').trim();
|
|
114
|
-
const enforceNodeId = this.getNodeParameter('enforceNodeId');
|
|
115
|
-
const requireSignature = this.getNodeParameter('requireSignature');
|
|
116
|
-
const includeRawBody = this.getNodeParameter('includeRawBody');
|
|
117
|
-
const responseCode = this.getNodeParameter('responseCode');
|
|
118
|
-
const responseBodyParam = this.getNodeParameter('responseBody');
|
|
119
63
|
const credentials = await this.getCredentials('elearningMagicApi');
|
|
120
64
|
const signingSecret = (credentials === null || credentials === void 0 ? void 0 : credentials.signingSecret) || '';
|
|
121
65
|
const rawBody = req.rawBody !== undefined
|
|
122
66
|
? req.rawBody.toString('utf8')
|
|
123
67
|
: JSON.stringify(req.body || {});
|
|
124
68
|
const headerSignature = req.headers['x-em-signature'] || '';
|
|
125
|
-
const headerNodeId = req.headers['x-em-node-id'] || '';
|
|
126
69
|
let signatureValid = false;
|
|
127
70
|
if (headerSignature && signingSecret) {
|
|
128
71
|
const computed = computeSignature(signingSecret, rawBody);
|
|
129
72
|
signatureValid = safeCompare(computed, headerSignature);
|
|
130
73
|
}
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
if (!headerSignature || !signatureValid) {
|
|
139
|
-
res.status(403);
|
|
140
|
-
return {
|
|
141
|
-
webhookResponse: { error: 'Invalid or missing X-EM-Signature header' },
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
let nodeIdMatched = true;
|
|
146
|
-
if (enforceNodeId) {
|
|
147
|
-
nodeIdMatched = !!nodeId && !!headerNodeId && nodeId === headerNodeId;
|
|
148
|
-
if (!nodeIdMatched) {
|
|
149
|
-
res.status(403);
|
|
150
|
-
return {
|
|
151
|
-
webhookResponse: { error: 'X-EM-Node-Id header did not match the configured Node ID' },
|
|
152
|
-
};
|
|
153
|
-
}
|
|
74
|
+
if (!signingSecret) {
|
|
75
|
+
res.status(401);
|
|
76
|
+
return {
|
|
77
|
+
webhookResponse: { error: 'Signing secret is not configured on this node' },
|
|
78
|
+
};
|
|
154
79
|
}
|
|
155
|
-
|
|
156
|
-
|
|
80
|
+
if (!headerSignature || !signatureValid) {
|
|
81
|
+
res.status(403);
|
|
82
|
+
return {
|
|
83
|
+
webhookResponse: { error: 'Invalid or missing X-EM-Signature header' },
|
|
84
|
+
};
|
|
157
85
|
}
|
|
158
86
|
const payload = {
|
|
159
87
|
json: {
|
|
@@ -162,30 +90,14 @@ class ElearningMagicTrigger {
|
|
|
162
90
|
headers: req.headers,
|
|
163
91
|
meta: {
|
|
164
92
|
receivedAt: new Date().toISOString(),
|
|
165
|
-
signatureValid
|
|
166
|
-
nodeIdMatched,
|
|
93
|
+
signatureValid,
|
|
167
94
|
webhookPath: req.url,
|
|
168
95
|
},
|
|
169
96
|
},
|
|
170
97
|
};
|
|
171
|
-
|
|
172
|
-
payload.json.rawBody = rawBody;
|
|
173
|
-
}
|
|
174
|
-
let responseBody = responseBodyParam;
|
|
175
|
-
if (typeof responseBodyParam === 'string') {
|
|
176
|
-
const trimmed = responseBodyParam.trim();
|
|
177
|
-
if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
|
|
178
|
-
try {
|
|
179
|
-
responseBody = JSON.parse(trimmed);
|
|
180
|
-
}
|
|
181
|
-
catch {
|
|
182
|
-
responseBody = responseBodyParam;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
res.status(responseCode || 200);
|
|
98
|
+
res.status(200);
|
|
187
99
|
return {
|
|
188
|
-
webhookResponse:
|
|
100
|
+
webhookResponse: { received: true },
|
|
189
101
|
workflowData: [this.helpers.returnJsonArray([payload.json])],
|
|
190
102
|
};
|
|
191
103
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" width="239.09" height="241.78" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 239.09 241.78"><defs><style>.cls-1{fill:url(#linear-gradient);}</style><linearGradient id="linear-gradient" x1="0" y1="120.89" x2="239.09" y2="120.89" gradientUnits="userSpaceOnUse"><stop offset=".32" stop-color="#bf00a7"/><stop offset="1" stop-color="#5c00e9"/></linearGradient></defs><path class="cls-1" d="M.06,240.33c21.58-25.98,50.53-58.9,72.12-84.88,12.66-15.23,29.3-33.54,42.08-48.93-18.77,0-43.96,1.69-62.81,1.69,5.16-5.47,10.32-10.94,15.48-16.42,3.31-3.51,6.59-7.07,9.9-10.58,2.42-2.57,8.32-4.31,11.56-4.88,10.3-1.79,20.85-3.72,31.17-4.13,3.53-.14,6.18-.93,8.72-3.65,14.34-15.3,28.09-28.92,42.69-43.97,6.87-7.08,15.84-11.12,24.76-14.63C208.26,5,221.41,1.89,234.85.1c3.62-.48,4.47.83,4.2,4.04-.77,9.15-3,18.16-5.81,26.68-3.57,10.93-6.43,20.35-11.72,30.71-1.15,2.49-3.53,5.97-5.87,8.43-13.91,14.58-26.75,29.94-40.74,44.44-2.55,2.65-3.5,5.31-3.3,8.87.59,10.4.54,21.57-2.77,31.45-.41,1.24-.84,2.62-1.68,3.54-8.75,9.54-19,17.98-28.71,28.43-.63-20.82-1.23-40.44-1.84-60.72C91.18,159.51,41.84,203.15,1.47,241.71c-1.17.26-1.65-.2-1.42-1.38Z"/></svg>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-elearning-magic",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "n8n community node for receiving signed payloads from the eLearning Magic SCORM wrapper",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n-community-node",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"LICENSE"
|
|
27
27
|
],
|
|
28
28
|
"scripts": {
|
|
29
|
-
"build": "tsc",
|
|
29
|
+
"build": "tsc && mkdir -p dist/nodes/ElearningMagic && cp src/nodes/ElearningMagic/elearningMagic.svg dist/nodes/ElearningMagic/",
|
|
30
30
|
"prepare": "npm run build"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|