n8n-nodes-sendit 1.0.3 → 1.0.4
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.
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SendIt = void 0;
|
|
4
|
+
// Base URL for SendIt API
|
|
5
|
+
const SENDIT_API_BASE_URL = 'https://sendit.infiniteappsai.com/api/v1';
|
|
4
6
|
class SendIt {
|
|
5
7
|
constructor() {
|
|
6
8
|
this.description = {
|
|
@@ -23,7 +25,7 @@ class SendIt {
|
|
|
23
25
|
},
|
|
24
26
|
],
|
|
25
27
|
requestDefaults: {
|
|
26
|
-
baseURL:
|
|
28
|
+
baseURL: SENDIT_API_BASE_URL,
|
|
27
29
|
headers: {
|
|
28
30
|
Accept: 'application/json',
|
|
29
31
|
'Content-Type': 'application/json',
|
|
@@ -52,6 +54,10 @@ class SendIt {
|
|
|
52
54
|
name: 'Validation',
|
|
53
55
|
value: 'validation',
|
|
54
56
|
},
|
|
57
|
+
{
|
|
58
|
+
name: 'Analytics',
|
|
59
|
+
value: 'analytics',
|
|
60
|
+
},
|
|
55
61
|
],
|
|
56
62
|
default: 'post',
|
|
57
63
|
},
|
|
@@ -157,6 +163,27 @@ class SendIt {
|
|
|
157
163
|
],
|
|
158
164
|
default: 'validate',
|
|
159
165
|
},
|
|
166
|
+
// Analytics operations
|
|
167
|
+
{
|
|
168
|
+
displayName: 'Operation',
|
|
169
|
+
name: 'operation',
|
|
170
|
+
type: 'options',
|
|
171
|
+
noDataExpression: true,
|
|
172
|
+
displayOptions: {
|
|
173
|
+
show: {
|
|
174
|
+
resource: ['analytics'],
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
options: [
|
|
178
|
+
{
|
|
179
|
+
name: 'Get Analytics',
|
|
180
|
+
value: 'getAnalytics',
|
|
181
|
+
description: 'Get engagement analytics for posts on a platform',
|
|
182
|
+
action: 'Get analytics for a platform',
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
default: 'getAnalytics',
|
|
186
|
+
},
|
|
160
187
|
// Platforms field
|
|
161
188
|
{
|
|
162
189
|
displayName: 'Platforms',
|
|
@@ -263,6 +290,28 @@ class SendIt {
|
|
|
263
290
|
},
|
|
264
291
|
description: 'Filter scheduled posts by platform',
|
|
265
292
|
},
|
|
293
|
+
// Analytics platform selector
|
|
294
|
+
{
|
|
295
|
+
displayName: 'Platform',
|
|
296
|
+
name: 'analyticsPlatform',
|
|
297
|
+
type: 'options',
|
|
298
|
+
options: [
|
|
299
|
+
{ name: 'LinkedIn', value: 'linkedin' },
|
|
300
|
+
{ name: 'Instagram', value: 'instagram' },
|
|
301
|
+
{ name: 'Threads', value: 'threads' },
|
|
302
|
+
{ name: 'TikTok', value: 'tiktok' },
|
|
303
|
+
{ name: 'X (Twitter)', value: 'x' },
|
|
304
|
+
],
|
|
305
|
+
default: 'linkedin',
|
|
306
|
+
required: true,
|
|
307
|
+
displayOptions: {
|
|
308
|
+
show: {
|
|
309
|
+
resource: ['analytics'],
|
|
310
|
+
operation: ['getAnalytics'],
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
description: 'The platform to get analytics for',
|
|
314
|
+
},
|
|
266
315
|
// Additional options
|
|
267
316
|
{
|
|
268
317
|
displayName: 'Additional Options',
|
|
@@ -328,8 +377,10 @@ class SendIt {
|
|
|
328
377
|
};
|
|
329
378
|
response = await this.helpers.httpRequestWithAuthentication.call(this, 'sendItApi', {
|
|
330
379
|
method: 'POST',
|
|
380
|
+
baseURL: SENDIT_API_BASE_URL,
|
|
331
381
|
url: '/publish',
|
|
332
382
|
body,
|
|
383
|
+
json: true,
|
|
333
384
|
});
|
|
334
385
|
}
|
|
335
386
|
}
|
|
@@ -341,6 +392,7 @@ class SendIt {
|
|
|
341
392
|
const scheduledTime = this.getNodeParameter('scheduledTime', i);
|
|
342
393
|
response = await this.helpers.httpRequestWithAuthentication.call(this, 'sendItApi', {
|
|
343
394
|
method: 'POST',
|
|
395
|
+
baseURL: SENDIT_API_BASE_URL,
|
|
344
396
|
url: '/schedule',
|
|
345
397
|
body: {
|
|
346
398
|
platforms,
|
|
@@ -350,6 +402,7 @@ class SendIt {
|
|
|
350
402
|
},
|
|
351
403
|
scheduledTime,
|
|
352
404
|
},
|
|
405
|
+
json: true,
|
|
353
406
|
});
|
|
354
407
|
}
|
|
355
408
|
else if (operation === 'getAll') {
|
|
@@ -360,6 +413,7 @@ class SendIt {
|
|
|
360
413
|
}
|
|
361
414
|
response = await this.helpers.httpRequestWithAuthentication.call(this, 'sendItApi', {
|
|
362
415
|
method: 'GET',
|
|
416
|
+
baseURL: SENDIT_API_BASE_URL,
|
|
363
417
|
url: '/scheduled',
|
|
364
418
|
qs,
|
|
365
419
|
});
|
|
@@ -368,6 +422,7 @@ class SendIt {
|
|
|
368
422
|
const scheduleId = this.getNodeParameter('scheduleId', i);
|
|
369
423
|
response = await this.helpers.httpRequestWithAuthentication.call(this, 'sendItApi', {
|
|
370
424
|
method: 'DELETE',
|
|
425
|
+
baseURL: SENDIT_API_BASE_URL,
|
|
371
426
|
url: `/scheduled/${scheduleId}`,
|
|
372
427
|
});
|
|
373
428
|
}
|
|
@@ -375,6 +430,7 @@ class SendIt {
|
|
|
375
430
|
const scheduleId = this.getNodeParameter('scheduleId', i);
|
|
376
431
|
response = await this.helpers.httpRequestWithAuthentication.call(this, 'sendItApi', {
|
|
377
432
|
method: 'POST',
|
|
433
|
+
baseURL: SENDIT_API_BASE_URL,
|
|
378
434
|
url: `/scheduled/${scheduleId}/trigger`,
|
|
379
435
|
});
|
|
380
436
|
}
|
|
@@ -383,6 +439,7 @@ class SendIt {
|
|
|
383
439
|
if (operation === 'getAll') {
|
|
384
440
|
response = await this.helpers.httpRequestWithAuthentication.call(this, 'sendItApi', {
|
|
385
441
|
method: 'GET',
|
|
442
|
+
baseURL: SENDIT_API_BASE_URL,
|
|
386
443
|
url: '/accounts',
|
|
387
444
|
});
|
|
388
445
|
}
|
|
@@ -395,6 +452,7 @@ class SendIt {
|
|
|
395
452
|
const additionalOptions = this.getNodeParameter('additionalOptions', i);
|
|
396
453
|
response = await this.helpers.httpRequestWithAuthentication.call(this, 'sendItApi', {
|
|
397
454
|
method: 'POST',
|
|
455
|
+
baseURL: SENDIT_API_BASE_URL,
|
|
398
456
|
url: '/validate',
|
|
399
457
|
body: {
|
|
400
458
|
platforms,
|
|
@@ -407,6 +465,20 @@ class SendIt {
|
|
|
407
465
|
mediaType: additionalOptions.mediaType || 'auto',
|
|
408
466
|
},
|
|
409
467
|
},
|
|
468
|
+
json: true,
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
else if (resource === 'analytics') {
|
|
473
|
+
if (operation === 'getAnalytics') {
|
|
474
|
+
const platform = this.getNodeParameter('analyticsPlatform', i);
|
|
475
|
+
response = await this.helpers.httpRequestWithAuthentication.call(this, 'sendItApi', {
|
|
476
|
+
method: 'GET',
|
|
477
|
+
baseURL: SENDIT_API_BASE_URL,
|
|
478
|
+
url: '/analytics',
|
|
479
|
+
qs: {
|
|
480
|
+
platform,
|
|
481
|
+
},
|
|
410
482
|
});
|
|
411
483
|
}
|
|
412
484
|
}
|
|
@@ -1,6 +1,55 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.SendItTrigger = void 0;
|
|
7
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
8
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
9
|
+
// Base URL for SendIt API
|
|
10
|
+
const SENDIT_API_BASE_URL = 'https://sendit.infiniteappsai.com/api/v1';
|
|
11
|
+
// Signature tolerance in seconds (5 minutes)
|
|
12
|
+
const SIGNATURE_TOLERANCE_SECONDS = 300;
|
|
13
|
+
/**
|
|
14
|
+
* Verify webhook signature using HMAC-SHA256
|
|
15
|
+
* Signature format: t=<timestamp>,v1=<hex_signature>
|
|
16
|
+
*/
|
|
17
|
+
function verifyWebhookSignature(payload, signature, secret) {
|
|
18
|
+
try {
|
|
19
|
+
const parts = signature.split(',');
|
|
20
|
+
const timestampPart = parts.find((p) => p.startsWith('t='));
|
|
21
|
+
const signaturePart = parts.find((p) => p.startsWith('v1='));
|
|
22
|
+
if (!timestampPart || !signaturePart) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const timestamp = parseInt(timestampPart.substring(2), 10);
|
|
26
|
+
const expectedSignature = signaturePart.substring(3);
|
|
27
|
+
// Check timestamp tolerance
|
|
28
|
+
const now = Math.floor(Date.now() / 1000);
|
|
29
|
+
if (Math.abs(now - timestamp) > SIGNATURE_TOLERANCE_SECONDS) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
// Compute expected signature
|
|
33
|
+
const signedPayload = `${timestamp}.${payload}`;
|
|
34
|
+
const computedSignature = crypto_1.default
|
|
35
|
+
.createHmac('sha256', secret)
|
|
36
|
+
.update(signedPayload)
|
|
37
|
+
.digest('hex');
|
|
38
|
+
// Use timing-safe comparison
|
|
39
|
+
if (expectedSignature.length !== computedSignature.length) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const expectedBuf = Buffer.from(expectedSignature, 'hex');
|
|
43
|
+
const computedBuf = Buffer.from(computedSignature, 'hex');
|
|
44
|
+
if (expectedBuf.length !== computedBuf.length) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return crypto_1.default.timingSafeEqual(expectedBuf, computedBuf);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
4
53
|
class SendItTrigger {
|
|
5
54
|
constructor() {
|
|
6
55
|
this.description = {
|
|
@@ -84,7 +133,8 @@ class SendItTrigger {
|
|
|
84
133
|
try {
|
|
85
134
|
const response = await this.helpers.httpRequestWithAuthentication.call(this, 'sendItApi', {
|
|
86
135
|
method: 'GET',
|
|
87
|
-
|
|
136
|
+
baseURL: SENDIT_API_BASE_URL,
|
|
137
|
+
url: `/webhooks/${webhookData.webhookId}`,
|
|
88
138
|
});
|
|
89
139
|
if (response && ((_a = response.webhook) === null || _a === void 0 ? void 0 : _a.url) === webhookUrl) {
|
|
90
140
|
return true;
|
|
@@ -107,8 +157,12 @@ class SendItTrigger {
|
|
|
107
157
|
};
|
|
108
158
|
const response = await this.helpers.httpRequestWithAuthentication.call(this, 'sendItApi', {
|
|
109
159
|
method: 'POST',
|
|
110
|
-
|
|
111
|
-
|
|
160
|
+
baseURL: SENDIT_API_BASE_URL,
|
|
161
|
+
url: '/webhooks',
|
|
162
|
+
headers: {
|
|
163
|
+
'Content-Type': 'application/json',
|
|
164
|
+
},
|
|
165
|
+
body: JSON.stringify(body),
|
|
112
166
|
});
|
|
113
167
|
const webhookResponse = response;
|
|
114
168
|
if ((_a = webhookResponse.webhook) === null || _a === void 0 ? void 0 : _a.id) {
|
|
@@ -124,7 +178,8 @@ class SendItTrigger {
|
|
|
124
178
|
try {
|
|
125
179
|
await this.helpers.httpRequestWithAuthentication.call(this, 'sendItApi', {
|
|
126
180
|
method: 'DELETE',
|
|
127
|
-
|
|
181
|
+
baseURL: SENDIT_API_BASE_URL,
|
|
182
|
+
url: `/webhooks/${webhookData.webhookId}`,
|
|
128
183
|
});
|
|
129
184
|
}
|
|
130
185
|
catch {
|
|
@@ -139,7 +194,24 @@ class SendItTrigger {
|
|
|
139
194
|
};
|
|
140
195
|
}
|
|
141
196
|
async webhook() {
|
|
197
|
+
const req = this.getRequestObject();
|
|
198
|
+
const webhookData = this.getWorkflowStaticData('node');
|
|
199
|
+
const secret = webhookData.webhookSecret;
|
|
200
|
+
// Get the signature header
|
|
201
|
+
const signature = req.headers['x-sendit-signature'];
|
|
202
|
+
// Get raw body for signature verification
|
|
142
203
|
const bodyData = this.getBodyData();
|
|
204
|
+
const rawBody = JSON.stringify(bodyData);
|
|
205
|
+
// Verify signature if we have a secret
|
|
206
|
+
if (secret) {
|
|
207
|
+
if (!signature) {
|
|
208
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Missing X-SendIt-Signature header. Webhook request rejected.');
|
|
209
|
+
}
|
|
210
|
+
const isValid = verifyWebhookSignature(rawBody, signature, secret);
|
|
211
|
+
if (!isValid) {
|
|
212
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid webhook signature. Request may be tampered with or expired.');
|
|
213
|
+
}
|
|
214
|
+
}
|
|
143
215
|
return {
|
|
144
216
|
workflowData: [
|
|
145
217
|
this.helpers.returnJsonArray(bodyData),
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 60 60">
|
|
2
|
+
<rect width="60" height="60" rx="12" fill="#4F46E5"/>
|
|
3
|
+
<path d="M15 30L25 40L45 20" stroke="white" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
|
4
|
+
<path d="M30 15L42 27" stroke="white" stroke-width="3" stroke-linecap="round" opacity="0.6"/>
|
|
5
|
+
<path d="M18 33L33 45" stroke="white" stroke-width="3" stroke-linecap="round" opacity="0.6"/>
|
|
6
|
+
</svg>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-sendit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "n8n community node for SendIt - Multi-platform social media publishing",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n-community-node-package",
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"tiktok",
|
|
12
12
|
"twitter",
|
|
13
13
|
"publishing",
|
|
14
|
-
"scheduling"
|
|
14
|
+
"scheduling",
|
|
15
|
+
"analytics"
|
|
15
16
|
],
|
|
16
17
|
"license": "MIT",
|
|
17
18
|
"homepage": "https://sendit.infiniteappsai.com",
|
|
@@ -26,7 +27,7 @@
|
|
|
26
27
|
"main": "index.js",
|
|
27
28
|
"scripts": {
|
|
28
29
|
"build": "tsc && npm run copy:icons",
|
|
29
|
-
"copy:icons": "mkdir -p dist/nodes/SendIt && cp nodes/SendIt/*.svg dist/nodes/SendIt/
|
|
30
|
+
"copy:icons": "mkdir -p dist/nodes/SendIt && cp nodes/SendIt/*.svg dist/nodes/SendIt/",
|
|
30
31
|
"dev": "tsc --watch",
|
|
31
32
|
"format": "prettier nodes credentials --write",
|
|
32
33
|
"lint": "eslint nodes credentials --ext .ts --fix",
|