n8n-nodes-dominusnode 1.0.1 → 1.2.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/DominusNodeApi.credentials.js +8 -0
- package/dist/credentials/DominusNodeApi.credentials.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/nodes/DominusNodeAccount/DominusNodeAccount.node.d.ts +23 -0
- package/dist/nodes/DominusNodeAccount/DominusNodeAccount.node.js +223 -0
- package/dist/nodes/DominusNodeAccount/DominusNodeAccount.node.js.map +1 -0
- package/dist/nodes/DominusNodeKeys/DominusNodeKeys.node.d.ts +20 -0
- package/dist/nodes/DominusNodeKeys/DominusNodeKeys.node.js +145 -0
- package/dist/nodes/DominusNodeKeys/DominusNodeKeys.node.js.map +1 -0
- package/dist/nodes/DominusNodePlans/DominusNodePlans.node.d.ts +19 -0
- package/dist/nodes/DominusNodePlans/DominusNodePlans.node.js +123 -0
- package/dist/nodes/DominusNodePlans/DominusNodePlans.node.js.map +1 -0
- package/dist/nodes/DominusNodeProxy/DominusNodeProxy.node.d.ts +2 -1
- package/dist/nodes/DominusNodeProxy/DominusNodeProxy.node.js +14 -2
- package/dist/nodes/DominusNodeProxy/DominusNodeProxy.node.js.map +1 -1
- package/dist/nodes/DominusNodeTeams/DominusNodeTeams.node.d.ts +27 -0
- package/dist/nodes/DominusNodeTeams/DominusNodeTeams.node.js +258 -0
- package/dist/nodes/DominusNodeTeams/DominusNodeTeams.node.js.map +1 -0
- package/dist/nodes/DominusNodeUsage/DominusNodeUsage.node.d.ts +3 -1
- package/dist/nodes/DominusNodeUsage/DominusNodeUsage.node.js +49 -2
- package/dist/nodes/DominusNodeUsage/DominusNodeUsage.node.js.map +1 -1
- package/dist/nodes/DominusNodeWallet/DominusNodeWallet.node.d.ts +5 -1
- package/dist/nodes/DominusNodeWallet/DominusNodeWallet.node.js +46 -2
- package/dist/nodes/DominusNodeWallet/DominusNodeWallet.node.js.map +1 -1
- package/dist/shared/auth.d.ts +2 -1
- package/dist/shared/auth.js +16 -5
- package/dist/shared/auth.js.map +1 -1
- package/package.json +7 -3
- package/src/credentials/DominusNodeApi.credentials.ts +9 -0
- package/src/index.ts +4 -0
- package/src/nodes/DominusNodeAccount/DominusNodeAccount.node.ts +283 -0
- package/src/nodes/DominusNodeKeys/DominusNodeKeys.node.ts +192 -0
- package/src/nodes/DominusNodePlans/DominusNodePlans.node.ts +154 -0
- package/src/nodes/DominusNodeProxy/DominusNodeProxy.node.ts +13 -2
- package/src/nodes/DominusNodeTeams/DominusNodeTeams.node.ts +351 -0
- package/src/nodes/DominusNodeUsage/DominusNodeUsage.node.ts +55 -2
- package/src/nodes/DominusNodeWallet/DominusNodeWallet.node.ts +54 -2
- package/src/shared/auth.ts +19 -4
- package/tests/DominusNodeProxy.test.ts +2 -2
- package/tests/DominusNodeUsage.test.ts +2 -2
- package/tests/DominusNodeWallet.test.ts +2 -2
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dominus Node Teams n8n community node.
|
|
3
|
+
*
|
|
4
|
+
* Operations (9 tools):
|
|
5
|
+
* - Delete Team: Delete a team
|
|
6
|
+
* - Revoke Team Key: Revoke a team API key
|
|
7
|
+
* - List Team Keys: List all API keys for a team
|
|
8
|
+
* - List Team Members: List all members of a team
|
|
9
|
+
* - Add Team Member: Add a user to a team
|
|
10
|
+
* - Remove Team Member: Remove a user from a team
|
|
11
|
+
* - Invite Team Member: Send an email invitation to join a team
|
|
12
|
+
* - List Team Invites: List pending invitations for a team
|
|
13
|
+
* - Cancel Team Invite: Cancel a pending team invitation
|
|
14
|
+
*
|
|
15
|
+
* Security:
|
|
16
|
+
* - UUID validation for team/user/key/invite IDs
|
|
17
|
+
* - Email validation for invitations
|
|
18
|
+
* - Control character rejection
|
|
19
|
+
* - Credential sanitization in error messages
|
|
20
|
+
*
|
|
21
|
+
* @module
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import {
|
|
25
|
+
IDataObject,
|
|
26
|
+
IExecuteFunctions,
|
|
27
|
+
INodeExecutionData,
|
|
28
|
+
INodeType,
|
|
29
|
+
INodeTypeDescription,
|
|
30
|
+
NodeOperationError,
|
|
31
|
+
} from "n8n-workflow";
|
|
32
|
+
|
|
33
|
+
import { DominusNodeAuth, sanitizeError } from "../../shared/auth";
|
|
34
|
+
|
|
35
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
36
|
+
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
37
|
+
|
|
38
|
+
export class DominusNodeTeams implements INodeType {
|
|
39
|
+
description: INodeTypeDescription = {
|
|
40
|
+
displayName: "Dominus Node Teams",
|
|
41
|
+
name: "dominusNodeTeams",
|
|
42
|
+
icon: "file:dominusnode.svg",
|
|
43
|
+
group: ["transform"],
|
|
44
|
+
version: 1,
|
|
45
|
+
subtitle: '={{$parameter["operation"]}}',
|
|
46
|
+
description: "Manage Dominus Node team members, keys, and invitations",
|
|
47
|
+
defaults: { name: "Dominus Node Teams" },
|
|
48
|
+
inputs: ["main"],
|
|
49
|
+
outputs: ["main"],
|
|
50
|
+
credentials: [{ name: "dominusNodeApi", required: true }],
|
|
51
|
+
properties: [
|
|
52
|
+
{
|
|
53
|
+
displayName: "Operation",
|
|
54
|
+
name: "operation",
|
|
55
|
+
type: "options",
|
|
56
|
+
noDataExpression: true,
|
|
57
|
+
options: [
|
|
58
|
+
{ name: "Delete Team", value: "teamDelete", description: "Delete a team", action: "Delete team" },
|
|
59
|
+
{ name: "Revoke Team Key", value: "teamRevokeKey", description: "Revoke a team API key", action: "Revoke team key" },
|
|
60
|
+
{ name: "List Team Keys", value: "teamListKeys", description: "List all API keys for a team", action: "List team keys" },
|
|
61
|
+
{ name: "List Team Members", value: "teamListMembers", description: "List all members of a team", action: "List team members" },
|
|
62
|
+
{ name: "Add Team Member", value: "teamAddMember", description: "Add a user to a team", action: "Add team member" },
|
|
63
|
+
{ name: "Remove Team Member", value: "teamRemoveMember", description: "Remove a user from a team", action: "Remove team member" },
|
|
64
|
+
{ name: "Invite Team Member", value: "teamInviteMember", description: "Send an email invitation to join a team", action: "Invite team member" },
|
|
65
|
+
{ name: "List Team Invites", value: "teamListInvites", description: "List pending invitations for a team", action: "List team invites" },
|
|
66
|
+
{ name: "Cancel Team Invite", value: "teamCancelInvite", description: "Cancel a pending team invitation", action: "Cancel team invite" },
|
|
67
|
+
],
|
|
68
|
+
default: "teamListMembers",
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
// --- Team ID (shared) ---
|
|
72
|
+
{
|
|
73
|
+
displayName: "Team ID",
|
|
74
|
+
name: "teamId",
|
|
75
|
+
type: "string",
|
|
76
|
+
default: "",
|
|
77
|
+
required: true,
|
|
78
|
+
description: "Team UUID",
|
|
79
|
+
displayOptions: {
|
|
80
|
+
show: {
|
|
81
|
+
operation: [
|
|
82
|
+
"teamDelete",
|
|
83
|
+
"teamRevokeKey",
|
|
84
|
+
"teamListKeys",
|
|
85
|
+
"teamListMembers",
|
|
86
|
+
"teamAddMember",
|
|
87
|
+
"teamRemoveMember",
|
|
88
|
+
"teamInviteMember",
|
|
89
|
+
"teamListInvites",
|
|
90
|
+
"teamCancelInvite",
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
// --- Key ID for revoke ---
|
|
97
|
+
{
|
|
98
|
+
displayName: "Key ID",
|
|
99
|
+
name: "keyId",
|
|
100
|
+
type: "string",
|
|
101
|
+
default: "",
|
|
102
|
+
required: true,
|
|
103
|
+
description: "UUID of the team API key to revoke",
|
|
104
|
+
displayOptions: { show: { operation: ["teamRevokeKey"] } },
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
// --- User ID for add/remove member ---
|
|
108
|
+
{
|
|
109
|
+
displayName: "User ID",
|
|
110
|
+
name: "userId",
|
|
111
|
+
type: "string",
|
|
112
|
+
default: "",
|
|
113
|
+
required: true,
|
|
114
|
+
description: "UUID of the user to add or remove",
|
|
115
|
+
displayOptions: { show: { operation: ["teamAddMember", "teamRemoveMember"] } },
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
// --- Role for add member / invite ---
|
|
119
|
+
{
|
|
120
|
+
displayName: "Role",
|
|
121
|
+
name: "role",
|
|
122
|
+
type: "options",
|
|
123
|
+
options: [
|
|
124
|
+
{ name: "Admin", value: "admin" },
|
|
125
|
+
{ name: "Member", value: "member" },
|
|
126
|
+
],
|
|
127
|
+
default: "member",
|
|
128
|
+
description: "Role for the team member",
|
|
129
|
+
displayOptions: { show: { operation: ["teamAddMember", "teamInviteMember"] } },
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
// --- Invite email ---
|
|
133
|
+
{
|
|
134
|
+
displayName: "Email",
|
|
135
|
+
name: "inviteEmail",
|
|
136
|
+
type: "string",
|
|
137
|
+
default: "",
|
|
138
|
+
required: true,
|
|
139
|
+
description: "Email address to send the invitation to",
|
|
140
|
+
displayOptions: { show: { operation: ["teamInviteMember"] } },
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
// --- Invite ID for cancel ---
|
|
144
|
+
{
|
|
145
|
+
displayName: "Invite ID",
|
|
146
|
+
name: "inviteId",
|
|
147
|
+
type: "string",
|
|
148
|
+
default: "",
|
|
149
|
+
required: true,
|
|
150
|
+
description: "UUID of the invitation to cancel",
|
|
151
|
+
displayOptions: { show: { operation: ["teamCancelInvite"] } },
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
|
157
|
+
const items = this.getInputData();
|
|
158
|
+
const returnData: INodeExecutionData[] = [];
|
|
159
|
+
const credentials = await this.getCredentials("dominusNodeApi");
|
|
160
|
+
|
|
161
|
+
const apiKey = credentials.apiKey as string;
|
|
162
|
+
const baseUrl = (credentials.baseUrl as string) || "https://api.dominusnode.com";
|
|
163
|
+
|
|
164
|
+
if (!apiKey) {
|
|
165
|
+
throw new NodeOperationError(this.getNode(), "API Key is required");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const agentSecret = (credentials.agentSecret as string) || undefined;
|
|
169
|
+
const auth = new DominusNodeAuth(apiKey, baseUrl, 30000, agentSecret);
|
|
170
|
+
const operation = this.getNodeParameter("operation", 0) as string;
|
|
171
|
+
|
|
172
|
+
for (let i = 0; i < items.length; i++) {
|
|
173
|
+
try {
|
|
174
|
+
let result: unknown;
|
|
175
|
+
|
|
176
|
+
// All operations require teamId
|
|
177
|
+
const teamId = this.getNodeParameter("teamId", i) as string;
|
|
178
|
+
validateUuid(this, teamId, "teamId", i);
|
|
179
|
+
|
|
180
|
+
switch (operation) {
|
|
181
|
+
case "teamDelete": {
|
|
182
|
+
result = await auth.apiRequest(
|
|
183
|
+
"DELETE",
|
|
184
|
+
`/api/teams/${encodeURIComponent(teamId)}`,
|
|
185
|
+
);
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
case "teamRevokeKey": {
|
|
190
|
+
const keyId = this.getNodeParameter("keyId", i) as string;
|
|
191
|
+
validateUuid(this, keyId, "keyId", i);
|
|
192
|
+
result = await auth.apiRequest(
|
|
193
|
+
"DELETE",
|
|
194
|
+
`/api/teams/${encodeURIComponent(teamId)}/keys/${encodeURIComponent(keyId)}`,
|
|
195
|
+
);
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
case "teamListKeys": {
|
|
200
|
+
result = await auth.apiRequest(
|
|
201
|
+
"GET",
|
|
202
|
+
`/api/teams/${encodeURIComponent(teamId)}/keys`,
|
|
203
|
+
);
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
case "teamListMembers": {
|
|
208
|
+
result = await auth.apiRequest(
|
|
209
|
+
"GET",
|
|
210
|
+
`/api/teams/${encodeURIComponent(teamId)}/members`,
|
|
211
|
+
);
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
case "teamAddMember": {
|
|
216
|
+
const userId = this.getNodeParameter("userId", i) as string;
|
|
217
|
+
const role = this.getNodeParameter("role", i, "member") as string;
|
|
218
|
+
validateUuid(this, userId, "userId", i);
|
|
219
|
+
if (role !== "member" && role !== "admin") {
|
|
220
|
+
throw new NodeOperationError(
|
|
221
|
+
this.getNode(),
|
|
222
|
+
"Role must be 'member' or 'admin'",
|
|
223
|
+
{ itemIndex: i },
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
result = await auth.apiRequest(
|
|
227
|
+
"POST",
|
|
228
|
+
`/api/teams/${encodeURIComponent(teamId)}/members`,
|
|
229
|
+
{ userId, role },
|
|
230
|
+
);
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
case "teamRemoveMember": {
|
|
235
|
+
const userId = this.getNodeParameter("userId", i) as string;
|
|
236
|
+
validateUuid(this, userId, "userId", i);
|
|
237
|
+
result = await auth.apiRequest(
|
|
238
|
+
"DELETE",
|
|
239
|
+
`/api/teams/${encodeURIComponent(teamId)}/members/${encodeURIComponent(userId)}`,
|
|
240
|
+
);
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
case "teamInviteMember": {
|
|
245
|
+
const email = this.getNodeParameter("inviteEmail", i) as string;
|
|
246
|
+
const role = this.getNodeParameter("role", i, "member") as string;
|
|
247
|
+
const safeEmail = validateEmail(this, email, i);
|
|
248
|
+
if (role !== "member" && role !== "admin") {
|
|
249
|
+
throw new NodeOperationError(
|
|
250
|
+
this.getNode(),
|
|
251
|
+
"Role must be 'member' or 'admin'",
|
|
252
|
+
{ itemIndex: i },
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
result = await auth.apiRequest(
|
|
256
|
+
"POST",
|
|
257
|
+
`/api/teams/${encodeURIComponent(teamId)}/invites`,
|
|
258
|
+
{ email: safeEmail, role },
|
|
259
|
+
);
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
case "teamListInvites": {
|
|
264
|
+
result = await auth.apiRequest(
|
|
265
|
+
"GET",
|
|
266
|
+
`/api/teams/${encodeURIComponent(teamId)}/invites`,
|
|
267
|
+
);
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
case "teamCancelInvite": {
|
|
272
|
+
const inviteId = this.getNodeParameter("inviteId", i) as string;
|
|
273
|
+
validateUuid(this, inviteId, "inviteId", i);
|
|
274
|
+
result = await auth.apiRequest(
|
|
275
|
+
"DELETE",
|
|
276
|
+
`/api/teams/${encodeURIComponent(teamId)}/invites/${encodeURIComponent(inviteId)}`,
|
|
277
|
+
);
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
default:
|
|
282
|
+
throw new NodeOperationError(
|
|
283
|
+
this.getNode(),
|
|
284
|
+
`Unknown operation: ${operation}`,
|
|
285
|
+
{ itemIndex: i },
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
returnData.push({ json: (result ?? {}) as IDataObject });
|
|
290
|
+
} catch (err) {
|
|
291
|
+
if (this.continueOnFail()) {
|
|
292
|
+
returnData.push({
|
|
293
|
+
json: {
|
|
294
|
+
error: sanitizeError(err instanceof Error ? err.message : String(err)),
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
if (err instanceof NodeOperationError) throw err;
|
|
300
|
+
throw new NodeOperationError(
|
|
301
|
+
this.getNode(),
|
|
302
|
+
sanitizeError(err instanceof Error ? err.message : String(err)),
|
|
303
|
+
{ itemIndex: i },
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return [returnData];
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ---------------------------------------------------------------------------
|
|
313
|
+
// Validation helpers
|
|
314
|
+
// ---------------------------------------------------------------------------
|
|
315
|
+
|
|
316
|
+
function validateUuid(
|
|
317
|
+
ctx: IExecuteFunctions,
|
|
318
|
+
value: string,
|
|
319
|
+
fieldName: string,
|
|
320
|
+
itemIndex: number,
|
|
321
|
+
): void {
|
|
322
|
+
if (!value || typeof value !== "string") {
|
|
323
|
+
throw new NodeOperationError(ctx.getNode(), `${fieldName} is required`, { itemIndex });
|
|
324
|
+
}
|
|
325
|
+
if (!UUID_RE.test(value)) {
|
|
326
|
+
throw new NodeOperationError(ctx.getNode(), `${fieldName} must be a valid UUID`, { itemIndex });
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function validateEmail(
|
|
331
|
+
ctx: IExecuteFunctions,
|
|
332
|
+
email: string,
|
|
333
|
+
itemIndex: number,
|
|
334
|
+
): string {
|
|
335
|
+
const trimmed = (email ?? "").trim();
|
|
336
|
+
if (!trimmed || !EMAIL_RE.test(trimmed)) {
|
|
337
|
+
throw new NodeOperationError(
|
|
338
|
+
ctx.getNode(),
|
|
339
|
+
"A valid email address is required",
|
|
340
|
+
{ itemIndex },
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
if (trimmed.length > 254) {
|
|
344
|
+
throw new NodeOperationError(
|
|
345
|
+
ctx.getNode(),
|
|
346
|
+
"Email address too long (max 254 characters)",
|
|
347
|
+
{ itemIndex },
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
return trimmed;
|
|
351
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Dominus Node Usage n8n community node.
|
|
3
3
|
*
|
|
4
|
-
* Operations:
|
|
4
|
+
* Operations (3 tools):
|
|
5
5
|
* - Check Usage: Get proxy usage statistics for a given period
|
|
6
|
+
* - Get Daily Usage: Get daily usage breakdown for the last N days
|
|
7
|
+
* - Get Top Hosts: Get top accessed hosts by bandwidth
|
|
6
8
|
*
|
|
7
9
|
* @module
|
|
8
10
|
*/
|
|
@@ -46,6 +48,18 @@ export class DominusNodeUsage implements INodeType {
|
|
|
46
48
|
description: "Get proxy usage statistics for a given period",
|
|
47
49
|
action: "Check usage",
|
|
48
50
|
},
|
|
51
|
+
{
|
|
52
|
+
name: "Get Daily Usage",
|
|
53
|
+
value: "getDailyUsage",
|
|
54
|
+
description: "Get daily usage breakdown for the last N days",
|
|
55
|
+
action: "Get daily usage",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "Get Top Hosts",
|
|
59
|
+
value: "getTopHosts",
|
|
60
|
+
description: "Get top accessed hosts by bandwidth",
|
|
61
|
+
action: "Get top hosts",
|
|
62
|
+
},
|
|
49
63
|
],
|
|
50
64
|
default: "checkUsage",
|
|
51
65
|
},
|
|
@@ -62,6 +76,22 @@ export class DominusNodeUsage implements INodeType {
|
|
|
62
76
|
description: "Time period for usage statistics",
|
|
63
77
|
displayOptions: { show: { operation: ["checkUsage"] } },
|
|
64
78
|
},
|
|
79
|
+
{
|
|
80
|
+
displayName: "Days",
|
|
81
|
+
name: "days",
|
|
82
|
+
type: "number",
|
|
83
|
+
default: 7,
|
|
84
|
+
description: "Number of days of daily usage to return (1-365)",
|
|
85
|
+
displayOptions: { show: { operation: ["getDailyUsage"] } },
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
displayName: "Limit",
|
|
89
|
+
name: "topHostsLimit",
|
|
90
|
+
type: "number",
|
|
91
|
+
default: 10,
|
|
92
|
+
description: "Number of top hosts to return (1-100)",
|
|
93
|
+
displayOptions: { show: { operation: ["getTopHosts"] } },
|
|
94
|
+
},
|
|
65
95
|
],
|
|
66
96
|
};
|
|
67
97
|
|
|
@@ -77,7 +107,8 @@ export class DominusNodeUsage implements INodeType {
|
|
|
77
107
|
throw new NodeOperationError(this.getNode(), "API Key is required");
|
|
78
108
|
}
|
|
79
109
|
|
|
80
|
-
const
|
|
110
|
+
const agentSecret = (credentials.agentSecret as string) || undefined;
|
|
111
|
+
const auth = new DominusNodeAuth(apiKey, baseUrl, 30000, agentSecret);
|
|
81
112
|
|
|
82
113
|
for (let i = 0; i < items.length; i++) {
|
|
83
114
|
try {
|
|
@@ -99,6 +130,28 @@ export class DominusNodeUsage implements INodeType {
|
|
|
99
130
|
const params = new URLSearchParams({ since, until });
|
|
100
131
|
const result = await auth.apiRequest("GET", `/api/usage?${params.toString()}`);
|
|
101
132
|
|
|
133
|
+
returnData.push({ json: (result ?? {}) as IDataObject });
|
|
134
|
+
} else if (operation === "getDailyUsage") {
|
|
135
|
+
const days = this.getNodeParameter("days", i, 7) as number;
|
|
136
|
+
if (!Number.isInteger(days) || days < 1 || days > 365) {
|
|
137
|
+
throw new NodeOperationError(
|
|
138
|
+
this.getNode(),
|
|
139
|
+
"Days must be an integer between 1 and 365",
|
|
140
|
+
{ itemIndex: i },
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
const result = await auth.apiRequest("GET", `/api/usage/daily?days=${days}`);
|
|
144
|
+
returnData.push({ json: (result ?? {}) as IDataObject });
|
|
145
|
+
} else if (operation === "getTopHosts") {
|
|
146
|
+
const limit = this.getNodeParameter("topHostsLimit", i, 10) as number;
|
|
147
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
148
|
+
throw new NodeOperationError(
|
|
149
|
+
this.getNode(),
|
|
150
|
+
"Limit must be an integer between 1 and 100",
|
|
151
|
+
{ itemIndex: i },
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
const result = await auth.apiRequest("GET", `/api/usage/top-hosts?limit=${limit}`);
|
|
102
155
|
returnData.push({ json: (result ?? {}) as IDataObject });
|
|
103
156
|
} else {
|
|
104
157
|
throw new NodeOperationError(
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Dominus Node Wallet n8n community node.
|
|
3
3
|
*
|
|
4
|
-
* Operations:
|
|
4
|
+
* Operations (25 tools):
|
|
5
5
|
* - Check Balance: Get current wallet balance
|
|
6
6
|
* - Top Up (Stripe): Create a Stripe checkout session
|
|
7
7
|
* - Top Up (Crypto): Create a crypto payment invoice
|
|
8
8
|
* - Top Up (PayPal): Create a PayPal checkout session via Stripe
|
|
9
|
+
* - Get Transactions: Get wallet transaction history
|
|
10
|
+
* - Get Forecast: Get wallet spend forecast
|
|
11
|
+
* - Check Payment: Check crypto payment invoice status
|
|
9
12
|
* - Create Agentic Wallet: Create a sub-wallet with spending limits
|
|
10
13
|
* - Fund Agentic Wallet: Transfer funds to an agentic wallet
|
|
11
14
|
* - Get Agentic Wallet Balance: Check an agentic wallet's balance
|
|
@@ -23,6 +26,7 @@
|
|
|
23
26
|
* - Team Usage: Get team wallet transaction history
|
|
24
27
|
* - Update Team: Update team name/max members
|
|
25
28
|
* - Update Team Member Role: Update a team member's role
|
|
29
|
+
* - x402 Info: Get x402 micropayment protocol information
|
|
26
30
|
*
|
|
27
31
|
* @module
|
|
28
32
|
*/
|
|
@@ -70,6 +74,9 @@ export class DominusNodeWallet implements INodeType {
|
|
|
70
74
|
{ name: "Top Up (Stripe)", value: "topUpStripe", description: "Create a Stripe checkout session", action: "Top up stripe" },
|
|
71
75
|
{ name: "Top Up (Crypto)", value: "topUpCrypto", description: "Create a crypto payment invoice", action: "Top up crypto" },
|
|
72
76
|
{ name: "Top Up (PayPal)", value: "topUpPaypal", description: "Create a PayPal checkout session", action: "Top up paypal" },
|
|
77
|
+
{ name: "Get Transactions", value: "getTransactions", description: "Get wallet transaction history", action: "Get transactions" },
|
|
78
|
+
{ name: "Get Forecast", value: "getForecast", description: "Get wallet spend forecast", action: "Get forecast" },
|
|
79
|
+
{ name: "Check Payment", value: "checkPayment", description: "Check crypto payment invoice status", action: "Check payment" },
|
|
73
80
|
// Agentic Wallets
|
|
74
81
|
{ name: "Create Agentic Wallet", value: "createAgenticWallet", description: "Create a sub-wallet with spending limits", action: "Create agentic wallet" },
|
|
75
82
|
{ name: "Fund Agentic Wallet", value: "fundAgenticWallet", description: "Transfer funds to an agentic wallet", action: "Fund agentic wallet" },
|
|
@@ -137,6 +144,25 @@ export class DominusNodeWallet implements INodeType {
|
|
|
137
144
|
displayOptions: { show: { operation: ["topUpCrypto"] } },
|
|
138
145
|
},
|
|
139
146
|
|
|
147
|
+
// --- Transaction / payment params ---
|
|
148
|
+
{
|
|
149
|
+
displayName: "Limit",
|
|
150
|
+
name: "walletTransactionLimit",
|
|
151
|
+
type: "number",
|
|
152
|
+
default: 20,
|
|
153
|
+
description: "Number of transactions to return (1-100)",
|
|
154
|
+
displayOptions: { show: { operation: ["getTransactions"] } },
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
displayName: "Invoice ID",
|
|
158
|
+
name: "invoiceId",
|
|
159
|
+
type: "string",
|
|
160
|
+
default: "",
|
|
161
|
+
required: true,
|
|
162
|
+
description: "Crypto payment invoice UUID to check status",
|
|
163
|
+
displayOptions: { show: { operation: ["checkPayment"] } },
|
|
164
|
+
},
|
|
165
|
+
|
|
140
166
|
// --- Agentic wallet params ---
|
|
141
167
|
{
|
|
142
168
|
displayName: "Label",
|
|
@@ -343,7 +369,8 @@ export class DominusNodeWallet implements INodeType {
|
|
|
343
369
|
throw new NodeOperationError(this.getNode(), "API Key is required");
|
|
344
370
|
}
|
|
345
371
|
|
|
346
|
-
const
|
|
372
|
+
const agentSecret = (credentials.agentSecret as string) || undefined;
|
|
373
|
+
const auth = new DominusNodeAuth(apiKey, baseUrl, 30000, agentSecret);
|
|
347
374
|
const operation = this.getNodeParameter("operation", 0) as string;
|
|
348
375
|
|
|
349
376
|
for (let i = 0; i < items.length; i++) {
|
|
@@ -410,6 +437,31 @@ export class DominusNodeWallet implements INodeType {
|
|
|
410
437
|
break;
|
|
411
438
|
}
|
|
412
439
|
|
|
440
|
+
case "getTransactions": {
|
|
441
|
+
const limit = this.getNodeParameter("walletTransactionLimit", i, 20) as number;
|
|
442
|
+
validateLimit(this, limit, i);
|
|
443
|
+
|
|
444
|
+
const params = new URLSearchParams();
|
|
445
|
+
params.set("limit", String(limit));
|
|
446
|
+
result = await auth.apiRequest("GET", `/api/wallet/transactions?${params.toString()}`);
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
case "getForecast": {
|
|
451
|
+
result = await auth.apiRequest("GET", "/api/wallet/forecast");
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
case "checkPayment": {
|
|
456
|
+
const invoiceId = this.getNodeParameter("invoiceId", i) as string;
|
|
457
|
+
validateUuid(this, invoiceId, "invoiceId", i);
|
|
458
|
+
result = await auth.apiRequest(
|
|
459
|
+
"GET",
|
|
460
|
+
`/api/wallet/topup/crypto/${encodeURIComponent(invoiceId)}/status`,
|
|
461
|
+
);
|
|
462
|
+
break;
|
|
463
|
+
}
|
|
464
|
+
|
|
413
465
|
// ----- Agentic Wallets -----
|
|
414
466
|
case "createAgenticWallet": {
|
|
415
467
|
const label = this.getNodeParameter("agenticLabel", i) as string;
|
package/src/shared/auth.ts
CHANGED
|
@@ -156,14 +156,18 @@ export class DominusNodeAuth {
|
|
|
156
156
|
private token: string | null = null;
|
|
157
157
|
private authPromise: Promise<void> | null = null;
|
|
158
158
|
|
|
159
|
+
private agentSecret?: string;
|
|
160
|
+
|
|
159
161
|
constructor(
|
|
160
162
|
private apiKey: string,
|
|
161
163
|
private baseUrl: string,
|
|
162
164
|
private timeoutMs: number = 30000,
|
|
165
|
+
agentSecret?: string,
|
|
163
166
|
) {
|
|
164
167
|
if (!apiKey || typeof apiKey !== "string") {
|
|
165
168
|
throw new Error("apiKey is required and must be a non-empty string");
|
|
166
169
|
}
|
|
170
|
+
this.agentSecret = agentSecret || process.env.DOMINUSNODE_AGENT_SECRET;
|
|
167
171
|
}
|
|
168
172
|
|
|
169
173
|
/**
|
|
@@ -202,6 +206,11 @@ export class DominusNodeAuth {
|
|
|
202
206
|
Authorization: `Bearer ${this.token}`,
|
|
203
207
|
};
|
|
204
208
|
|
|
209
|
+
if (this.agentSecret) {
|
|
210
|
+
headers["X-DominusNode-Agent"] = "mcp";
|
|
211
|
+
headers["X-DominusNode-Agent-Secret"] = this.agentSecret;
|
|
212
|
+
}
|
|
213
|
+
|
|
205
214
|
const response = await fetch(url, {
|
|
206
215
|
method,
|
|
207
216
|
headers,
|
|
@@ -247,12 +256,18 @@ export class DominusNodeAuth {
|
|
|
247
256
|
}
|
|
248
257
|
|
|
249
258
|
private async authenticate(): Promise<void> {
|
|
259
|
+
const authHeaders: Record<string, string> = {
|
|
260
|
+
"User-Agent": "n8n-nodes-dominusnode/1.0.0",
|
|
261
|
+
"Content-Type": "application/json",
|
|
262
|
+
};
|
|
263
|
+
if (this.agentSecret) {
|
|
264
|
+
authHeaders["X-DominusNode-Agent"] = "mcp";
|
|
265
|
+
authHeaders["X-DominusNode-Agent-Secret"] = this.agentSecret;
|
|
266
|
+
}
|
|
267
|
+
|
|
250
268
|
const response = await fetch(`${this.baseUrl}/api/auth/verify-key`, {
|
|
251
269
|
method: "POST",
|
|
252
|
-
headers:
|
|
253
|
-
"User-Agent": "n8n-nodes-dominusnode/1.0.0",
|
|
254
|
-
"Content-Type": "application/json",
|
|
255
|
-
},
|
|
270
|
+
headers: authHeaders,
|
|
256
271
|
body: JSON.stringify({ apiKey: this.apiKey }),
|
|
257
272
|
signal: AbortSignal.timeout(this.timeoutMs),
|
|
258
273
|
redirect: "error",
|
|
@@ -76,10 +76,10 @@ describe("DominusNodeProxy", () => {
|
|
|
76
76
|
]);
|
|
77
77
|
});
|
|
78
78
|
|
|
79
|
-
it("has
|
|
79
|
+
it("has four operations", () => {
|
|
80
80
|
const opProp = node.description.properties.find((p) => p.name === "operation");
|
|
81
81
|
expect(opProp).toBeDefined();
|
|
82
|
-
expect((opProp as any).options).toHaveLength(
|
|
82
|
+
expect((opProp as any).options).toHaveLength(4);
|
|
83
83
|
});
|
|
84
84
|
});
|
|
85
85
|
|
|
@@ -95,11 +95,11 @@ describe("DominusNodeUsage - metadata", () => {
|
|
|
95
95
|
]);
|
|
96
96
|
});
|
|
97
97
|
|
|
98
|
-
it("has
|
|
98
|
+
it("has three operations", () => {
|
|
99
99
|
const node = new DominusNodeUsage();
|
|
100
100
|
const opProp = node.description.properties.find((p) => p.name === "operation");
|
|
101
101
|
expect(opProp).toBeDefined();
|
|
102
|
-
expect((opProp as any).options).toHaveLength(
|
|
102
|
+
expect((opProp as any).options).toHaveLength(3);
|
|
103
103
|
});
|
|
104
104
|
|
|
105
105
|
it("has period options: day, week, month", () => {
|
|
@@ -93,11 +93,11 @@ describe("DominusNodeWallet - metadata", () => {
|
|
|
93
93
|
]);
|
|
94
94
|
});
|
|
95
95
|
|
|
96
|
-
it("has
|
|
96
|
+
it("has 25 operations", () => {
|
|
97
97
|
const node = new DominusNodeWallet();
|
|
98
98
|
const opProp = node.description.properties.find((p) => p.name === "operation");
|
|
99
99
|
expect(opProp).toBeDefined();
|
|
100
|
-
expect((opProp as any).options).toHaveLength(
|
|
100
|
+
expect((opProp as any).options).toHaveLength(25);
|
|
101
101
|
});
|
|
102
102
|
});
|
|
103
103
|
|