@aiteza/n8n-nodes-aiteza 0.3.1 → 1.0.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/dist/credentials/AitezaOAuth2Api.credentials.js +1 -1
- package/dist/nodes/Aiteza/Aiteza.node.js +1201 -370
- package/dist/nodes/Aiteza/AitezaProgress.node.js +43 -42
- package/dist/nodes/Aiteza/AitezaTrigger.node.js +6 -22
- package/dist/nodes/Aiteza/AitezaWorkflowResult.node.js +42 -42
- package/dist/nodes/Aiteza/GenericFunctions.d.ts +0 -9
- package/dist/nodes/Aiteza/GenericFunctions.js +42 -123
- package/package.json +1 -1
|
@@ -4,7 +4,6 @@ exports.getUpstreamAuthToken = getUpstreamAuthToken;
|
|
|
4
4
|
exports.getUpstreamBaseUrl = getUpstreamBaseUrl;
|
|
5
5
|
exports.isJwtExpired = isJwtExpired;
|
|
6
6
|
exports.aitezaApiRequest = aitezaApiRequest;
|
|
7
|
-
exports.aitezaApiRequestFullResponse = aitezaApiRequestFullResponse;
|
|
8
7
|
exports.loadDatarooms = loadDatarooms;
|
|
9
8
|
exports.loadDataroomsOptional = loadDataroomsOptional;
|
|
10
9
|
exports.loadModels = loadModels;
|
|
@@ -16,116 +15,74 @@ exports.loadStandaloneFiles = loadStandaloneFiles;
|
|
|
16
15
|
exports.loadStandaloneImages = loadStandaloneImages;
|
|
17
16
|
exports.validateRequiredField = validateRequiredField;
|
|
18
17
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
19
|
-
|
|
20
|
-
// includes the package namespace (e.g. "@aiteza/n8n-nodes-aiteza.aitezaTrigger"),
|
|
21
|
-
// so we match on the suffix.
|
|
22
|
-
function isAitezaTriggerType(type) {
|
|
23
|
-
return typeof type === 'string' && /(^|\.)aitezaTrigger$/i.test(type);
|
|
24
|
-
}
|
|
25
|
-
function findAitezaTriggerParentName(ef) {
|
|
18
|
+
function readFromTriggerItems(ef, picker) {
|
|
26
19
|
try {
|
|
27
20
|
const currentNodeName = ef.getNode().name;
|
|
28
21
|
const parents = ef.getParentNodes(currentNodeName);
|
|
29
|
-
const trigger = parents.find((p) =>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
catch {
|
|
33
|
-
return undefined;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
function readFromTriggerItems(ef, picker) {
|
|
37
|
-
const triggerName = findAitezaTriggerParentName(ef);
|
|
38
|
-
if (!triggerName)
|
|
39
|
-
return undefined;
|
|
40
|
-
try {
|
|
22
|
+
const trigger = parents.find((p) => /(^|\.)aitezaTrigger$/i.test(p.type) && !p.disabled);
|
|
23
|
+
if (!trigger)
|
|
24
|
+
return undefined;
|
|
41
25
|
const proxy = ef.getWorkflowDataProxy(0);
|
|
42
|
-
const triggerItems = proxy.$items(
|
|
26
|
+
const triggerItems = proxy.$items(trigger.name) ?? [];
|
|
43
27
|
for (const item of triggerItems) {
|
|
44
28
|
const value = picker(item?.json);
|
|
45
29
|
if (value !== undefined && value !== null && value !== '')
|
|
46
30
|
return value;
|
|
47
31
|
}
|
|
48
32
|
}
|
|
49
|
-
catch {
|
|
50
|
-
// proxy/items unavailable – ignore
|
|
51
|
-
}
|
|
33
|
+
catch { }
|
|
52
34
|
return undefined;
|
|
53
35
|
}
|
|
54
36
|
function getUpstreamAuthToken(ef) {
|
|
55
|
-
// 1. Prefer the token on the immediate input items (cheap and matches the
|
|
56
|
-
// documented contract of forwarding `_authToken`).
|
|
57
37
|
try {
|
|
58
|
-
const
|
|
59
|
-
for (const item of items) {
|
|
38
|
+
for (const item of ef.getInputData()) {
|
|
60
39
|
const token = item.json?._authToken;
|
|
61
40
|
if (token)
|
|
62
41
|
return token;
|
|
63
42
|
}
|
|
64
43
|
}
|
|
65
|
-
catch {
|
|
66
|
-
// getInputData may not be available in every context
|
|
67
|
-
}
|
|
68
|
-
// 2. Fall back to the AITEZA Trigger node's original output. This makes the
|
|
69
|
-
// node resilient to intermediate transformations (Set/Edit Fields, Split
|
|
70
|
-
// Out, IF, etc.) that strip the `_authToken` field from items.
|
|
44
|
+
catch { }
|
|
71
45
|
return readFromTriggerItems(ef, (json) => json?._authToken);
|
|
72
46
|
}
|
|
73
47
|
function getUpstreamBaseUrl(ef) {
|
|
74
48
|
try {
|
|
75
|
-
const
|
|
76
|
-
for (const item of items) {
|
|
49
|
+
for (const item of ef.getInputData()) {
|
|
77
50
|
const url = item.json?._baseUrl;
|
|
78
51
|
if (url)
|
|
79
52
|
return url.replace(/\/+$/, '');
|
|
80
53
|
}
|
|
81
54
|
}
|
|
82
|
-
catch {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
55
|
+
catch { }
|
|
56
|
+
const fromTrigger = readFromTriggerItems(ef, (json) => {
|
|
57
|
+
const explicit = json?._baseUrl;
|
|
58
|
+
if (explicit)
|
|
59
|
+
return explicit;
|
|
60
|
+
const body = json?.body ?? {};
|
|
61
|
+
return body?.callbackUrl;
|
|
62
|
+
});
|
|
86
63
|
return fromTrigger ? fromTrigger.replace(/\/+$/, '') : undefined;
|
|
87
64
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
function decodeJwtPayload(token) {
|
|
65
|
+
function isJwtExpired(token, skewSeconds = 30) {
|
|
66
|
+
if (!token)
|
|
67
|
+
return true;
|
|
92
68
|
try {
|
|
93
69
|
const payload = token.split('.')[1];
|
|
94
70
|
if (!payload)
|
|
95
|
-
return
|
|
71
|
+
return false;
|
|
96
72
|
const normalized = payload.replace(/-/g, '+').replace(/_/g, '/');
|
|
97
73
|
const padded = normalized + '='.repeat((4 - (normalized.length % 4)) % 4);
|
|
98
|
-
|
|
99
|
-
// binary string. JWT payloads are UTF-8 JSON; for the `exp` claim a
|
|
100
|
-
// binary-string decode is sufficient.
|
|
101
|
-
const decoded = globalThis.atob
|
|
102
|
-
? globalThis.atob(padded)
|
|
103
|
-
: '';
|
|
74
|
+
const decoded = globalThis.atob ? globalThis.atob(padded) : '';
|
|
104
75
|
if (!decoded)
|
|
105
|
-
return
|
|
106
|
-
|
|
76
|
+
return false;
|
|
77
|
+
const { exp } = JSON.parse(decoded);
|
|
78
|
+
if (!exp)
|
|
79
|
+
return false;
|
|
80
|
+
return exp <= Math.floor(Date.now() / 1000) + skewSeconds;
|
|
107
81
|
}
|
|
108
82
|
catch {
|
|
109
|
-
return undefined;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Returns true when the token is a JWT with an `exp` claim that is in the past
|
|
114
|
-
* (with a small skew). Returns false when the token is not a parsable JWT or
|
|
115
|
-
* has no `exp` (we assume valid in that case – the server is the source of truth).
|
|
116
|
-
*/
|
|
117
|
-
function isJwtExpired(token, skewSeconds = 30) {
|
|
118
|
-
if (!token)
|
|
119
|
-
return true;
|
|
120
|
-
const payload = decodeJwtPayload(token);
|
|
121
|
-
if (!payload?.exp)
|
|
122
83
|
return false;
|
|
123
|
-
|
|
124
|
-
return payload.exp <= nowSeconds + skewSeconds;
|
|
84
|
+
}
|
|
125
85
|
}
|
|
126
|
-
// ---------------------------------------------------------------------------
|
|
127
|
-
// Internal helpers
|
|
128
|
-
// ---------------------------------------------------------------------------
|
|
129
86
|
async function resolveBaseUrl(ctx, authCtx) {
|
|
130
87
|
if (authCtx?.baseUrl)
|
|
131
88
|
return authCtx.baseUrl;
|
|
@@ -134,28 +91,20 @@ async function resolveBaseUrl(ctx, authCtx) {
|
|
|
134
91
|
}
|
|
135
92
|
async function executeRequest(ctx, options, authCtx) {
|
|
136
93
|
if (authCtx?.triggerToken) {
|
|
137
|
-
options.headers = {
|
|
138
|
-
...(options.headers ?? {}),
|
|
139
|
-
Authorization: `Bearer ${authCtx.triggerToken}`,
|
|
140
|
-
};
|
|
94
|
+
options.headers = { ...(options.headers ?? {}), Authorization: `Bearer ${authCtx.triggerToken}` };
|
|
141
95
|
return ctx.helpers.httpRequest(options);
|
|
142
96
|
}
|
|
143
97
|
return ctx.helpers.httpRequestWithAuthentication.call(ctx, 'aitezaOAuth2Api', options);
|
|
144
98
|
}
|
|
145
|
-
/** Convert API response items to n8n dropdown options. */
|
|
146
99
|
function toOptions(items, nameField = 'name') {
|
|
147
100
|
return items.map((item) => ({
|
|
148
101
|
name: item[nameField] ?? item.id,
|
|
149
102
|
value: item.id,
|
|
150
103
|
}));
|
|
151
104
|
}
|
|
152
|
-
/** Extract array from API response (handles both raw arrays and paginated `{ content: [...] }`). */
|
|
153
105
|
function extractItems(data) {
|
|
154
|
-
return Array.isArray(data) ? data : data?.content ?? [];
|
|
106
|
+
return Array.isArray(data) ? data : (data?.content ?? []);
|
|
155
107
|
}
|
|
156
|
-
// ---------------------------------------------------------------------------
|
|
157
|
-
// Generic authenticated request helper
|
|
158
|
-
// ---------------------------------------------------------------------------
|
|
159
108
|
async function aitezaApiRequest(method, endpoint, body = {}, qs = {}, extraOpts = {}, authCtx) {
|
|
160
109
|
const baseUrl = await resolveBaseUrl(this, authCtx);
|
|
161
110
|
const options = {
|
|
@@ -164,15 +113,12 @@ async function aitezaApiRequest(method, endpoint, body = {}, qs = {}, extraOpts
|
|
|
164
113
|
json: true,
|
|
165
114
|
...extraOpts,
|
|
166
115
|
};
|
|
167
|
-
|
|
168
|
-
if (Object.keys(qs).length > 0) {
|
|
116
|
+
if (Object.keys(qs).length > 0)
|
|
169
117
|
options.qs = qs;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
else if (method === 'DELETE' && Object.keys(body).length > 0) {
|
|
175
|
-
options.body = body;
|
|
118
|
+
if (method !== 'GET') {
|
|
119
|
+
const bodyObj = body;
|
|
120
|
+
if (method !== 'DELETE' || Object.keys(bodyObj).length > 0)
|
|
121
|
+
options.body = bodyObj;
|
|
176
122
|
}
|
|
177
123
|
try {
|
|
178
124
|
return await executeRequest(this, options, authCtx);
|
|
@@ -180,41 +126,15 @@ async function aitezaApiRequest(method, endpoint, body = {}, qs = {}, extraOpts
|
|
|
180
126
|
catch (error) {
|
|
181
127
|
const statusCode = error?.statusCode ?? error?.httpCode ?? error?.response?.status;
|
|
182
128
|
const messages = {
|
|
183
|
-
401: 'OAuth2 token expired or invalid – check your
|
|
184
|
-
403: '
|
|
185
|
-
404: '
|
|
129
|
+
401: 'The OAuth2 token is expired or invalid – check your Aiteza credentials',
|
|
130
|
+
403: 'Your account does not have permission to access this resource',
|
|
131
|
+
404: 'The requested resource could not be found',
|
|
186
132
|
};
|
|
187
133
|
throw new n8n_workflow_1.NodeApiError(this.getNode(), error, {
|
|
188
|
-
message: messages[statusCode] ?? error.message ?? '
|
|
134
|
+
message: messages[statusCode] ?? error.message ?? 'The request could not be completed',
|
|
189
135
|
});
|
|
190
136
|
}
|
|
191
137
|
}
|
|
192
|
-
// ---------------------------------------------------------------------------
|
|
193
|
-
// Full-response variant (gives access to response headers, e.g. X-Chat-Id)
|
|
194
|
-
// ---------------------------------------------------------------------------
|
|
195
|
-
async function aitezaApiRequestFullResponse(method, endpoint, body = {}, qs = {}, extraOpts = {}, authCtx) {
|
|
196
|
-
const baseUrl = await resolveBaseUrl(this, authCtx);
|
|
197
|
-
const options = {
|
|
198
|
-
method,
|
|
199
|
-
url: `${baseUrl}${endpoint}`,
|
|
200
|
-
qs,
|
|
201
|
-
json: true,
|
|
202
|
-
returnFullResponse: true,
|
|
203
|
-
...extraOpts,
|
|
204
|
-
};
|
|
205
|
-
if (method !== 'GET') {
|
|
206
|
-
options.body = body;
|
|
207
|
-
}
|
|
208
|
-
try {
|
|
209
|
-
return (await executeRequest(this, options, authCtx));
|
|
210
|
-
}
|
|
211
|
-
catch (error) {
|
|
212
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
// ---------------------------------------------------------------------------
|
|
216
|
-
// loadOptions helpers
|
|
217
|
-
// ---------------------------------------------------------------------------
|
|
218
138
|
async function loadDatarooms() {
|
|
219
139
|
try {
|
|
220
140
|
const data = await aitezaApiRequest.call(this, 'GET', '/api/dataroom/search?q=&sortBy=recentlyUsed');
|
|
@@ -271,11 +191,10 @@ async function loadStandaloneImages() {
|
|
|
271
191
|
const data = await aitezaApiRequest.call(this, 'GET', '/api/images', {}, { size: 100 });
|
|
272
192
|
return toOptions(extractItems(data));
|
|
273
193
|
}
|
|
274
|
-
// ---------------------------------------------------------------------------
|
|
275
|
-
// Validation helper
|
|
276
|
-
// ---------------------------------------------------------------------------
|
|
277
194
|
function validateRequiredField(ef, value, fieldName) {
|
|
278
195
|
if (value === undefined || value === null || value === '') {
|
|
279
|
-
throw new n8n_workflow_1.NodeOperationError(ef.getNode(), `
|
|
196
|
+
throw new n8n_workflow_1.NodeOperationError(ef.getNode(), `The '${fieldName}' field is required but was not provided.`, {
|
|
197
|
+
description: `Please fill in the '${fieldName}' field before running this node.`,
|
|
198
|
+
});
|
|
280
199
|
}
|
|
281
200
|
}
|