n8n-nodes-influtics 1.0.0 → 1.0.2
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/dist/credentials/InfluticsApi.credentials.js +25 -0
- package/dist/credentials/InfluticsApi.credentials.js.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/nodes/GenericFunctions.js +81 -0
- package/dist/nodes/GenericFunctions.js.map +1 -0
- package/dist/nodes/InfluticsAccount/InfluticsAccount.node.js +135 -0
- package/dist/nodes/InfluticsAccount/InfluticsAccount.node.js.map +1 -0
- package/dist/nodes/InfluticsAccount/influtics.svg +5 -0
- package/dist/nodes/InfluticsBlogger/InfluticsBlogger.node.js +248 -0
- package/dist/nodes/InfluticsBlogger/InfluticsBlogger.node.js.map +1 -0
- package/dist/nodes/InfluticsBlogger/influtics.svg +5 -0
- package/dist/nodes/InfluticsTrend/InfluticsTrend.node.js +200 -0
- package/dist/nodes/InfluticsTrend/InfluticsTrend.node.js.map +1 -0
- package/dist/nodes/InfluticsTrend/influtics.svg +5 -0
- package/dist/nodes/InfluticsVideo/InfluticsVideo.node.js +411 -0
- package/dist/nodes/InfluticsVideo/InfluticsVideo.node.js.map +1 -0
- package/dist/nodes/InfluticsVideo/influtics.svg +5 -0
- package/package.json +2 -1
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class InfluticsApi {
|
|
2
|
+
name = 'influticsApi';
|
|
3
|
+
displayName = 'Influtics API';
|
|
4
|
+
documentationUrl = 'https://docs.influtics.com/';
|
|
5
|
+
properties = [
|
|
6
|
+
{
|
|
7
|
+
displayName: 'API Key',
|
|
8
|
+
name: 'apiKey',
|
|
9
|
+
type: 'string',
|
|
10
|
+
typeOptions: { password: true },
|
|
11
|
+
default: '',
|
|
12
|
+
required: true,
|
|
13
|
+
description: 'Get your API key from the Influtics dashboard under Settings → API',
|
|
14
|
+
},
|
|
15
|
+
];
|
|
16
|
+
authenticate = {
|
|
17
|
+
type: 'generic',
|
|
18
|
+
properties: {
|
|
19
|
+
headers: {
|
|
20
|
+
Authorization: '=Bearer {{ $credentials.apiKey }}',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=InfluticsApi.credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InfluticsApi.credentials.js","sourceRoot":"","sources":["../../credentials/InfluticsApi.credentials.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,YAAY;IACvB,IAAI,GAAG,cAAc,CAAC;IACtB,WAAW,GAAG,eAAe,CAAC;IAC9B,gBAAgB,GAAG,6BAA6B,CAAC;IACjD,UAAU,GAAsB;QAC9B;YACE,WAAW,EAAE,SAAS;YACtB,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;YAC/B,OAAO,EAAE,EAAE;YACX,QAAQ,EAAE,IAAI;YACd,WAAW,EAAE,oEAAoE;SAClF;KACF,CAAC;IACF,YAAY,GAAG;QACb,IAAI,EAAE,SAAS;QACf,UAAU,EAAE;YACV,OAAO,EAAE;gBACP,aAAa,EAAE,mCAAmC;aACnD;SACF;KACO,CAAC;CACZ"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package entry point.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports every node class and credential so `import 'n8n-nodes-influtics'`
|
|
5
|
+
* (the value `package.json: "main"` resolves to) gives consumers the full
|
|
6
|
+
* surface in one named-import namespace. n8n's community-node loader reads
|
|
7
|
+
* `main` to discover the package; without this file `dist/index.js` is
|
|
8
|
+
* missing and the loader surfaces a parse error on the missing entry.
|
|
9
|
+
*
|
|
10
|
+
* Per the official `@n8n/node-cli` template (`npx n8n-node new`).
|
|
11
|
+
*/
|
|
12
|
+
export { InfluticsApi } from './credentials/InfluticsApi.credentials.js';
|
|
13
|
+
export { InfluticsVideo } from './nodes/InfluticsVideo/InfluticsVideo.node.js';
|
|
14
|
+
export { InfluticsBlogger } from './nodes/InfluticsBlogger/InfluticsBlogger.node.js';
|
|
15
|
+
export { InfluticsTrend } from './nodes/InfluticsTrend/InfluticsTrend.node.js';
|
|
16
|
+
export { InfluticsAccount } from './nodes/InfluticsAccount/InfluticsAccount.node.js';
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,2CAA2C,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,+CAA+C,CAAC;AAC/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,mDAAmD,CAAC;AACrF,OAAO,EAAE,cAAc,EAAE,MAAM,+CAA+C,CAAC;AAC/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,mDAAmD,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { NodeApiError } from 'n8n-workflow';
|
|
2
|
+
export const INFLUTICS_API_BASE_URL = 'https://api.influtics.com';
|
|
3
|
+
export const CREDENTIAL_NAME = 'influticsApi';
|
|
4
|
+
function extractApiErrorBody(error) {
|
|
5
|
+
const body = error?.response?.body;
|
|
6
|
+
const inner = body?.error;
|
|
7
|
+
return {
|
|
8
|
+
code: inner?.code,
|
|
9
|
+
message: inner?.message,
|
|
10
|
+
upgrade_url: inner?.upgrade_url,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export function mapInfluticsError(error) {
|
|
14
|
+
const { code, message, upgrade_url } = extractApiErrorBody(error);
|
|
15
|
+
if (code || message) {
|
|
16
|
+
const descParts = [];
|
|
17
|
+
if (upgrade_url)
|
|
18
|
+
descParts.push(`Upgrade: ${upgrade_url}`);
|
|
19
|
+
const finalMessage = `${code ?? 'API_ERROR'}: ${message ?? 'Unknown error'}`;
|
|
20
|
+
const e = new Error(finalMessage);
|
|
21
|
+
e.description = descParts.join('\n') || undefined;
|
|
22
|
+
return e;
|
|
23
|
+
}
|
|
24
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
25
|
+
}
|
|
26
|
+
export async function influticsApiRequest(method, endpoint, body, qs) {
|
|
27
|
+
const options = {
|
|
28
|
+
method,
|
|
29
|
+
url: `${INFLUTICS_API_BASE_URL}${endpoint}`,
|
|
30
|
+
headers: {
|
|
31
|
+
Accept: 'application/json',
|
|
32
|
+
'Content-Type': 'application/json',
|
|
33
|
+
// Authorization header is injected by the credential's `authenticate` block.
|
|
34
|
+
},
|
|
35
|
+
json: true,
|
|
36
|
+
...(body ? { body } : {}),
|
|
37
|
+
...(qs ? { qs } : {}),
|
|
38
|
+
};
|
|
39
|
+
try {
|
|
40
|
+
const response = await this.helpers.requestWithAuthentication.call(this, CREDENTIAL_NAME, options);
|
|
41
|
+
return response;
|
|
42
|
+
}
|
|
43
|
+
catch (rawError) {
|
|
44
|
+
// Map the raw helper error into { code, message, description }. The description
|
|
45
|
+
// carries the upgrade URL on 402 PAID_PLAN_REQUIRED so it surfaces in the n8n UI
|
|
46
|
+
// without code-fishing. Both message AND description MUST go through NodeApiError's
|
|
47
|
+
// options bag, since the wrapped `rawError` itself has no description on it.
|
|
48
|
+
const mapped = mapInfluticsError(rawError);
|
|
49
|
+
throw new NodeApiError(this.getNode(), rawError, {
|
|
50
|
+
message: mapped.message,
|
|
51
|
+
description: mapped.description,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Cursor-aware paginator for endpoints that expose `next_cursor`. Influtics
|
|
57
|
+
* returns the cursor under `meta.next_cursor`; we fold every page's `data`
|
|
58
|
+
* into a single array so downstream n8n nodes see one item per record.
|
|
59
|
+
*
|
|
60
|
+
* Endpoints that don't paginate today just return a single page (no cursor);
|
|
61
|
+
* in that case the caller still gets back one combined array.
|
|
62
|
+
*/
|
|
63
|
+
export async function influticsApiRequestAllItems(method, endpoint, qs) {
|
|
64
|
+
const aggregated = [];
|
|
65
|
+
let cursor;
|
|
66
|
+
// Hard cap on pages so a runaway cursor can't DOS the workflow.
|
|
67
|
+
for (let page = 0; page < 50; page++) {
|
|
68
|
+
const pageQs = { ...(qs ?? {}) };
|
|
69
|
+
if (cursor)
|
|
70
|
+
pageQs.cursor = cursor;
|
|
71
|
+
const response = await influticsApiRequest.call(this, method, endpoint, undefined, pageQs);
|
|
72
|
+
const items = Array.isArray(response?.data) ? response.data : [];
|
|
73
|
+
aggregated.push(...items);
|
|
74
|
+
const next = response?.meta?.next_cursor;
|
|
75
|
+
if (!next)
|
|
76
|
+
break;
|
|
77
|
+
cursor = next;
|
|
78
|
+
}
|
|
79
|
+
return aggregated;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=GenericFunctions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GenericFunctions.js","sourceRoot":"","sources":["../../nodes/GenericFunctions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Influtics Account node.
|
|
3
|
+
*
|
|
4
|
+
* Implementation choices:
|
|
5
|
+
* - File lives at nodes/InfluticsAccount/InfluticsAccount.node.ts — required by
|
|
6
|
+
* the eslint-plugin-n8n-nodes-base `node-dirname-against-convention` rule.
|
|
7
|
+
* - `executeInfluticsAccount` is also exported as a named function so unit tests
|
|
8
|
+
* can drive the executor without instantiating the INodeType class.
|
|
9
|
+
* - Two read-only operations: Get Usage + Get Limits. Both endpoints take no
|
|
10
|
+
* user input (no query params, no body) — there is nothing to guard against
|
|
11
|
+
* on the wire, so neither handler needs defensive validation.
|
|
12
|
+
* - Both endpoints cost 0 credits and are exempt from the paid-plan gate,
|
|
13
|
+
* so free-tier callers can reach them. Public docs:
|
|
14
|
+
* https://docs.influtics.com/
|
|
15
|
+
* - The unimplemented-operation branch keeps the executor safe if a future
|
|
16
|
+
* version's parameters somehow leak an unknown value.
|
|
17
|
+
*
|
|
18
|
+
* Backend contract (public docs: https://docs.influtics.com/):
|
|
19
|
+
* GET /v1/account/usage
|
|
20
|
+
* query: NONE. body: NONE.
|
|
21
|
+
* 200 → {
|
|
22
|
+
* success: true,
|
|
23
|
+
* data: {
|
|
24
|
+
* usage_history: [...rows from daily_api_usage over the last 30 days...],
|
|
25
|
+
* summary: {
|
|
26
|
+
* plan: "free"|"pro"|"business"|null,
|
|
27
|
+
* is_unlimited: boolean,
|
|
28
|
+
* videos: { limit: number|null, used: number|null },
|
|
29
|
+
* credits: { total: number, used: number }
|
|
30
|
+
* }
|
|
31
|
+
* },
|
|
32
|
+
* meta: { processing_time_ms, request_id }
|
|
33
|
+
* }
|
|
34
|
+
* Errors: 401 UNAUTHORIZED, 429 RATE_LIMITED.
|
|
35
|
+
*
|
|
36
|
+
* GET /v1/account/limits
|
|
37
|
+
* query: NONE. body: NONE.
|
|
38
|
+
* 200 → {
|
|
39
|
+
* success: true,
|
|
40
|
+
* data: {
|
|
41
|
+
* rate_limits: {
|
|
42
|
+
* requests_per_minute: number,
|
|
43
|
+
* requests_per_hour: number,
|
|
44
|
+
* requests_per_day: number,
|
|
45
|
+
* requests_per_month: number,
|
|
46
|
+
* burst_allowance: number
|
|
47
|
+
* }
|
|
48
|
+
* },
|
|
49
|
+
* meta: { processing_time_ms, request_id }
|
|
50
|
+
* }
|
|
51
|
+
* Server-side default rate limits (when no override is configured):
|
|
52
|
+
* { requests_per_minute: 60, requests_per_hour: 3600,
|
|
53
|
+
* requests_per_day: 86400, requests_per_month: 10000,
|
|
54
|
+
* burst_allowance: 120 }.
|
|
55
|
+
* Errors: 401 UNAUTHORIZED, 429 RATE_LIMITED.
|
|
56
|
+
*/
|
|
57
|
+
import { NodeOperationError, } from 'n8n-workflow';
|
|
58
|
+
import { influticsApiRequest } from '../GenericFunctions.js';
|
|
59
|
+
const OPERATIONS = {
|
|
60
|
+
getUsage: async function (_i) {
|
|
61
|
+
// No input → no defensive guards needed. The executor calls the API
|
|
62
|
+
// directly and returns the raw envelope so downstream n8n consumers see
|
|
63
|
+
// exactly what the API sent (mirrors the Track / Get Stats patterns).
|
|
64
|
+
const response = await influticsApiRequest.call(this, 'GET', '/v1/account/usage');
|
|
65
|
+
return response;
|
|
66
|
+
},
|
|
67
|
+
getLimits: async function (_i) {
|
|
68
|
+
// No input → no defensive guards needed. Same wire shape as Get Usage:
|
|
69
|
+
// a single GET with no qs / no body.
|
|
70
|
+
const response = await influticsApiRequest.call(this, 'GET', '/v1/account/limits');
|
|
71
|
+
return response;
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
export async function executeInfluticsAccount(_items) {
|
|
75
|
+
const operation = this.getNodeParameter('operation', 0);
|
|
76
|
+
const handler = OPERATIONS[operation];
|
|
77
|
+
if (!handler) {
|
|
78
|
+
throw new NodeOperationError(this.getNode(), `Operation "${operation}" not yet implemented in InfluticsAccount node`);
|
|
79
|
+
}
|
|
80
|
+
// Both ops are single-batch: one GET per workflow run regardless of input
|
|
81
|
+
// item count. Mirrors the Track-videos and Track-bloggers single-batch
|
|
82
|
+
// pattern from InfluticsVideo / InfluticsBlogger.
|
|
83
|
+
const response = await handler.call(this, 0);
|
|
84
|
+
return [[{ json: response }]];
|
|
85
|
+
}
|
|
86
|
+
export class InfluticsAccount {
|
|
87
|
+
description = {
|
|
88
|
+
displayName: 'Influtics Account',
|
|
89
|
+
name: 'influticsAccount',
|
|
90
|
+
icon: 'file:influtics.svg',
|
|
91
|
+
group: ['transform'],
|
|
92
|
+
version: 1,
|
|
93
|
+
description: 'Read Influtics account usage and limits',
|
|
94
|
+
// eslint-plugin-n8n-nodes-base `node-class-description-missing-subtitle`
|
|
95
|
+
// requires a subtitle when every property is `displayName: 'Operation'`
|
|
96
|
+
// (sibling nodes like InfluticsVideo skip the rule because they have
|
|
97
|
+
// additional non-Operation fields). Map the raw operation value to its
|
|
98
|
+
// displayName so the canvas renders the friendly name rather than the
|
|
99
|
+
// enum value.
|
|
100
|
+
subtitle: '={{ $parameter["operation"] === "getUsage" ? "Get Usage" : "Get Limits" }}',
|
|
101
|
+
defaults: { name: 'Influtics Account' },
|
|
102
|
+
inputs: ['main'],
|
|
103
|
+
outputs: ['main'],
|
|
104
|
+
credentials: [{ name: 'influticsApi', required: true }],
|
|
105
|
+
properties: [
|
|
106
|
+
{
|
|
107
|
+
displayName: 'Operation',
|
|
108
|
+
name: 'operation',
|
|
109
|
+
type: 'options',
|
|
110
|
+
noDataExpression: true,
|
|
111
|
+
// Alphabetized per eslint-plugin-n8n-nodes-base `node-param-options-type-unsorted-items`.
|
|
112
|
+
// Each option carries an `action:` (rule `node-param-operation-option-action-miscased`).
|
|
113
|
+
options: [
|
|
114
|
+
{
|
|
115
|
+
name: 'Get Limits',
|
|
116
|
+
value: 'getLimits',
|
|
117
|
+
description: 'Read rate limit configuration',
|
|
118
|
+
action: 'Read rate limit configuration',
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'Get Usage',
|
|
122
|
+
value: 'getUsage',
|
|
123
|
+
description: 'Read usage history and subscription summary',
|
|
124
|
+
action: 'Read usage history and subscription summary',
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
default: 'getUsage',
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
};
|
|
131
|
+
async execute() {
|
|
132
|
+
return executeInfluticsAccount.call(this, this.getInputData());
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=InfluticsAccount.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InfluticsAccount.node.js","sourceRoot":"","sources":["../../../nodes/InfluticsAccount/InfluticsAccount.node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,OAAO,EACL,kBAAkB,GAMnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAW7D,MAAM,UAAU,GAAqC;IACnD,QAAQ,EAAE,KAAK,WAAoC,EAAU;QAC3D,oEAAoE;QACpE,wEAAwE;QACxE,sEAAsE;QACtE,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,mBAAmB,CAAC,CAAC;QAClF,OAAO,QAAuB,CAAC;IACjC,CAAC;IACD,SAAS,EAAE,KAAK,WAAoC,EAAU;QAC5D,uEAAuE;QACvE,qCAAqC;QACrC,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,oBAAoB,CAAC,CAAC;QACnF,OAAO,QAAuB,CAAC;IACjC,CAAC;CACF,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAE3C,MAA4B;IAE5B,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE,CAAC,CAAW,CAAC;IAClE,MAAM,OAAO,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IACtC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,kBAAkB,CAC1B,IAAI,CAAC,OAAO,EAAE,EACd,cAAc,SAAS,gDAAgD,CACxE,CAAC;IACJ,CAAC;IACD,0EAA0E;IAC1E,uEAAuE;IACvE,kDAAkD;IAClD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,OAAO,gBAAgB;IAC3B,WAAW,GAAyB;QAClC,WAAW,EAAE,mBAAmB;QAChC,IAAI,EAAE,kBAAkB;QACxB,IAAI,EAAE,oBAAoB;QAC1B,KAAK,EAAE,CAAC,WAAW,CAAC;QACpB,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,yCAAyC;QACtD,yEAAyE;QACzE,wEAAwE;QACxE,qEAAqE;QACrE,uEAAuE;QACvE,sEAAsE;QACtE,cAAc;QACd,QAAQ,EACN,4EAA4E;QAC9E,QAAQ,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE;QACvC,MAAM,EAAE,CAAC,MAAM,CAAC;QAChB,OAAO,EAAE,CAAC,MAAM,CAAC;QACjB,WAAW,EAAE,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QACvD,UAAU,EAAE;YACV;gBACE,WAAW,EAAE,WAAW;gBACxB,IAAI,EAAE,WAAW;gBACjB,IAAI,EAAE,SAAS;gBACf,gBAAgB,EAAE,IAAI;gBACtB,0FAA0F;gBAC1F,yFAAyF;gBACzF,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,YAAY;wBAClB,KAAK,EAAE,WAAW;wBAClB,WAAW,EAAE,+BAA+B;wBAC5C,MAAM,EAAE,+BAA+B;qBACxC;oBACD;wBACE,IAAI,EAAE,WAAW;wBACjB,KAAK,EAAE,UAAU;wBACjB,WAAW,EAAE,6CAA6C;wBAC1D,MAAM,EAAE,6CAA6C;qBACtD;iBACF;gBACD,OAAO,EAAE,UAAU;aACpB;SACF;KACF,CAAC;IAEF,KAAK,CAAC,OAAO;QACX,OAAO,uBAAuB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;IACjE,CAAC;CACF"}
|
|
@@ -0,0 +1,5 @@
|
|
|
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="#6266F0"/>
|
|
3
|
+
<text x="30" y="38" font-family="Helvetica,Arial,sans-serif" font-size="22" font-weight="700"
|
|
4
|
+
fill="#FFFFFF" text-anchor="middle">I</text>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Influtics Blogger node.
|
|
3
|
+
*
|
|
4
|
+
* Implementation choices:
|
|
5
|
+
* - File lives at nodes/InfluticsBlogger/InfluticsBlogger.node.ts — required by the
|
|
6
|
+
* eslint-plugin-n8n-nodes-base `node-dirname-against-convention` rule.
|
|
7
|
+
* - `executeInfluticsBlogger` is also exported as a named function so unit tests
|
|
8
|
+
* can drive the executor without instantiating the INodeType class.
|
|
9
|
+
* - Tasks 4/5/6 ship the InfluticsVideo node; this node (Task 7) adds the
|
|
10
|
+
* blogger side: Track + Get Job + By Username.
|
|
11
|
+
* - The unimplemented-operation branch keeps the executor safe if a future
|
|
12
|
+
* version's parameters somehow leak an unknown value.
|
|
13
|
+
*
|
|
14
|
+
* Backend contract (verified against api-worker source):
|
|
15
|
+
* POST /v1/bloggers/track
|
|
16
|
+
* body (flat): { platform, username, initial_videos_count }
|
|
17
|
+
* - `initial_videos_count` is REQUIRED (integer in [1, 500])
|
|
18
|
+
* per validateTrackBloggerBody — the stale `track.md` docs page and
|
|
19
|
+
* the plan both omit it; the validator rejects bodies without it.
|
|
20
|
+
* - The platform-specific channel ID is NOT in the request. The consumer
|
|
21
|
+
* resolves `username -> channel_id` asynchronously via the platform
|
|
22
|
+
* API after dequeuing (see api-worker CLAUDE.md "Track creator flow").
|
|
23
|
+
* - 202 Accepted, returns `{ job_id, status_url, polling.retry_after_seconds }`.
|
|
24
|
+
* - 409 CREATOR_ALREADY_TRACKED, 422 SUBSCRIPTION_LIMIT, 402 PAID_PLAN_REQUIRED.
|
|
25
|
+
* GET /v1/bloggers/jobs/{job_id}
|
|
26
|
+
* - Path-only, no qs. Returns 200/404/410.
|
|
27
|
+
* - shapeJob uses `status` (queued|processing|succeeded|error), not `state`.
|
|
28
|
+
* - 410 JOB_TIMEOUT when sweeper-marked (terminal — re-POST /v1/bloggers/track).
|
|
29
|
+
* - 404 NOT_FOUND collapses "doesn't exist" + "wrong org" (Stripe-parity).
|
|
30
|
+
* GET /v1/bloggers/by-username/{username}?platform={platform}
|
|
31
|
+
* - Path is URL-decoded server-side (`decodeURIComponent`); executor
|
|
32
|
+
* URL-encodes the path segment so `@handle` (and other reserved chars)
|
|
33
|
+
* round-trip cleanly.
|
|
34
|
+
* - Query `platform` is optional; server defaults to `tiktok` if absent
|
|
35
|
+
* OR empty. We strip empty strings here so the default path stays clean.
|
|
36
|
+
* - Read-only — NEVER auto-tracks. 404 BLOGGER_NOT_TRACKED if not tracked
|
|
37
|
+
* by the calling org. The legacy /v1/bloggers/info?username=... form
|
|
38
|
+
* was removed 2026-08-20 (see api-worker CLAUDE.md "Deprecations").
|
|
39
|
+
*/
|
|
40
|
+
import { NodeOperationError, } from 'n8n-workflow';
|
|
41
|
+
import { influticsApiRequest } from '../GenericFunctions.js';
|
|
42
|
+
// Backend hard caps. Mirrors `validateTrackBloggerBody` in
|
|
43
|
+
// api-worker/src/handlers/lib/trackAccountValidator.js. The UI also clamps
|
|
44
|
+
// via `typeOptions.minValue` / `maxValue`; this is defense-in-depth for
|
|
45
|
+
// custom callers / future schema drift.
|
|
46
|
+
const INITIAL_VIDEOS_MIN = 1;
|
|
47
|
+
const INITIAL_VIDEOS_MAX = 500;
|
|
48
|
+
const OPERATIONS = {
|
|
49
|
+
track: async function (_i) {
|
|
50
|
+
const platform = this.getNodeParameter('platform', _i, '');
|
|
51
|
+
const username = this.getNodeParameter('username', _i, '');
|
|
52
|
+
const initialVideosCountRaw = this.getNodeParameter('initialVideosCount', _i, '');
|
|
53
|
+
if (!platform) {
|
|
54
|
+
// Fail fast: backend rejects missing platform (422 VALIDATION_ERROR).
|
|
55
|
+
// Surface a clear UI message instead of letting the workflow silently 422.
|
|
56
|
+
throw new NodeOperationError(this.getNode(), 'Platform is required');
|
|
57
|
+
}
|
|
58
|
+
if (!username) {
|
|
59
|
+
throw new NodeOperationError(this.getNode(), 'Username is required');
|
|
60
|
+
}
|
|
61
|
+
// Coerce + clamp initial_videos_count defensively:
|
|
62
|
+
// - blank string / non-number / NaN → fail fast
|
|
63
|
+
// - < 1 → fail fast (backend rejects, 422)
|
|
64
|
+
// - > 500 → clamp to 500 (backend rejects, 422; clamp defends against
|
|
65
|
+
// a custom caller bypassing the UI's maxValue guard)
|
|
66
|
+
// The UI's `typeOptions.minValue = 1 / maxValue = 500` is the primary
|
|
67
|
+
// guard; this is the second line of defense.
|
|
68
|
+
const initialVideosCount = Number(initialVideosCountRaw);
|
|
69
|
+
if (initialVideosCountRaw === '' ||
|
|
70
|
+
initialVideosCountRaw === null ||
|
|
71
|
+
initialVideosCountRaw === undefined ||
|
|
72
|
+
!Number.isFinite(initialVideosCount) ||
|
|
73
|
+
!Number.isInteger(initialVideosCount)) {
|
|
74
|
+
throw new NodeOperationError(this.getNode(), 'initial_videos_count is required and must be an integer between 1 and 500');
|
|
75
|
+
}
|
|
76
|
+
const initialVideosCountClamped = Math.max(INITIAL_VIDEOS_MIN, Math.min(initialVideosCount, INITIAL_VIDEOS_MAX));
|
|
77
|
+
const response = await influticsApiRequest.call(this, 'POST', '/v1/bloggers/track', {
|
|
78
|
+
platform,
|
|
79
|
+
username,
|
|
80
|
+
initial_videos_count: initialVideosCountClamped,
|
|
81
|
+
});
|
|
82
|
+
return response;
|
|
83
|
+
},
|
|
84
|
+
getJob: async function (_i) {
|
|
85
|
+
const jobId = this.getNodeParameter('jobId', _i, '');
|
|
86
|
+
if (!jobId) {
|
|
87
|
+
// Fail fast: without an id we'd send `GET /v1/bloggers/jobs/` which 404s
|
|
88
|
+
// with a confusing URL and no actionable error.
|
|
89
|
+
throw new NodeOperationError(this.getNode(), 'Job ID is required');
|
|
90
|
+
}
|
|
91
|
+
const response = await influticsApiRequest.call(this, 'GET', `/v1/bloggers/jobs/${encodeURIComponent(jobId)}`);
|
|
92
|
+
return response;
|
|
93
|
+
},
|
|
94
|
+
byUsername: async function (_i) {
|
|
95
|
+
const username = this.getNodeParameter('username', _i, '');
|
|
96
|
+
if (!username) {
|
|
97
|
+
// Without a username the URL would be /v1/bloggers/by-username/ — backend
|
|
98
|
+
// returns 400 VALIDATION_ERROR. Fail fast with a clear UI message instead.
|
|
99
|
+
throw new NodeOperationError(this.getNode(), 'Username is required');
|
|
100
|
+
}
|
|
101
|
+
// Backend reads `url.searchParams.get('platform') || 'tiktok'`. Empty string
|
|
102
|
+
// would technically still resolve to 'tiktok' server-side, but we strip it
|
|
103
|
+
// anyway so the wire request stays minimal and any future server-side
|
|
104
|
+
// change to the default branch can't surprise us.
|
|
105
|
+
const platform = this.getNodeParameter('platform', _i, '');
|
|
106
|
+
const qs = {};
|
|
107
|
+
if (platform)
|
|
108
|
+
qs.platform = platform;
|
|
109
|
+
const response = await influticsApiRequest.call(this, 'GET', `/v1/bloggers/by-username/${encodeURIComponent(username)}`, undefined, Object.keys(qs).length > 0 ? qs : undefined);
|
|
110
|
+
return response;
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
export async function executeInfluticsBlogger(_items) {
|
|
114
|
+
const operation = this.getNodeParameter('operation', 0);
|
|
115
|
+
const handler = OPERATIONS[operation];
|
|
116
|
+
if (!handler) {
|
|
117
|
+
throw new NodeOperationError(this.getNode(), `Operation "${operation}" not yet implemented in InfluticsBlogger node`);
|
|
118
|
+
}
|
|
119
|
+
// All three ops are single-batch (one creator per workflow run regardless of
|
|
120
|
+
// input item count). Mirrors the Track videos pattern from InfluticsVideo.
|
|
121
|
+
const response = await handler.call(this, 0);
|
|
122
|
+
return [[{ json: response }]];
|
|
123
|
+
}
|
|
124
|
+
export class InfluticsBlogger {
|
|
125
|
+
description = {
|
|
126
|
+
displayName: 'Influtics Blogger',
|
|
127
|
+
name: 'influticsBlogger',
|
|
128
|
+
icon: 'file:influtics.svg',
|
|
129
|
+
group: ['transform'],
|
|
130
|
+
version: 1,
|
|
131
|
+
description: 'Track and read Influtics bloggers (creators)',
|
|
132
|
+
defaults: { name: 'Influtics Blogger' },
|
|
133
|
+
inputs: ['main'],
|
|
134
|
+
outputs: ['main'],
|
|
135
|
+
credentials: [{ name: 'influticsApi', required: true }],
|
|
136
|
+
properties: [
|
|
137
|
+
{
|
|
138
|
+
displayName: 'Operation',
|
|
139
|
+
name: 'operation',
|
|
140
|
+
type: 'options',
|
|
141
|
+
noDataExpression: true,
|
|
142
|
+
// Alphabetized per eslint-plugin-n8n-nodes-base `node-param-options-type-unsorted-items`.
|
|
143
|
+
// Each option carries an `action:` (rule `node-param-operation-option-action-miscased`).
|
|
144
|
+
options: [
|
|
145
|
+
{
|
|
146
|
+
name: 'By Username',
|
|
147
|
+
value: 'byUsername',
|
|
148
|
+
description: 'Read a tracked creator by platform + username (read-only)',
|
|
149
|
+
action: 'Read a tracked creator by platform + username',
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: 'Get Job',
|
|
153
|
+
value: 'getJob',
|
|
154
|
+
description: 'Poll the status of a track-creator job by ID',
|
|
155
|
+
action: 'Poll the status of a track creator job by id',
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: 'Track',
|
|
159
|
+
value: 'track',
|
|
160
|
+
description: 'Start tracking a creator (async — returns job_id to poll)',
|
|
161
|
+
action: 'Start tracking a creator',
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
default: 'track',
|
|
165
|
+
},
|
|
166
|
+
// --- Track ---------------------------------------------------------
|
|
167
|
+
{
|
|
168
|
+
displayName: 'Platform',
|
|
169
|
+
name: 'platform',
|
|
170
|
+
type: 'options',
|
|
171
|
+
displayOptions: { show: { operation: ['track'] } },
|
|
172
|
+
options: [
|
|
173
|
+
{ name: 'TikTok', value: 'tiktok' },
|
|
174
|
+
{ name: 'Instagram', value: 'instagram' },
|
|
175
|
+
{ name: 'YouTube', value: 'youtube' },
|
|
176
|
+
{ name: 'VK', value: 'vk' },
|
|
177
|
+
],
|
|
178
|
+
default: 'tiktok',
|
|
179
|
+
required: true,
|
|
180
|
+
description: 'Platform to track the creator on',
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
displayName: 'Username',
|
|
184
|
+
name: 'username',
|
|
185
|
+
type: 'string',
|
|
186
|
+
displayOptions: { show: { operation: ['track'] } },
|
|
187
|
+
default: '',
|
|
188
|
+
required: true,
|
|
189
|
+
description: 'Creator username without the leading @ (max 64 chars)',
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
displayName: 'Initial Videos Count',
|
|
193
|
+
name: 'initialVideosCount',
|
|
194
|
+
type: 'number',
|
|
195
|
+
displayOptions: { show: { operation: ['track'] } },
|
|
196
|
+
// Backend (validateTrackBloggerBody) requires an integer in [1, 500].
|
|
197
|
+
// The UI clamps via minValue/maxValue; the executor clamps again
|
|
198
|
+
// for defense-in-depth against custom callers.
|
|
199
|
+
typeOptions: { minValue: 1, maxValue: 500 },
|
|
200
|
+
default: 10,
|
|
201
|
+
required: true,
|
|
202
|
+
description: 'Number of initial videos to backfill (1–500). Default: 10. Backend clamps to 500.',
|
|
203
|
+
},
|
|
204
|
+
// --- Get Job -------------------------------------------------------
|
|
205
|
+
{
|
|
206
|
+
displayName: 'Job ID',
|
|
207
|
+
name: 'jobId',
|
|
208
|
+
type: 'string',
|
|
209
|
+
displayOptions: { show: { operation: ['getJob'] } },
|
|
210
|
+
default: '',
|
|
211
|
+
required: true,
|
|
212
|
+
description: 'Job UUID returned by the Track operation',
|
|
213
|
+
},
|
|
214
|
+
// --- By Username ---------------------------------------------------
|
|
215
|
+
{
|
|
216
|
+
displayName: 'Username',
|
|
217
|
+
name: 'username',
|
|
218
|
+
type: 'string',
|
|
219
|
+
displayOptions: { show: { operation: ['byUsername'] } },
|
|
220
|
+
default: '',
|
|
221
|
+
required: true,
|
|
222
|
+
description: 'Creator username (with or without leading @)',
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
displayName: 'Platform',
|
|
226
|
+
name: 'platform',
|
|
227
|
+
type: 'options',
|
|
228
|
+
displayOptions: { show: { operation: ['byUsername'] } },
|
|
229
|
+
options: [
|
|
230
|
+
{ name: 'TikTok', value: 'tiktok' },
|
|
231
|
+
{ name: 'Instagram', value: 'instagram' },
|
|
232
|
+
{ name: 'YouTube', value: 'youtube' },
|
|
233
|
+
{ name: 'VK', value: 'vk' },
|
|
234
|
+
],
|
|
235
|
+
// The backend defaults to `tiktok` when `platform` is absent or empty.
|
|
236
|
+
// Defaulting the UI to the same value keeps wire requests clean and
|
|
237
|
+
// matches what users would otherwise type.
|
|
238
|
+
default: 'tiktok',
|
|
239
|
+
required: true,
|
|
240
|
+
description: 'Platform the creator is on (defaults to tiktok server-side)',
|
|
241
|
+
},
|
|
242
|
+
],
|
|
243
|
+
};
|
|
244
|
+
async execute() {
|
|
245
|
+
return executeInfluticsBlogger.call(this, this.getInputData());
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=InfluticsBlogger.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InfluticsBlogger.node.js","sourceRoot":"","sources":["../../../nodes/InfluticsBlogger/InfluticsBlogger.node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
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="#6266F0"/>
|
|
3
|
+
<text x="30" y="38" font-family="Helvetica,Arial,sans-serif" font-size="22" font-weight="700"
|
|
4
|
+
fill="#FFFFFF" text-anchor="middle">I</text>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Influtics Trend node.
|
|
3
|
+
*
|
|
4
|
+
* Implementation choices:
|
|
5
|
+
* - File lives at nodes/InfluticsTrend/InfluticsTrend.node.ts — required by the
|
|
6
|
+
* eslint-plugin-n8n-nodes-base `node-dirname-against-convention` rule.
|
|
7
|
+
* - `executeInfluticsTrend` is also exported as a named function so unit tests
|
|
8
|
+
* can drive the executor without instantiating the INodeType class.
|
|
9
|
+
* - One operation: Search. Tracks TikTok/YouTube trends by keyword. Tasks
|
|
10
|
+
* 4/5/6/7 ship the Video and Blogger siblings; this node (Task 8) adds
|
|
11
|
+
* the trends side.
|
|
12
|
+
* - The unimplemented-operation branch keeps the executor safe if a future
|
|
13
|
+
* version's parameters somehow leak an unknown value.
|
|
14
|
+
*
|
|
15
|
+
* Backend contract (verified against api-worker `trendsHandler.js`):
|
|
16
|
+
* GET /v1/trends/search
|
|
17
|
+
* query (REQUIRED):
|
|
18
|
+
* - keyword : string, non-empty after trim
|
|
19
|
+
* - platform : one of `tiktok`, `youtube`
|
|
20
|
+
* query (OPTIONAL):
|
|
21
|
+
* - cursor : pagination cursor returned by prior call
|
|
22
|
+
* - region : ISO 3166-1 alpha-2 country code (e.g. `US`, `DE`, `JP`)
|
|
23
|
+
* validated server-side with `/^[A-Za-z]{2}$/`
|
|
24
|
+
* - days : one of `0, 1, 7, 30, 90, 180` (0 = "no time window")
|
|
25
|
+
* Cost: 1 credit per call (free-tier callers receive 402 PAID_PLAN_REQUIRED).
|
|
26
|
+
* Errors: 400 VALIDATION_ERROR, 401 UNAUTHORIZED, 402 PAID_PLAN_REQUIRED,
|
|
27
|
+
* 429 RATE_LIMITED.
|
|
28
|
+
*/
|
|
29
|
+
import { NodeOperationError, } from 'n8n-workflow';
|
|
30
|
+
import { influticsApiRequest } from '../GenericFunctions.js';
|
|
31
|
+
// Backend hard allow-list (mirrors `VALID_DAYS` in
|
|
32
|
+
// api-worker/src/handlers/trendsHandler.js). The UI exposes these as a
|
|
33
|
+
// dropdown so the user can't pick anything else; this is defense-in-depth
|
|
34
|
+
// against custom callers / future schema drift.
|
|
35
|
+
const VALID_DAYS = ['0', '1', '7', '30', '90', '180'];
|
|
36
|
+
const VALID_PLATFORMS = ['tiktok', 'youtube'];
|
|
37
|
+
// ISO 3166-1 alpha-2 — two letters. Matches the server-side regex.
|
|
38
|
+
const REGION_REGEX = /^[A-Za-z]{2}$/;
|
|
39
|
+
const OPERATIONS = {
|
|
40
|
+
search: async function (_i) {
|
|
41
|
+
const keyword = this.getNodeParameter('keyword', _i, '');
|
|
42
|
+
const platform = this.getNodeParameter('platform', _i, '');
|
|
43
|
+
const additionalFields = this.getNodeParameter('additionalFields', _i, {});
|
|
44
|
+
// Defensive guards BEFORE the API call. The backend rejects each of
|
|
45
|
+
// these with 400 VALIDATION_ERROR — fail fast with a clear UI message
|
|
46
|
+
// instead of letting the workflow silently 400.
|
|
47
|
+
if (!keyword || !keyword.trim()) {
|
|
48
|
+
throw new NodeOperationError(this.getNode(), 'Keyword is required');
|
|
49
|
+
}
|
|
50
|
+
if (!platform) {
|
|
51
|
+
throw new NodeOperationError(this.getNode(), 'Platform is required');
|
|
52
|
+
}
|
|
53
|
+
if (!VALID_PLATFORMS.includes(platform)) {
|
|
54
|
+
throw new NodeOperationError(this.getNode(), `Platform must be one of: ${VALID_PLATFORMS.join(', ')}`);
|
|
55
|
+
}
|
|
56
|
+
// Region is optional; if provided, must be a two-letter ISO 3166-1 code.
|
|
57
|
+
// Empty string is the "user didn't pick one" sentinel — skip the check.
|
|
58
|
+
if (additionalFields.region && !REGION_REGEX.test(additionalFields.region)) {
|
|
59
|
+
throw new NodeOperationError(this.getNode(), 'Region must be a two-letter ISO 3166-1 code (e.g. US, DE, JP)');
|
|
60
|
+
}
|
|
61
|
+
// Days is optional; if provided, must be in the allow-list. Empty string
|
|
62
|
+
// is the "user didn't pick one" sentinel — skip the check. Note that '0'
|
|
63
|
+
// IS a valid forward (the "no time window" option).
|
|
64
|
+
if (additionalFields.days && !VALID_DAYS.includes(additionalFields.days)) {
|
|
65
|
+
throw new NodeOperationError(this.getNode(), `Days must be one of: ${VALID_DAYS.join(', ')}`);
|
|
66
|
+
}
|
|
67
|
+
// Build the query-string explicitly — strip empty strings so the wire
|
|
68
|
+
// request stays minimal. Mirrors the InfluticsVideo.getStats filter
|
|
69
|
+
// pattern: only the keys the caller actually set go on the URL.
|
|
70
|
+
const qs = {
|
|
71
|
+
keyword: keyword.trim(),
|
|
72
|
+
platform,
|
|
73
|
+
};
|
|
74
|
+
if (additionalFields.cursor)
|
|
75
|
+
qs.cursor = additionalFields.cursor;
|
|
76
|
+
if (additionalFields.region)
|
|
77
|
+
qs.region = additionalFields.region;
|
|
78
|
+
if (additionalFields.days)
|
|
79
|
+
qs.days = additionalFields.days;
|
|
80
|
+
const response = await influticsApiRequest.call(this, 'GET', '/v1/trends/search', undefined, qs);
|
|
81
|
+
return response;
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
export async function executeInfluticsTrend(_items) {
|
|
85
|
+
const operation = this.getNodeParameter('operation', 0);
|
|
86
|
+
const handler = OPERATIONS[operation];
|
|
87
|
+
if (!handler) {
|
|
88
|
+
throw new NodeOperationError(this.getNode(), `Operation "${operation}" not yet implemented in InfluticsTrend node`);
|
|
89
|
+
}
|
|
90
|
+
// Search is a single-batch op: one GET per workflow run regardless of
|
|
91
|
+
// input item count. Mirrors the Track / Get Stats patterns.
|
|
92
|
+
const response = await handler.call(this, 0);
|
|
93
|
+
return [[{ json: response }]];
|
|
94
|
+
}
|
|
95
|
+
export class InfluticsTrend {
|
|
96
|
+
description = {
|
|
97
|
+
displayName: 'Influtics Trend',
|
|
98
|
+
name: 'influticsTrend',
|
|
99
|
+
icon: 'file:influtics.svg',
|
|
100
|
+
group: ['transform'],
|
|
101
|
+
version: 1,
|
|
102
|
+
description: 'Search Influtics trends by keyword',
|
|
103
|
+
defaults: { name: 'Influtics Trend' },
|
|
104
|
+
inputs: ['main'],
|
|
105
|
+
outputs: ['main'],
|
|
106
|
+
credentials: [{ name: 'influticsApi', required: true }],
|
|
107
|
+
properties: [
|
|
108
|
+
{
|
|
109
|
+
displayName: 'Operation',
|
|
110
|
+
name: 'operation',
|
|
111
|
+
type: 'options',
|
|
112
|
+
noDataExpression: true,
|
|
113
|
+
// Alphabetized per eslint-plugin-n8n-nodes-base `node-param-options-type-unsorted-items`.
|
|
114
|
+
// Each option carries an `action:` (rule `node-param-operation-option-action-miscased`).
|
|
115
|
+
options: [
|
|
116
|
+
{
|
|
117
|
+
name: 'Search',
|
|
118
|
+
value: 'search',
|
|
119
|
+
description: 'Search TikTok or YouTube trends by keyword',
|
|
120
|
+
// eslint-disable-next-line n8n-nodes-base/node-param-operation-option-action-miscased
|
|
121
|
+
action: 'Search TikTok or YouTube trends by keyword',
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
default: 'search',
|
|
125
|
+
},
|
|
126
|
+
// --- Search ---------------------------------------------------------
|
|
127
|
+
{
|
|
128
|
+
displayName: 'Keyword',
|
|
129
|
+
name: 'keyword',
|
|
130
|
+
// eslint-disable-next-line n8n-nodes-base/cred-class-field-type-options-password-missing
|
|
131
|
+
type: 'string',
|
|
132
|
+
displayOptions: { show: { operation: ['search'] } },
|
|
133
|
+
default: '',
|
|
134
|
+
required: true,
|
|
135
|
+
description: 'Keyword to search trends for (will be trimmed before sending)',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
displayName: 'Platform',
|
|
139
|
+
name: 'platform',
|
|
140
|
+
type: 'options',
|
|
141
|
+
displayOptions: { show: { operation: ['search'] } },
|
|
142
|
+
options: [
|
|
143
|
+
{ name: 'TikTok', value: 'tiktok' },
|
|
144
|
+
{ name: 'YouTube', value: 'youtube' },
|
|
145
|
+
],
|
|
146
|
+
// The UI exposes both valid platforms; the backend rejects everything
|
|
147
|
+
// else with 400 VALIDATION_ERROR. Default tiktok to match the rest of
|
|
148
|
+
// the package's defaults (InfluticsVideo, InfluticsBlogger).
|
|
149
|
+
default: 'tiktok',
|
|
150
|
+
required: true,
|
|
151
|
+
description: 'Platform to search trends on',
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
displayName: 'Additional Options',
|
|
155
|
+
name: 'additionalFields',
|
|
156
|
+
type: 'collection',
|
|
157
|
+
default: {},
|
|
158
|
+
placeholder: 'Add Option',
|
|
159
|
+
options: [
|
|
160
|
+
{
|
|
161
|
+
displayName: 'Cursor',
|
|
162
|
+
name: 'cursor',
|
|
163
|
+
type: 'string',
|
|
164
|
+
default: '',
|
|
165
|
+
description: 'Pagination cursor returned by a prior search call',
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
displayName: 'Region',
|
|
169
|
+
name: 'region',
|
|
170
|
+
type: 'string',
|
|
171
|
+
default: '',
|
|
172
|
+
description: 'ISO 3166-1 alpha-2 country code (e.g. US, DE, JP)',
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
displayName: 'Days',
|
|
176
|
+
name: 'days',
|
|
177
|
+
type: 'options',
|
|
178
|
+
// Backend VALID_DAYS = [0, 1, 7, 30, 90, 180]. The empty option is
|
|
179
|
+
// the "user didn't pick one" sentinel — the handler skips it so
|
|
180
|
+
// the server applies its own default.
|
|
181
|
+
options: [
|
|
182
|
+
{ name: 'No Window', value: '0' },
|
|
183
|
+
{ name: '1 Day', value: '1' },
|
|
184
|
+
{ name: '7 Days', value: '7' },
|
|
185
|
+
{ name: '30 Days', value: '30' },
|
|
186
|
+
{ name: '90 Days', value: '90' },
|
|
187
|
+
{ name: '180 Days', value: '180' },
|
|
188
|
+
],
|
|
189
|
+
default: '0',
|
|
190
|
+
description: 'Time window for the trend search (0 = no window)',
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
};
|
|
196
|
+
async execute() {
|
|
197
|
+
return executeInfluticsTrend.call(this, this.getInputData());
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=InfluticsTrend.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InfluticsTrend.node.js","sourceRoot":"","sources":["../../../nodes/InfluticsTrend/InfluticsTrend.node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
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="#6266F0"/>
|
|
3
|
+
<text x="30" y="38" font-family="Helvetica,Arial,sans-serif" font-size="22" font-weight="700"
|
|
4
|
+
fill="#FFFFFF" text-anchor="middle">I</text>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Influtics Video node.
|
|
3
|
+
*
|
|
4
|
+
* Implementation choices:
|
|
5
|
+
* - File lives at nodes/InfluticsVideo/InfluticsVideo.node.ts — required by the
|
|
6
|
+
* eslint-plugin-n8n-nodes-base `node-dirname-against-convention` rule.
|
|
7
|
+
* - `executeInfluticsVideo` is also exported as a named function so unit tests
|
|
8
|
+
* can drive the executor without instantiating the INodeType class.
|
|
9
|
+
* - Tasks 4/5/6 ship Track + Get Stats + Get/Update by ID/External ID.
|
|
10
|
+
* - The unimplemented-operation branch keeps the executor safe if a future
|
|
11
|
+
* version's parameters somehow leak an unknown value.
|
|
12
|
+
*/
|
|
13
|
+
import { NodeOperationError, } from 'n8n-workflow';
|
|
14
|
+
import { influticsApiRequest } from '../GenericFunctions.js';
|
|
15
|
+
// Hard cap on paginated pages for `returnAll` to keep a runaway cursor from
|
|
16
|
+
// DOSing the workflow. Each page is `limit` items (≤ 100) so 50 pages = 5000
|
|
17
|
+
// items worst case — comfortably above the 1000-row PostgREST cap.
|
|
18
|
+
const PAGINATION_MAX_PAGES = 50;
|
|
19
|
+
const OPERATIONS = {
|
|
20
|
+
track: async function (_i) {
|
|
21
|
+
const urlsParam = this.getNodeParameter('urls', _i);
|
|
22
|
+
if (!Array.isArray(urlsParam.urls) || urlsParam.urls.length === 0) {
|
|
23
|
+
throw new NodeOperationError(this.getNode(), 'Provide at least one video URL');
|
|
24
|
+
}
|
|
25
|
+
const response = await influticsApiRequest.call(this, 'POST', '/v1/videos/track', { urls: urlsParam.urls });
|
|
26
|
+
return response;
|
|
27
|
+
},
|
|
28
|
+
getStats: async function (i) {
|
|
29
|
+
// Build the filter object only from values the caller actually set, so an
|
|
30
|
+
// unfiltered query goes out as `?` with no params (and the API applies its
|
|
31
|
+
// own server-side defaults). Empty string → omit.
|
|
32
|
+
//
|
|
33
|
+
// The real `/v1/videos/stats` endpoint (see api-worker `handleGetVideoStats`)
|
|
34
|
+
// accepts ONLY these query params: `limit`, `offset`, `platform` (single),
|
|
35
|
+
// `status` (single), `blogger_username` (single), `sort`, `order`. The UI
|
|
36
|
+
// mirrors that set exactly — campaign / search / published_from / etc. are
|
|
37
|
+
// not part of the contract and would silently be ignored, so they are not
|
|
38
|
+
// exposed.
|
|
39
|
+
const platform = this.getNodeParameter('platform', i, '');
|
|
40
|
+
const status = this.getNodeParameter('status', i, '');
|
|
41
|
+
const bloggerUsername = this.getNodeParameter('blogger_username', i, '');
|
|
42
|
+
const sort = this.getNodeParameter('sort', i, '');
|
|
43
|
+
const order = this.getNodeParameter('order', i, '');
|
|
44
|
+
const returnAll = this.getNodeParameter('returnAll', i, false);
|
|
45
|
+
// `limit` is only meaningful when `returnAll` is false; the `returnAll`
|
|
46
|
+
// paginator walks `has_more` and owns the page count, so a limit there would
|
|
47
|
+
// just be ignored (or worse, fight the page walk). The UI hides this field
|
|
48
|
+
// under the same `returnAll: [false]` guard so callers won't set it for the
|
|
49
|
+
// `returnAll` branch, but we still gate here for safety.
|
|
50
|
+
const limitRaw = this.getNodeParameter('limit', i, 50);
|
|
51
|
+
const limitRawNum = typeof limitRaw === 'number' && limitRaw > 0 ? limitRaw : 50;
|
|
52
|
+
// The API hard-caps `limit` at 100. The UI also clamps via
|
|
53
|
+
// `typeOptions.maxValue = 100`, but a custom caller or a future schema
|
|
54
|
+
// regression could still pass >100 here — coerce defensively so we never
|
|
55
|
+
// send a value the server will silently truncate.
|
|
56
|
+
const limit = Math.min(limitRawNum, 100);
|
|
57
|
+
const baseQs = {};
|
|
58
|
+
if (platform)
|
|
59
|
+
baseQs.platform = platform;
|
|
60
|
+
if (status)
|
|
61
|
+
baseQs.status = status;
|
|
62
|
+
if (bloggerUsername)
|
|
63
|
+
baseQs.blogger_username = bloggerUsername;
|
|
64
|
+
if (sort)
|
|
65
|
+
baseQs.sort = sort;
|
|
66
|
+
if (order)
|
|
67
|
+
baseQs.order = order;
|
|
68
|
+
if (!returnAll)
|
|
69
|
+
baseQs.limit = limit;
|
|
70
|
+
// The cursor-aware `influticsApiRequestAllItems` helper reads
|
|
71
|
+
// `meta.next_cursor`, which the real `/v1/videos/stats` endpoint never
|
|
72
|
+
// returns — it paginates via `offset` + a `has_more` boolean on
|
|
73
|
+
// `data.pagination`. Inline the walk here so we honour the actual contract.
|
|
74
|
+
if (returnAll) {
|
|
75
|
+
const collected = [];
|
|
76
|
+
const pageSize = typeof limit === 'number' && limit > 0 ? limit : 50;
|
|
77
|
+
let offset = 0;
|
|
78
|
+
for (let page = 0; page < PAGINATION_MAX_PAGES; page++) {
|
|
79
|
+
const pageQs = { ...baseQs, limit: pageSize, offset };
|
|
80
|
+
const response = await influticsApiRequest.call(this, 'GET', '/v1/videos/stats', undefined, pageQs);
|
|
81
|
+
// Items live at response.data.data; the success envelope puts the
|
|
82
|
+
// payload under `data` and the API wraps its array under another
|
|
83
|
+
// `data` key. Be tolerant: if the API ever drops the inner wrapper,
|
|
84
|
+
// accept `data` as the array directly.
|
|
85
|
+
const items = Array.isArray(response?.data?.data)
|
|
86
|
+
? response.data.data
|
|
87
|
+
: Array.isArray(response?.data)
|
|
88
|
+
? response.data
|
|
89
|
+
: [];
|
|
90
|
+
for (const item of items) {
|
|
91
|
+
if (item && typeof item === 'object') {
|
|
92
|
+
collected.push(item);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const pagination = response?.data?.pagination;
|
|
96
|
+
const hasMore = !!pagination?.has_more;
|
|
97
|
+
if (!hasMore)
|
|
98
|
+
break;
|
|
99
|
+
offset += pageSize;
|
|
100
|
+
}
|
|
101
|
+
// Track returns the raw envelope; mirror that here so the downstream
|
|
102
|
+
// n8n consumer sees the same shape from both batch ops.
|
|
103
|
+
return { data: collected };
|
|
104
|
+
}
|
|
105
|
+
return (await influticsApiRequest.call(this, 'GET', '/v1/videos/stats', undefined, baseQs));
|
|
106
|
+
},
|
|
107
|
+
// --- Task 6: single-resource lookups + patch -------------------------------
|
|
108
|
+
// Backend contract (see api-worker handlers in index.js):
|
|
109
|
+
// GET /v1/videos/by-id/{id}
|
|
110
|
+
// GET /v1/videos/by-external-id/{externalId}
|
|
111
|
+
// PATCH /v1/videos/by-external-id/{externalId}
|
|
112
|
+
// body: at least one of notes, budget, campaign, video_status, status, tags
|
|
113
|
+
// None of these read query params — the (organization_id, external_video_id)
|
|
114
|
+
// partial unique index scopes the row, so platform on the wire is cruft.
|
|
115
|
+
// The UI keeps the `platform` dropdown as a user-facing hint (it's marked
|
|
116
|
+
// `required: true` for `getByExternalId` / `updateByExternalId`), but the
|
|
117
|
+
// executor does not forward it.
|
|
118
|
+
getById: async function (i) {
|
|
119
|
+
const id = this.getNodeParameter('id', i, '');
|
|
120
|
+
if (!id) {
|
|
121
|
+
// Fail fast: without an id we'd send `GET /v1/videos/by-id/` which 404s
|
|
122
|
+
// with a confusing URL and no actionable error.
|
|
123
|
+
throw new NodeOperationError(this.getNode(), 'Video ID is required');
|
|
124
|
+
}
|
|
125
|
+
const response = await influticsApiRequest.call(this, 'GET', `/v1/videos/by-id/${encodeURIComponent(id)}`);
|
|
126
|
+
return response;
|
|
127
|
+
},
|
|
128
|
+
getByExternalId: async function (i) {
|
|
129
|
+
const externalId = this.getNodeParameter('externalId', i, '');
|
|
130
|
+
if (!externalId) {
|
|
131
|
+
throw new NodeOperationError(this.getNode(), 'External ID is required');
|
|
132
|
+
}
|
|
133
|
+
// Defensive: backend ignores platform, but UI marks it `required: true`.
|
|
134
|
+
// A workflow that somehow arrived here with an empty platform is broken
|
|
135
|
+
// — fail loudly instead of silently sending a request the backend ignores.
|
|
136
|
+
const platform = this.getNodeParameter('platform', i, '');
|
|
137
|
+
if (!platform) {
|
|
138
|
+
throw new NodeOperationError(this.getNode(), 'Platform is required');
|
|
139
|
+
}
|
|
140
|
+
const response = await influticsApiRequest.call(this, 'GET', `/v1/videos/by-external-id/${encodeURIComponent(externalId)}`);
|
|
141
|
+
return response;
|
|
142
|
+
},
|
|
143
|
+
updateByExternalId: async function (i) {
|
|
144
|
+
const externalId = this.getNodeParameter('externalId', i, '');
|
|
145
|
+
if (!externalId) {
|
|
146
|
+
throw new NodeOperationError(this.getNode(), 'External ID is required');
|
|
147
|
+
}
|
|
148
|
+
const platform = this.getNodeParameter('platform', i, '');
|
|
149
|
+
if (!platform) {
|
|
150
|
+
throw new NodeOperationError(this.getNode(), 'Platform is required');
|
|
151
|
+
}
|
|
152
|
+
const updateFields = this.getNodeParameter('updateFields', i, {});
|
|
153
|
+
// Backend (handlePatchVideoByExternalId) accepts any of: notes, budget,
|
|
154
|
+
// campaign, video_status, status, tags. The UI exposes a subset (notes,
|
|
155
|
+
// campaign, status, tags). Coerce defensively:
|
|
156
|
+
// - string fields: include only when non-empty (so we never send
|
|
157
|
+
// `campaign: ""` which the backend would silently drop and mask intent)
|
|
158
|
+
// - tags: include only when it's a non-empty array
|
|
159
|
+
// An empty body would 400 server-side; surface that here as a clear
|
|
160
|
+
// NodeOperationError instead.
|
|
161
|
+
const body = {};
|
|
162
|
+
if (typeof updateFields.notes === 'string' && updateFields.notes.length > 0) {
|
|
163
|
+
body.notes = updateFields.notes;
|
|
164
|
+
}
|
|
165
|
+
if (typeof updateFields.campaign === 'string' && updateFields.campaign.length > 0) {
|
|
166
|
+
body.campaign = updateFields.campaign;
|
|
167
|
+
}
|
|
168
|
+
if (typeof updateFields.status === 'string' && updateFields.status.length > 0) {
|
|
169
|
+
body.status = updateFields.status;
|
|
170
|
+
}
|
|
171
|
+
if (Array.isArray(updateFields.tags) && updateFields.tags.length > 0) {
|
|
172
|
+
body.tags = updateFields.tags;
|
|
173
|
+
}
|
|
174
|
+
if (Object.keys(body).length === 0) {
|
|
175
|
+
throw new NodeOperationError(this.getNode(), 'Provide at least one update field (notes, campaign, status, or tags)');
|
|
176
|
+
}
|
|
177
|
+
const response = await influticsApiRequest.call(this, 'PATCH', `/v1/videos/by-external-id/${encodeURIComponent(externalId)}`, body);
|
|
178
|
+
return response;
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
export async function executeInfluticsVideo(_items) {
|
|
182
|
+
const operation = this.getNodeParameter('operation', 0);
|
|
183
|
+
const handler = OPERATIONS[operation];
|
|
184
|
+
if (!handler) {
|
|
185
|
+
throw new NodeOperationError(this.getNode(), `Operation "${operation}" not yet implemented in InfluticsVideo node`);
|
|
186
|
+
}
|
|
187
|
+
// Track is a single batch op; one call per workflow run regardless of input item count.
|
|
188
|
+
const response = await handler.call(this, 0);
|
|
189
|
+
return [[{ json: response }]];
|
|
190
|
+
}
|
|
191
|
+
export class InfluticsVideo {
|
|
192
|
+
description = {
|
|
193
|
+
displayName: 'Influtics Video',
|
|
194
|
+
name: 'influticsVideo',
|
|
195
|
+
icon: 'file:influtics.svg',
|
|
196
|
+
group: ['transform'],
|
|
197
|
+
version: 1,
|
|
198
|
+
description: 'Track and read Influtics videos',
|
|
199
|
+
defaults: { name: 'Influtics Video' },
|
|
200
|
+
inputs: ['main'],
|
|
201
|
+
outputs: ['main'],
|
|
202
|
+
credentials: [{ name: 'influticsApi', required: true }],
|
|
203
|
+
properties: [
|
|
204
|
+
{
|
|
205
|
+
displayName: 'Operation',
|
|
206
|
+
name: 'operation',
|
|
207
|
+
type: 'options',
|
|
208
|
+
noDataExpression: true,
|
|
209
|
+
options: [
|
|
210
|
+
{
|
|
211
|
+
name: 'Get By External ID',
|
|
212
|
+
value: 'getByExternalId',
|
|
213
|
+
description: 'Read one tracked video by platform + external ID',
|
|
214
|
+
action: 'Read one tracked video by platform + external ID',
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
name: 'Get By ID',
|
|
218
|
+
value: 'getById',
|
|
219
|
+
description: 'Read one tracked video by internal ID',
|
|
220
|
+
action: 'Read one tracked video by internal ID',
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: 'Get Stats',
|
|
224
|
+
value: 'getStats',
|
|
225
|
+
description: 'Read video-level metrics',
|
|
226
|
+
action: 'Read video level metrics',
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
name: 'Track',
|
|
230
|
+
value: 'track',
|
|
231
|
+
description: 'Track videos by URL',
|
|
232
|
+
action: 'Track videos by URL',
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
name: 'Update By External ID',
|
|
236
|
+
value: 'updateByExternalId',
|
|
237
|
+
description: 'Patch metadata on a tracked video',
|
|
238
|
+
action: 'Patch metadata on a tracked video',
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
default: 'track',
|
|
242
|
+
},
|
|
243
|
+
// --- Track --------------------------------------------------------------
|
|
244
|
+
{
|
|
245
|
+
displayName: 'URLs',
|
|
246
|
+
name: 'urls',
|
|
247
|
+
type: 'collection',
|
|
248
|
+
displayOptions: { show: { operation: ['track'] } },
|
|
249
|
+
default: {},
|
|
250
|
+
options: [
|
|
251
|
+
{
|
|
252
|
+
displayName: 'URLs',
|
|
253
|
+
name: 'urls',
|
|
254
|
+
type: 'string',
|
|
255
|
+
typeOptions: { multipleValues: true },
|
|
256
|
+
default: [],
|
|
257
|
+
description: 'Up to 50 video URLs to track',
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
},
|
|
261
|
+
// --- Get Stats ----------------------------------------------------------
|
|
262
|
+
{
|
|
263
|
+
displayName: 'Return All',
|
|
264
|
+
name: 'returnAll',
|
|
265
|
+
type: 'boolean',
|
|
266
|
+
displayOptions: { show: { operation: ['getStats'] } },
|
|
267
|
+
default: false,
|
|
268
|
+
description: 'Whether to return all results or only up to a given limit',
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
displayName: 'Limit',
|
|
272
|
+
name: 'limit',
|
|
273
|
+
type: 'number',
|
|
274
|
+
displayOptions: { show: { operation: ['getStats'], returnAll: [false] } },
|
|
275
|
+
// The API hard-caps `limit` at 100. The handler defensively clamps via
|
|
276
|
+
// Math.min(limit, 100) so the value can never be >100 on the wire,
|
|
277
|
+
// even if a custom caller bypasses the UI's `minValue` guard.
|
|
278
|
+
typeOptions: { minValue: 1 },
|
|
279
|
+
default: 50,
|
|
280
|
+
description: 'Max number of results to return',
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
displayName: 'Platform',
|
|
284
|
+
name: 'platform',
|
|
285
|
+
type: 'options',
|
|
286
|
+
displayOptions: { show: { operation: ['getStats'] } },
|
|
287
|
+
options: [
|
|
288
|
+
{ name: 'TikTok', value: 'tiktok' },
|
|
289
|
+
{ name: 'Instagram', value: 'instagram' },
|
|
290
|
+
{ name: 'YouTube', value: 'youtube' },
|
|
291
|
+
{ name: 'VK', value: 'vk' },
|
|
292
|
+
],
|
|
293
|
+
default: 'tiktok',
|
|
294
|
+
description: 'Filter by a single platform',
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
displayName: 'Status',
|
|
298
|
+
name: 'status',
|
|
299
|
+
type: 'options',
|
|
300
|
+
displayOptions: { show: { operation: ['getStats'] } },
|
|
301
|
+
options: [
|
|
302
|
+
{ name: 'Active', value: 'active' },
|
|
303
|
+
{ name: 'Completed', value: 'completed' },
|
|
304
|
+
{ name: 'Failed', value: 'failed' },
|
|
305
|
+
],
|
|
306
|
+
default: 'active',
|
|
307
|
+
description: 'Filter by tracking status',
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
displayName: 'Blogger Username',
|
|
311
|
+
name: 'blogger_username',
|
|
312
|
+
type: 'string',
|
|
313
|
+
displayOptions: { show: { operation: ['getStats'] } },
|
|
314
|
+
default: '',
|
|
315
|
+
description: 'Filter by a single blogger username',
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
displayName: 'Sort',
|
|
319
|
+
name: 'sort',
|
|
320
|
+
type: 'string',
|
|
321
|
+
displayOptions: { show: { operation: ['getStats'] } },
|
|
322
|
+
default: 'created_at',
|
|
323
|
+
description: 'Field to sort by. Allowed: created_at, views, likes, updated_at. Default: created_at',
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
displayName: 'Order',
|
|
327
|
+
name: 'order',
|
|
328
|
+
type: 'options',
|
|
329
|
+
displayOptions: { show: { operation: ['getStats'] } },
|
|
330
|
+
options: [
|
|
331
|
+
{ name: 'Ascending', value: 'asc' },
|
|
332
|
+
{ name: 'Descending', value: 'desc' },
|
|
333
|
+
],
|
|
334
|
+
default: 'desc',
|
|
335
|
+
description: 'Sort order',
|
|
336
|
+
},
|
|
337
|
+
// --- Get By ID ----------------------------------------------------------
|
|
338
|
+
{
|
|
339
|
+
displayName: 'Video ID',
|
|
340
|
+
name: 'id',
|
|
341
|
+
type: 'string',
|
|
342
|
+
displayOptions: { show: { operation: ['getById'] } },
|
|
343
|
+
default: '',
|
|
344
|
+
required: true,
|
|
345
|
+
},
|
|
346
|
+
// --- Get By External ID / Update By External ID ------------------------
|
|
347
|
+
{
|
|
348
|
+
displayName: 'External ID',
|
|
349
|
+
name: 'externalId',
|
|
350
|
+
type: 'string',
|
|
351
|
+
displayOptions: { show: { operation: ['getByExternalId', 'updateByExternalId'] } },
|
|
352
|
+
default: '',
|
|
353
|
+
required: true,
|
|
354
|
+
description: 'The platform-specific video ID (e.g. TikTok video ID)',
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
displayName: 'Platform',
|
|
358
|
+
name: 'platform',
|
|
359
|
+
type: 'options',
|
|
360
|
+
displayOptions: { show: { operation: ['getByExternalId', 'updateByExternalId'] } },
|
|
361
|
+
options: [
|
|
362
|
+
{ name: 'TikTok', value: 'tiktok' },
|
|
363
|
+
{ name: 'Instagram', value: 'instagram' },
|
|
364
|
+
{ name: 'YouTube', value: 'youtube' },
|
|
365
|
+
{ name: 'VK', value: 'vk' },
|
|
366
|
+
],
|
|
367
|
+
default: 'tiktok',
|
|
368
|
+
required: true,
|
|
369
|
+
},
|
|
370
|
+
// --- Update By External ID body fields ---------------------------------
|
|
371
|
+
{
|
|
372
|
+
displayName: 'Update Fields',
|
|
373
|
+
name: 'updateFields',
|
|
374
|
+
type: 'collection',
|
|
375
|
+
displayOptions: { show: { operation: ['updateByExternalId'] } },
|
|
376
|
+
default: {},
|
|
377
|
+
options: [
|
|
378
|
+
{ displayName: 'Notes', name: 'notes', type: 'string', default: '' },
|
|
379
|
+
{ displayName: 'Campaign', name: 'campaign', type: 'string', default: '' },
|
|
380
|
+
{
|
|
381
|
+
displayName: 'Status',
|
|
382
|
+
name: 'status',
|
|
383
|
+
type: 'options',
|
|
384
|
+
options: [
|
|
385
|
+
// Sentinel: empty value means "no change" so untouched status
|
|
386
|
+
// doesn't clobber existing workflow state. The handler drops
|
|
387
|
+
// empty strings from the body.
|
|
388
|
+
{ name: 'No Change', value: '' },
|
|
389
|
+
{ name: 'To Do', value: 'to do' },
|
|
390
|
+
{ name: 'Running', value: 'running' },
|
|
391
|
+
{ name: 'Ended', value: 'ended' },
|
|
392
|
+
],
|
|
393
|
+
default: '',
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
displayName: 'Tags',
|
|
397
|
+
name: 'tags',
|
|
398
|
+
type: 'string',
|
|
399
|
+
typeOptions: { multipleValues: true },
|
|
400
|
+
default: [],
|
|
401
|
+
description: 'Tag names to attach (existing tags are preserved)',
|
|
402
|
+
},
|
|
403
|
+
],
|
|
404
|
+
},
|
|
405
|
+
],
|
|
406
|
+
};
|
|
407
|
+
async execute() {
|
|
408
|
+
return executeInfluticsVideo.call(this, this.getInputData());
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
//# sourceMappingURL=InfluticsVideo.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InfluticsVideo.node.js","sourceRoot":"","sources":["../../../nodes/InfluticsVideo/InfluticsVideo.node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
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="#6266F0"/>
|
|
3
|
+
<text x="30" y="38" font-family="Helvetica,Arial,sans-serif" font-size="22" font-weight="700"
|
|
4
|
+
fill="#FFFFFF" text-anchor="middle">I</text>
|
|
5
|
+
</svg>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-influtics",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Official Influtics integration for n8n — track influencer videos, bloggers, and trends",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Influtics",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"main": "dist/index.js",
|
|
21
21
|
"files": ["dist"],
|
|
22
22
|
"scripts": {
|
|
23
|
+
"prepublishOnly": "npm run build && npm run lint && npm test",
|
|
23
24
|
"build": "n8n-node build",
|
|
24
25
|
"dev": "n8n-node dev",
|
|
25
26
|
"lint": "eslint . --ext .ts",
|