@aiteza/n8n-nodes-aiteza 0.1.1 → 0.2.1
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 +3 -11
- package/dist/credentials/AitezaOAuth2Api.credentials.js +1 -1
- package/dist/nodes/Aiteza/Aiteza.node.js +1072 -342
- package/dist/nodes/Aiteza/AitezaTrigger.node.d.ts +5 -0
- package/dist/nodes/Aiteza/AitezaTrigger.node.js +73 -0
- package/dist/nodes/Aiteza/GenericFunctions.d.ts +7 -3
- package/dist/nodes/Aiteza/GenericFunctions.js +72 -66
- package/package.json +6 -2
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { INodeType, INodeTypeDescription, IWebhookFunctions, IWebhookResponseData } from 'n8n-workflow';
|
|
2
|
+
export declare class AitezaTrigger implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
webhook(this: IWebhookFunctions): Promise<IWebhookResponseData>;
|
|
5
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AitezaTrigger = void 0;
|
|
4
|
+
class AitezaTrigger {
|
|
5
|
+
description = {
|
|
6
|
+
displayName: 'AITEZA Trigger',
|
|
7
|
+
name: 'aitezaTrigger',
|
|
8
|
+
icon: 'file:aiteza.svg',
|
|
9
|
+
group: ['trigger'],
|
|
10
|
+
version: 1,
|
|
11
|
+
description: 'Starts the workflow when an AITEZA event is received via webhook. ' +
|
|
12
|
+
'Passes the caller\'s auth token to all downstream AITEZA nodes so they act on behalf of the triggering user.',
|
|
13
|
+
defaults: { name: 'AITEZA Trigger' },
|
|
14
|
+
inputs: [],
|
|
15
|
+
outputs: ['main'],
|
|
16
|
+
// No credentials required – the trigger just receives webhooks.
|
|
17
|
+
// Downstream AITEZA nodes still need credentials (for baseUrl), but will
|
|
18
|
+
// use the token from this trigger for actual authentication.
|
|
19
|
+
credentials: [],
|
|
20
|
+
webhooks: [
|
|
21
|
+
{
|
|
22
|
+
name: 'default',
|
|
23
|
+
httpMethod: 'POST',
|
|
24
|
+
responseMode: '={{$parameter["responseMode"]}}',
|
|
25
|
+
path: '={{$parameter["path"]}}',
|
|
26
|
+
isFullPath: true,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
properties: [
|
|
30
|
+
{
|
|
31
|
+
displayName: 'Path',
|
|
32
|
+
name: 'path',
|
|
33
|
+
type: 'string',
|
|
34
|
+
default: 'aiteza',
|
|
35
|
+
placeholder: 'aiteza',
|
|
36
|
+
required: true,
|
|
37
|
+
description: 'The webhook path to listen on. The full URL will be /webhook/{path}.',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
displayName: 'Response Mode',
|
|
41
|
+
name: 'responseMode',
|
|
42
|
+
type: 'options',
|
|
43
|
+
options: [
|
|
44
|
+
{
|
|
45
|
+
name: 'When Last Node Finishes',
|
|
46
|
+
value: 'lastNode',
|
|
47
|
+
description: 'Respond with data from the last node in the workflow',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'Immediately',
|
|
51
|
+
value: 'onReceived',
|
|
52
|
+
description: 'Respond immediately with 200 OK',
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
default: 'lastNode',
|
|
56
|
+
description: 'When and how to respond to the webhook',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
async webhook() {
|
|
61
|
+
const body = this.getBodyData();
|
|
62
|
+
const headers = this.getHeaderData();
|
|
63
|
+
const authHeader = headers.authorization || headers.Authorization || '';
|
|
64
|
+
const authToken = authHeader.replace(/^Bearer\s+/i, '');
|
|
65
|
+
const outputData = { ...body };
|
|
66
|
+
if (authToken)
|
|
67
|
+
outputData._authToken = authToken;
|
|
68
|
+
return {
|
|
69
|
+
workflowData: [this.helpers.returnJsonArray(outputData)],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.AitezaTrigger = AitezaTrigger;
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import type { IDataObject, IExecuteFunctions, IHttpRequestMethods, IHttpRequestOptions, ILoadOptionsFunctions, INodePropertyOptions } from 'n8n-workflow';
|
|
2
|
-
export
|
|
3
|
-
|
|
2
|
+
export interface AitezaAuthContext {
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
triggerToken?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function getUpstreamAuthToken(ef: IExecuteFunctions): string | undefined;
|
|
7
|
+
export declare function aitezaApiRequest(this: IExecuteFunctions | ILoadOptionsFunctions, method: IHttpRequestMethods, endpoint: string, body?: IDataObject | IDataObject[], qs?: IDataObject, extraOpts?: Partial<IHttpRequestOptions>, authCtx?: AitezaAuthContext): Promise<any>;
|
|
8
|
+
export declare function aitezaApiRequestFullResponse(this: IExecuteFunctions, method: IHttpRequestMethods, endpoint: string, body?: IDataObject, qs?: IDataObject, extraOpts?: Partial<IHttpRequestOptions>, authCtx?: AitezaAuthContext): Promise<{
|
|
4
9
|
body: any;
|
|
5
10
|
headers: Record<string, string>;
|
|
6
11
|
}>;
|
|
7
12
|
export declare function loadDatarooms(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
8
|
-
/** Same as loadDatarooms but with a leading "None" option for optional fields. */
|
|
9
13
|
export declare function loadDataroomsOptional(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
10
14
|
export declare function loadModels(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
11
15
|
export declare function loadChats(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getUpstreamAuthToken = getUpstreamAuthToken;
|
|
3
4
|
exports.aitezaApiRequest = aitezaApiRequest;
|
|
4
5
|
exports.aitezaApiRequestFullResponse = aitezaApiRequestFullResponse;
|
|
5
6
|
exports.loadDatarooms = loadDatarooms;
|
|
@@ -13,20 +14,62 @@ exports.loadStandaloneFiles = loadStandaloneFiles;
|
|
|
13
14
|
exports.loadStandaloneImages = loadStandaloneImages;
|
|
14
15
|
exports.validateRequiredField = validateRequiredField;
|
|
15
16
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
17
|
+
function getUpstreamAuthToken(ef) {
|
|
18
|
+
try {
|
|
19
|
+
const items = ef.getInputData();
|
|
20
|
+
for (const item of items) {
|
|
21
|
+
const token = item.json?._authToken;
|
|
22
|
+
if (token)
|
|
23
|
+
return token;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// getInputData may not be available in every context
|
|
28
|
+
}
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Internal helpers
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
async function resolveBaseUrl(ctx, authCtx) {
|
|
35
|
+
if (authCtx?.baseUrl)
|
|
36
|
+
return authCtx.baseUrl;
|
|
37
|
+
const credentials = await ctx.getCredentials('aitezaOAuth2Api');
|
|
38
|
+
return credentials.baseUrl.replace(/\/+$/, '');
|
|
39
|
+
}
|
|
40
|
+
async function executeRequest(ctx, options, authCtx) {
|
|
41
|
+
if (authCtx?.triggerToken) {
|
|
42
|
+
options.headers = {
|
|
43
|
+
...(options.headers ?? {}),
|
|
44
|
+
Authorization: `Bearer ${authCtx.triggerToken}`,
|
|
45
|
+
};
|
|
46
|
+
return ctx.helpers.httpRequest(options);
|
|
47
|
+
}
|
|
48
|
+
return ctx.helpers.httpRequestWithAuthentication.call(ctx, 'aitezaOAuth2Api', options);
|
|
49
|
+
}
|
|
50
|
+
/** Convert API response items to n8n dropdown options. */
|
|
51
|
+
function toOptions(items, nameField = 'name') {
|
|
52
|
+
return items.map((item) => ({
|
|
53
|
+
name: item[nameField] ?? item.id,
|
|
54
|
+
value: item.id,
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
/** Extract array from API response (handles both raw arrays and paginated `{ content: [...] }`). */
|
|
58
|
+
function extractItems(data) {
|
|
59
|
+
return Array.isArray(data) ? data : data?.content ?? [];
|
|
60
|
+
}
|
|
16
61
|
// ---------------------------------------------------------------------------
|
|
17
62
|
// Generic authenticated request helper
|
|
18
63
|
// ---------------------------------------------------------------------------
|
|
19
|
-
async function aitezaApiRequest(method, endpoint, body = {}, qs = {}, extraOpts = {}) {
|
|
20
|
-
const
|
|
21
|
-
const baseUrl = credentials.baseUrl.replace(/\/+$/, '');
|
|
64
|
+
async function aitezaApiRequest(method, endpoint, body = {}, qs = {}, extraOpts = {}, authCtx) {
|
|
65
|
+
const baseUrl = await resolveBaseUrl(this, authCtx);
|
|
22
66
|
const options = {
|
|
23
67
|
method,
|
|
24
68
|
url: `${baseUrl}${endpoint}`,
|
|
25
69
|
json: true,
|
|
26
70
|
...extraOpts,
|
|
27
71
|
};
|
|
28
|
-
// Only set qs when there are actual
|
|
29
|
-
// Setting qs: {} can cause n8n to strip query params already in the URL.
|
|
72
|
+
// Only set qs when there are actual values – empty qs can strip URL params.
|
|
30
73
|
if (Object.keys(qs).length > 0) {
|
|
31
74
|
options.qs = qs;
|
|
32
75
|
}
|
|
@@ -34,33 +77,28 @@ async function aitezaApiRequest(method, endpoint, body = {}, qs = {}, extraOpts
|
|
|
34
77
|
options.body = body;
|
|
35
78
|
}
|
|
36
79
|
else if (method === 'DELETE' && Object.keys(body).length > 0) {
|
|
37
|
-
// Some AITEZA DELETE endpoints accept a body
|
|
38
80
|
options.body = body;
|
|
39
81
|
}
|
|
40
82
|
try {
|
|
41
|
-
return await
|
|
83
|
+
return await executeRequest(this, options, authCtx);
|
|
42
84
|
}
|
|
43
85
|
catch (error) {
|
|
44
86
|
const statusCode = error?.statusCode ?? error?.httpCode ?? error?.response?.status;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
message = 'Resource not found';
|
|
54
|
-
}
|
|
55
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), error, { message });
|
|
87
|
+
const messages = {
|
|
88
|
+
401: 'OAuth2 token expired or invalid – check your AITEZA credentials',
|
|
89
|
+
403: 'Insufficient permissions for this resource',
|
|
90
|
+
404: 'Resource not found',
|
|
91
|
+
};
|
|
92
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), error, {
|
|
93
|
+
message: messages[statusCode] ?? error.message ?? 'Unknown error',
|
|
94
|
+
});
|
|
56
95
|
}
|
|
57
96
|
}
|
|
58
97
|
// ---------------------------------------------------------------------------
|
|
59
98
|
// Full-response variant (gives access to response headers, e.g. X-Chat-Id)
|
|
60
99
|
// ---------------------------------------------------------------------------
|
|
61
|
-
async function aitezaApiRequestFullResponse(method, endpoint, body = {}, qs = {}, extraOpts = {}) {
|
|
62
|
-
const
|
|
63
|
-
const baseUrl = credentials.baseUrl.replace(/\/+$/, '');
|
|
100
|
+
async function aitezaApiRequestFullResponse(method, endpoint, body = {}, qs = {}, extraOpts = {}, authCtx) {
|
|
101
|
+
const baseUrl = await resolveBaseUrl(this, authCtx);
|
|
64
102
|
const options = {
|
|
65
103
|
method,
|
|
66
104
|
url: `${baseUrl}${endpoint}`,
|
|
@@ -73,7 +111,7 @@ async function aitezaApiRequestFullResponse(method, endpoint, body = {}, qs = {}
|
|
|
73
111
|
options.body = body;
|
|
74
112
|
}
|
|
75
113
|
try {
|
|
76
|
-
return (await
|
|
114
|
+
return (await executeRequest(this, options, authCtx));
|
|
77
115
|
}
|
|
78
116
|
catch (error) {
|
|
79
117
|
throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
|
|
@@ -84,94 +122,62 @@ async function aitezaApiRequestFullResponse(method, endpoint, body = {}, qs = {}
|
|
|
84
122
|
// ---------------------------------------------------------------------------
|
|
85
123
|
async function loadDatarooms() {
|
|
86
124
|
try {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const items = Array.isArray(data) ? data : data?.content ?? [];
|
|
91
|
-
if (items.length === 0) {
|
|
125
|
+
const data = await aitezaApiRequest.call(this, 'GET', '/api/dataroom/search?q=&sortBy=recentlyUsed');
|
|
126
|
+
const items = extractItems(data);
|
|
127
|
+
if (items.length === 0)
|
|
92
128
|
return [{ name: '(No datarooms found)', value: '' }];
|
|
93
|
-
|
|
94
|
-
return items.map((d) => ({
|
|
95
|
-
name: d.name ?? d.id,
|
|
96
|
-
value: d.id,
|
|
97
|
-
}));
|
|
129
|
+
return toOptions(items);
|
|
98
130
|
}
|
|
99
131
|
catch (error) {
|
|
100
132
|
return [{ name: `Error loading datarooms: ${error.message ?? 'unknown'}`, value: '' }];
|
|
101
133
|
}
|
|
102
134
|
}
|
|
103
|
-
/** Same as loadDatarooms but with a leading "None" option for optional fields. */
|
|
104
135
|
async function loadDataroomsOptional() {
|
|
105
136
|
const options = await loadDatarooms.call(this);
|
|
106
137
|
return [{ name: '— None —', value: '' }, ...options];
|
|
107
138
|
}
|
|
108
139
|
async function loadModels() {
|
|
109
140
|
const data = await aitezaApiRequest.call(this, 'GET', '/api/models');
|
|
110
|
-
|
|
111
|
-
return items.map((m) => ({
|
|
112
|
-
name: m.name ?? m.id,
|
|
113
|
-
value: m.id,
|
|
114
|
-
}));
|
|
141
|
+
return toOptions(extractItems(data));
|
|
115
142
|
}
|
|
116
143
|
async function loadChats() {
|
|
117
144
|
const data = await aitezaApiRequest.call(this, 'GET', '/api/chat', {}, { size: 100 });
|
|
118
|
-
|
|
119
|
-
return items.map((c) => ({
|
|
120
|
-
name: c.name ?? c.id,
|
|
121
|
-
value: c.id,
|
|
122
|
-
}));
|
|
145
|
+
return toOptions(extractItems(data));
|
|
123
146
|
}
|
|
124
147
|
async function loadFilesInDataroom() {
|
|
125
148
|
const dataroomId = this.getCurrentNodeParameter('dataroomId');
|
|
126
149
|
if (!dataroomId)
|
|
127
150
|
return [];
|
|
128
151
|
const data = await aitezaApiRequest.call(this, 'GET', `/api/dataroom/${dataroomId}/files`, {}, { size: 100 });
|
|
129
|
-
|
|
130
|
-
return items.map((f) => ({
|
|
131
|
-
name: f.name ?? f.id,
|
|
132
|
-
value: f.id,
|
|
133
|
-
}));
|
|
152
|
+
return toOptions(extractItems(data));
|
|
134
153
|
}
|
|
135
154
|
async function loadImagesInDataroom() {
|
|
136
155
|
const dataroomId = this.getCurrentNodeParameter('dataroomId');
|
|
137
156
|
if (!dataroomId)
|
|
138
157
|
return [];
|
|
139
158
|
const data = await aitezaApiRequest.call(this, 'GET', `/api/dataroom/${dataroomId}/images`, {}, { size: 100 });
|
|
140
|
-
|
|
141
|
-
return items.map((img) => ({
|
|
142
|
-
name: img.name ?? img.id,
|
|
143
|
-
value: img.id,
|
|
144
|
-
}));
|
|
159
|
+
return toOptions(extractItems(data));
|
|
145
160
|
}
|
|
146
161
|
async function loadWebSourcesInDataroom() {
|
|
147
162
|
const dataroomId = this.getCurrentNodeParameter('dataroomId');
|
|
148
163
|
if (!dataroomId)
|
|
149
164
|
return [];
|
|
150
165
|
const data = await aitezaApiRequest.call(this, 'GET', `/api/dataroom/${dataroomId}/websites`, {}, { size: 100 });
|
|
151
|
-
|
|
152
|
-
return items.map((ws) => ({
|
|
166
|
+
return extractItems(data).map((ws) => ({
|
|
153
167
|
name: (ws.name || ws.url) ?? ws.id,
|
|
154
168
|
value: ws.id,
|
|
155
169
|
}));
|
|
156
170
|
}
|
|
157
171
|
async function loadStandaloneFiles() {
|
|
158
172
|
const data = await aitezaApiRequest.call(this, 'GET', '/api/files', {}, { size: 100 });
|
|
159
|
-
|
|
160
|
-
return items.map((f) => ({
|
|
161
|
-
name: f.name ?? f.id,
|
|
162
|
-
value: f.id,
|
|
163
|
-
}));
|
|
173
|
+
return toOptions(extractItems(data));
|
|
164
174
|
}
|
|
165
175
|
async function loadStandaloneImages() {
|
|
166
176
|
const data = await aitezaApiRequest.call(this, 'GET', '/api/images', {}, { size: 100 });
|
|
167
|
-
|
|
168
|
-
return items.map((img) => ({
|
|
169
|
-
name: img.name ?? img.id,
|
|
170
|
-
value: img.id,
|
|
171
|
-
}));
|
|
177
|
+
return toOptions(extractItems(data));
|
|
172
178
|
}
|
|
173
179
|
// ---------------------------------------------------------------------------
|
|
174
|
-
// Validation
|
|
180
|
+
// Validation helper
|
|
175
181
|
// ---------------------------------------------------------------------------
|
|
176
182
|
function validateRequiredField(ef, value, fieldName) {
|
|
177
183
|
if (value === undefined || value === null || value === '') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiteza/n8n-nodes-aiteza",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "n8n Community Node for the AITEZA REST API (Datarooms, Files, Chat, Search, Workflows)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n-community-node-package",
|
|
@@ -33,9 +33,13 @@
|
|
|
33
33
|
"dist/credentials/AitezaOAuth2Api.credentials.js"
|
|
34
34
|
],
|
|
35
35
|
"nodes": [
|
|
36
|
-
"dist/nodes/Aiteza/Aiteza.node.js"
|
|
36
|
+
"dist/nodes/Aiteza/Aiteza.node.js",
|
|
37
|
+
"dist/nodes/Aiteza/AitezaTrigger.node.js"
|
|
37
38
|
]
|
|
38
39
|
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"form-data": "^4.0.0"
|
|
42
|
+
},
|
|
39
43
|
"devDependencies": {
|
|
40
44
|
"@typescript-eslint/parser": "~7.18.0",
|
|
41
45
|
"eslint": "~8.57.0",
|