@tadnt2003/n8n-nodes-infisical 0.3.0 → 0.3.7
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/LICENSE
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 Kennis AI
|
|
4
|
-
Copyright (c) 2026 Nguyen Thanh Dat
|
|
5
|
-
|
|
6
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
-
in the Software without restriction, including without limitation the rights
|
|
9
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
-
furnished to do so, subject to the following conditions:
|
|
12
|
-
|
|
13
|
-
The above copyright notice and this permission notice shall be included in all
|
|
14
|
-
copies or substantial portions of the Software.
|
|
15
|
-
|
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Kennis AI
|
|
4
|
+
Copyright (c) 2026 Nguyen Thanh Dat
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
@@ -15,6 +15,7 @@ const CREDENTIAL_FIELD_MAPS = {
|
|
|
15
15
|
{ param: 'httpNode', secretKey: 'httpNode' },
|
|
16
16
|
],
|
|
17
17
|
googleOAuth2Api: [
|
|
18
|
+
{ param: 'serverUrl', secretKey: 'serverUrl' },
|
|
18
19
|
{ param: 'clientId', secretKey: 'clientId' },
|
|
19
20
|
{ param: 'clientSecret', secretKey: 'clientSecret' },
|
|
20
21
|
{ param: 'scope', secretKey: 'scope' },
|
|
@@ -210,18 +211,25 @@ const CREDENTIAL_FIELD_MAPS = {
|
|
|
210
211
|
{ param: 'algorithm', secretKey: 'algorithm' },
|
|
211
212
|
],
|
|
212
213
|
};
|
|
214
|
+
// Lossless encoding: [A-Za-z0-9-] pass through, _ → __, everything else → _XX hex sequences.
|
|
215
|
+
// Infisical folder names only allow [a-zA-Z0-9_-], so % cannot be used as an escape character.
|
|
216
|
+
function toFolderName(name) {
|
|
217
|
+
return [...name].map(c => {
|
|
218
|
+
if (/[A-Za-z0-9-]/.test(c))
|
|
219
|
+
return c;
|
|
220
|
+
if (c === '_')
|
|
221
|
+
return '__';
|
|
222
|
+
return encodeURIComponent(c).replace(/%/g, '_');
|
|
223
|
+
}).join('');
|
|
224
|
+
}
|
|
225
|
+
function fromFolderName(slug) {
|
|
226
|
+
const restored = slug.replace(/_([0-9A-Fa-f]{2}|_)/g, (_, p1) => p1 === '_' ? '_' : '%' + p1);
|
|
227
|
+
return decodeURIComponent(restored);
|
|
228
|
+
}
|
|
213
229
|
function buildSecretPath(rootPath, credentialName) {
|
|
214
230
|
const root = rootPath.replace(/\/+$/, '') || '/';
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
// Fix 7.5: removed `typeof value === 'boolean' && value === false` — false is a meaningful value
|
|
218
|
-
// that must be written to Infisical (e.g. ssl:false, sshTunnel:false as condition keys).
|
|
219
|
-
function isEmptyValue(value) {
|
|
220
|
-
if (value === null || value === undefined)
|
|
221
|
-
return true;
|
|
222
|
-
if (typeof value === 'string' && value.trim() === '')
|
|
223
|
-
return true;
|
|
224
|
-
return false;
|
|
231
|
+
const folder = toFolderName(credentialName);
|
|
232
|
+
return root === '/' ? `/${folder}` : `${root}/${folder}`;
|
|
225
233
|
}
|
|
226
234
|
// Fix 7.4: handles `number` type (port, connectTimeout, maxConnections, etc.)
|
|
227
235
|
// Fix 7.7: reads schema's own `default` before falling back to enum heuristic
|
|
@@ -257,6 +265,23 @@ function coerceValue(raw, def) {
|
|
|
257
265
|
return raw === 'true' || raw === '1';
|
|
258
266
|
return raw;
|
|
259
267
|
}
|
|
268
|
+
// Decide whether a conditional branch fires given its condition key and values.
|
|
269
|
+
//
|
|
270
|
+
// When condKey IS in schema properties, fire only when the current value matches condValues.
|
|
271
|
+
//
|
|
272
|
+
// When condKey is NOT in schema properties (e.g. useDynamicClientRegistration on
|
|
273
|
+
// googleOAuth2Api), JSON Schema evaluates the if-clause vacuously — the properties keyword
|
|
274
|
+
// has nothing to check, so it passes, and both the [true] branch and the [false] branch
|
|
275
|
+
// appear to fire simultaneously. That is the "vacuous truth" problem.
|
|
276
|
+
//
|
|
277
|
+
// n8n avoids it by internally defaulting the absent key to false before validating, so only
|
|
278
|
+
// the branch whose condValues contain a falsy value actually fires (the "standard mode"
|
|
279
|
+
// branch). We mirror that: condValues=[false] fires, condValues=[true] does not.
|
|
280
|
+
function conditionFires(condKeyInSchema, condValues, condVal) {
|
|
281
|
+
if (condKeyInSchema)
|
|
282
|
+
return condValues.includes(condVal);
|
|
283
|
+
return condValues.some((v) => !v);
|
|
284
|
+
}
|
|
260
285
|
// Validate credential data against the n8n schema's required fields and conditional requirements.
|
|
261
286
|
// For form mode, pass availableFormFields to skip checks for fields the form cannot provide.
|
|
262
287
|
function validateAgainstSchema(data, schemaInfo, availableFormFields) {
|
|
@@ -264,20 +289,20 @@ function validateAgainstSchema(data, schemaInfo, availableFormFields) {
|
|
|
264
289
|
for (const field of schemaInfo.topRequired) {
|
|
265
290
|
if (availableFormFields && !availableFormFields.has(field))
|
|
266
291
|
continue;
|
|
267
|
-
if (
|
|
268
|
-
errors.push(`"${field}" is required but
|
|
292
|
+
if (data[field] === undefined || data[field] === null) {
|
|
293
|
+
errors.push(`"${field}" is required but not provided`);
|
|
269
294
|
}
|
|
270
295
|
}
|
|
271
296
|
for (const { condKey, condValues, thenRequired } of schemaInfo.condBranches) {
|
|
272
297
|
const condVal = data[condKey];
|
|
273
298
|
const condKeyInSchema = condKey in schemaInfo.props;
|
|
274
|
-
if (
|
|
299
|
+
if (conditionFires(condKeyInSchema, condValues, condVal)) {
|
|
275
300
|
for (const field of thenRequired) {
|
|
276
301
|
if (availableFormFields && !availableFormFields.has(field))
|
|
277
302
|
continue;
|
|
278
|
-
if (
|
|
303
|
+
if (data[field] === undefined || data[field] === null) {
|
|
279
304
|
const when = condKeyInSchema ? ` when "${condKey}" is "${String(condVal)}"` : '';
|
|
280
|
-
errors.push(`"${field}" is required${when} but
|
|
305
|
+
errors.push(`"${field}" is required${when} but not provided`);
|
|
281
306
|
}
|
|
282
307
|
}
|
|
283
308
|
}
|
|
@@ -430,10 +455,8 @@ function applyCondBranches(fullData, schemaInfo) {
|
|
|
430
455
|
var _a, _b;
|
|
431
456
|
for (const { condKey, condValues, thenRequired, elseProhibited } of schemaInfo.condBranches) {
|
|
432
457
|
const condVal = fullData[condKey];
|
|
433
|
-
// When condKey is absent from schema properties it can't appear in the data,
|
|
434
|
-
// so JSON Schema's `properties` validator skips it → condition fires vacuously.
|
|
435
458
|
const condKeyInSchema = condKey in schemaInfo.props;
|
|
436
|
-
if (
|
|
459
|
+
if (conditionFires(condKeyInSchema, condValues, condVal)) {
|
|
437
460
|
// Condition fires → fill any missing then-required fields with safe defaults.
|
|
438
461
|
for (const field of thenRequired) {
|
|
439
462
|
if (field in fullData)
|
|
@@ -569,7 +592,8 @@ async function autoSyncFromInfisical(ctx, apiUrl, baseHeaders, i) {
|
|
|
569
592
|
credentialData[key] = coerceValue(raw, schemaInfo === null || schemaInfo === void 0 ? void 0 : schemaInfo.props[key]);
|
|
570
593
|
}
|
|
571
594
|
}
|
|
572
|
-
const
|
|
595
|
+
const credentialName = fromFolderName(folderName);
|
|
596
|
+
const existing = credByName.get(credentialName);
|
|
573
597
|
if (existing) {
|
|
574
598
|
// Fix 7.2: apply the same fullData build logic as the create path so that condition-key
|
|
575
599
|
// changes (e.g. ssl:false→true) get their required fields filled and their prohibited
|
|
@@ -607,7 +631,7 @@ async function autoSyncFromInfisical(ctx, apiUrl, baseHeaders, i) {
|
|
|
607
631
|
method: 'POST',
|
|
608
632
|
url: `${n8nApiUrl}/api/v1/credentials`,
|
|
609
633
|
headers: n8nHeaders,
|
|
610
|
-
body: { name:
|
|
634
|
+
body: { name: credentialName, type: credentialType, data: fullData },
|
|
611
635
|
});
|
|
612
636
|
results.push({
|
|
613
637
|
json: Object.assign(Object.assign({}, created), { action: 'created', secretPath, secretCount: secrets.length }),
|
|
@@ -691,7 +715,7 @@ async function executeSyncOperation(ctx, apiUrl, baseHeaders, operation, i) {
|
|
|
691
715
|
method: 'POST',
|
|
692
716
|
url: `${apiUrl}/v2/folders`,
|
|
693
717
|
headers: baseHeaders,
|
|
694
|
-
body: { projectId, environment, name: credentialName, path: folderPath },
|
|
718
|
+
body: { projectId, environment, name: toFolderName(credentialName), path: folderPath },
|
|
695
719
|
});
|
|
696
720
|
}
|
|
697
721
|
catch (err) {
|
|
@@ -701,12 +725,14 @@ async function executeSyncOperation(ctx, apiUrl, baseHeaders, operation, i) {
|
|
|
701
725
|
throw err;
|
|
702
726
|
}
|
|
703
727
|
}
|
|
704
|
-
// Collect
|
|
728
|
+
// Collect credential fields as Infisical secrets; empty strings are written as "" so that
|
|
729
|
+
// required-but-blank fields (e.g. serverUrl on googleOAuth2Api) don't fail schema validation
|
|
730
|
+
// on the way back. Only null/undefined is skipped.
|
|
705
731
|
const secretMetadata = [{ key: 'n8n_credential_type', value: credentialType }];
|
|
706
732
|
const secrets = [];
|
|
707
733
|
if (inputMode === 'json') {
|
|
708
734
|
for (const [key, value] of Object.entries(parsedJson !== null && parsedJson !== void 0 ? parsedJson : {})) {
|
|
709
|
-
if (
|
|
735
|
+
if (value === null || value === undefined)
|
|
710
736
|
continue;
|
|
711
737
|
if (fetchedSchemaProps && !(key in fetchedSchemaProps))
|
|
712
738
|
continue;
|
|
@@ -721,7 +747,7 @@ async function executeSyncOperation(ctx, apiUrl, baseHeaders, operation, i) {
|
|
|
721
747
|
}
|
|
722
748
|
for (const { param, secretKey } of fieldMap) {
|
|
723
749
|
const value = ctx.getNodeParameter(param, i, '');
|
|
724
|
-
if (
|
|
750
|
+
if (value === null || value === undefined)
|
|
725
751
|
continue;
|
|
726
752
|
secrets.push({ secretKey, secretValue: String(value), secretMetadata });
|
|
727
753
|
}
|
package/package.json
CHANGED
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@tadnt2003/n8n-nodes-infisical",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "n8n community node for Infisical - Secret management platform",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"n8n-community-node-package",
|
|
7
|
-
"n8n",
|
|
8
|
-
"infisical",
|
|
9
|
-
"secrets",
|
|
10
|
-
"secrets-management"
|
|
11
|
-
],
|
|
12
|
-
"license": "MIT",
|
|
13
|
-
"homepage": "https://github.com/TadNT2003/n8n-nodes-infisical",
|
|
14
|
-
"author": {
|
|
15
|
-
"name": "Nguyen Thanh Dat",
|
|
16
|
-
"email": "ntdatwows2003@gmail.com",
|
|
17
|
-
"url": "https://github.com/TadNT2003"
|
|
18
|
-
},
|
|
19
|
-
"repository": {
|
|
20
|
-
"type": "git",
|
|
21
|
-
"url": "git+https://github.com/TadNT2003/n8n-nodes-infisical.git"
|
|
22
|
-
},
|
|
23
|
-
"scripts": {
|
|
24
|
-
"build": "tsc && gulp build",
|
|
25
|
-
"dev": "tsc --watch",
|
|
26
|
-
"format": "prettier nodes credentials --write",
|
|
27
|
-
"lint": "eslint nodes credentials package.json",
|
|
28
|
-
"lintfix": "eslint nodes credentials package.json --fix",
|
|
29
|
-
"prepublishOnly": "npm run build && npm run lint"
|
|
30
|
-
},
|
|
31
|
-
"files": [
|
|
32
|
-
"dist"
|
|
33
|
-
],
|
|
34
|
-
"n8n": {
|
|
35
|
-
"n8nNodesApiVersion": 1,
|
|
36
|
-
"credentials": [
|
|
37
|
-
"dist/credentials/InfisicalApi.credentials.js"
|
|
38
|
-
],
|
|
39
|
-
"nodes": [
|
|
40
|
-
"dist/nodes/Infisical/Infisical.node.js",
|
|
41
|
-
"dist/nodes/InfisicalSync/InfisicalSync.node.js"
|
|
42
|
-
]
|
|
43
|
-
},
|
|
44
|
-
"devDependencies": {
|
|
45
|
-
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
|
46
|
-
"@typescript-eslint/parser": "^5.45.0",
|
|
47
|
-
"eslint": "^8.29.0",
|
|
48
|
-
"eslint-plugin-n8n-nodes-base": "^1.11.0",
|
|
49
|
-
"gulp": "^4.0.2",
|
|
50
|
-
"n8n-workflow": "^1.120.0",
|
|
51
|
-
"prettier": "^2.7.1",
|
|
52
|
-
"typescript": "~5.3.0"
|
|
53
|
-
},
|
|
54
|
-
"peerDependencies": {
|
|
55
|
-
"n8n-workflow": "*"
|
|
56
|
-
}
|
|
57
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@tadnt2003/n8n-nodes-infisical",
|
|
3
|
+
"version": "0.3.7",
|
|
4
|
+
"description": "n8n community node for Infisical - Secret management platform",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"n8n",
|
|
8
|
+
"infisical",
|
|
9
|
+
"secrets",
|
|
10
|
+
"secrets-management"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"homepage": "https://github.com/TadNT2003/n8n-nodes-infisical",
|
|
14
|
+
"author": {
|
|
15
|
+
"name": "Nguyen Thanh Dat",
|
|
16
|
+
"email": "ntdatwows2003@gmail.com",
|
|
17
|
+
"url": "https://github.com/TadNT2003"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/TadNT2003/n8n-nodes-infisical.git"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc && gulp build",
|
|
25
|
+
"dev": "tsc --watch",
|
|
26
|
+
"format": "prettier nodes credentials --write",
|
|
27
|
+
"lint": "eslint nodes credentials package.json",
|
|
28
|
+
"lintfix": "eslint nodes credentials package.json --fix",
|
|
29
|
+
"prepublishOnly": "npm run build && npm run lint"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist"
|
|
33
|
+
],
|
|
34
|
+
"n8n": {
|
|
35
|
+
"n8nNodesApiVersion": 1,
|
|
36
|
+
"credentials": [
|
|
37
|
+
"dist/credentials/InfisicalApi.credentials.js"
|
|
38
|
+
],
|
|
39
|
+
"nodes": [
|
|
40
|
+
"dist/nodes/Infisical/Infisical.node.js",
|
|
41
|
+
"dist/nodes/InfisicalSync/InfisicalSync.node.js"
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
|
46
|
+
"@typescript-eslint/parser": "^5.45.0",
|
|
47
|
+
"eslint": "^8.29.0",
|
|
48
|
+
"eslint-plugin-n8n-nodes-base": "^1.11.0",
|
|
49
|
+
"gulp": "^4.0.2",
|
|
50
|
+
"n8n-workflow": "^1.120.0",
|
|
51
|
+
"prettier": "^2.7.1",
|
|
52
|
+
"typescript": "~5.3.0"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"n8n-workflow": "*"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
|
2
|
-
<rect width="24" height="24" rx="4" fill="#000000"/>
|
|
3
|
-
<path d="M12 6L6 9v6c0 3.5 2.5 6.5 6 7.5 3.5-1 6-4 6-7.5V9l-6-3z" fill="#FFFFFF"/>
|
|
4
|
-
<path d="M12 11v5M10 13l2 2 2-2" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
5
|
-
</svg>
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
|
2
|
-
<rect width="24" height="24" rx="4" fill="#000000"/>
|
|
3
|
-
<path d="M12 6L6 9v6c0 3.5 2.5 6.5 6 7.5 3.5-1 6-4 6-7.5V9l-6-3z" fill="#FFFFFF"/>
|
|
4
|
-
<path d="M12 11v5M10 13l2 2 2-2" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
5
|
-
</svg>
|