@tadnt2003/n8n-nodes-infisical 0.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/LICENSE +21 -0
- package/README.md +231 -0
- package/dist/credentials/InfisicalApi.credentials.d.ts +8 -0
- package/dist/credentials/InfisicalApi.credentials.js +86 -0
- package/dist/nodes/Infisical/Infisical.node.d.ts +10 -0
- package/dist/nodes/Infisical/Infisical.node.js +743 -0
- package/dist/nodes/Infisical/infisical-legacy.svg +5 -0
- package/dist/nodes/Infisical/infisical.png +0 -0
- package/dist/nodes/Infisical/infisical.svg +5 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Kennis AI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# n8n-nodes-infisical
|
|
2
|
+
|
|
3
|
+
An n8n community node for integrating [Infisical](https://infisical.com/) — the open-source secrets management platform — into your n8n workflows.
|
|
4
|
+
|
|
5
|
+
> **Forked from** [kennis-ai/n8n-nodes-infisical](https://github.com/kennis-ai/n8n-nodes-infisical).
|
|
6
|
+
> Credit and thanks to the original author at [Kennis AI](https://github.com/kennis-ai) for the initial implementation.
|
|
7
|
+
|
|
8
|
+
[n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform.
|
|
9
|
+
|
|
10
|
+
[Installation](#installation)
|
|
11
|
+
[Credentials](#credentials)
|
|
12
|
+
[Operations](#operations)
|
|
13
|
+
[Usage Examples](#usage-examples)
|
|
14
|
+
[Compatibility](#compatibility)
|
|
15
|
+
[Resources](#resources)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
Follow the [community nodes installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n documentation.
|
|
22
|
+
|
|
23
|
+
Package name: `n8n-nodes-infisical`
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Credentials
|
|
28
|
+
|
|
29
|
+
The node supports two authentication methods. **Universal Auth is strongly recommended** — Service Tokens are deprecated by Infisical.
|
|
30
|
+
|
|
31
|
+
### Universal Auth (Machine Identity) — Recommended
|
|
32
|
+
|
|
33
|
+
Universal Auth uses a Machine Identity's Client ID and Client Secret to obtain a short-lived access token automatically before each workflow execution.
|
|
34
|
+
|
|
35
|
+
1. Log in to your Infisical account (Cloud or self-hosted)
|
|
36
|
+
2. Go to **Organization Settings → Access Control → Machine Identities**
|
|
37
|
+
3. Create a new Machine Identity
|
|
38
|
+
4. Under the identity, add a **Universal Auth** client secret
|
|
39
|
+
5. Assign the identity to your project with appropriate roles
|
|
40
|
+
6. Copy the **Client ID** and **Client Secret**
|
|
41
|
+
|
|
42
|
+
**Credential fields:**
|
|
43
|
+
|
|
44
|
+
| Field | Description |
|
|
45
|
+
| --- | --- |
|
|
46
|
+
| API URL | Base URL of your Infisical API (default: `https://app.infisical.com/api`) |
|
|
47
|
+
| Authentication Type | Select **Universal Auth (Machine Identity)** |
|
|
48
|
+
| Client ID | The Machine Identity's Client ID |
|
|
49
|
+
| Client Secret | The Machine Identity's Client Secret |
|
|
50
|
+
|
|
51
|
+
### Service Token (Legacy)
|
|
52
|
+
|
|
53
|
+
Service Tokens are deprecated by Infisical and may be removed in future versions. Use Universal Auth for new integrations.
|
|
54
|
+
|
|
55
|
+
1. Log in to your Infisical account
|
|
56
|
+
2. Go to **Project Settings → Service Tokens**
|
|
57
|
+
3. Create a new Service Token with the required permissions
|
|
58
|
+
4. Copy the token
|
|
59
|
+
|
|
60
|
+
**Credential fields:**
|
|
61
|
+
|
|
62
|
+
| Field | Description |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| API URL | Base URL of your Infisical API (default: `https://app.infisical.com/api`) |
|
|
65
|
+
| Authentication Type | Select **Service Token (Legacy)** |
|
|
66
|
+
| Service Token | Your Infisical Service Token |
|
|
67
|
+
|
|
68
|
+
> For self-hosted Infisical, set API URL to your instance (e.g., `https://infisical.example.com/api`).
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Operations
|
|
73
|
+
|
|
74
|
+
### Secret
|
|
75
|
+
|
|
76
|
+
All Secret operations require: **Project ID**, **Environment**, **Secret Path** (default: `/`).
|
|
77
|
+
|
|
78
|
+
| Operation | Description | API |
|
|
79
|
+
| --- | --- | --- |
|
|
80
|
+
| **Get** | Fetch a single secret by key | `GET /v3/secrets/raw/{key}` |
|
|
81
|
+
| **Get Many** | List all secrets in a path | `GET /v3/secrets/raw` |
|
|
82
|
+
| **Create** | Create a single secret | `POST /v3/secrets/raw/{key}` |
|
|
83
|
+
| **Create Many** | Create multiple secrets in one request | `POST /v4/secrets/batch` |
|
|
84
|
+
| **Update** | Update a single secret | `PATCH /v4/secrets/{key}` |
|
|
85
|
+
| **Update Many** | Update multiple secrets in one request | `PATCH /v4/secrets/batch` |
|
|
86
|
+
| **Delete** | Delete a single secret by key | `DELETE /v3/secrets/raw/{key}` |
|
|
87
|
+
|
|
88
|
+
#### Get
|
|
89
|
+
|
|
90
|
+
Required: **Secret Key**
|
|
91
|
+
|
|
92
|
+
#### Get Many
|
|
93
|
+
|
|
94
|
+
No extra required fields. Returns each secret as a separate output item.
|
|
95
|
+
|
|
96
|
+
#### Create
|
|
97
|
+
|
|
98
|
+
Required: **Secret Key**, **Secret Value**
|
|
99
|
+
|
|
100
|
+
**Additional Fields (optional):**
|
|
101
|
+
|
|
102
|
+
| Field | Description |
|
|
103
|
+
| --- | --- |
|
|
104
|
+
| Secret Comment | Attach a comment to the secret |
|
|
105
|
+
| Skip Multiline Encoding | Disable multiline encoding for the value |
|
|
106
|
+
| Type | `shared` (default) or `personal` |
|
|
107
|
+
|
|
108
|
+
#### Create Many
|
|
109
|
+
|
|
110
|
+
Add secrets using the repeatable **Secrets** list. Each entry requires **Secret Key** and **Secret Value**.
|
|
111
|
+
|
|
112
|
+
Per-secret optional fields: Secret Comment, Skip Multiline Encoding
|
|
113
|
+
|
|
114
|
+
**Additional Fields (optional):**
|
|
115
|
+
|
|
116
|
+
| Field | Description |
|
|
117
|
+
| --- | --- |
|
|
118
|
+
| Secret Path Override | Use a different path than the top-level Secret Path |
|
|
119
|
+
|
|
120
|
+
Returns each created secret as a separate output item. If a secret protection policy is active, returns an approval object instead.
|
|
121
|
+
|
|
122
|
+
#### Update
|
|
123
|
+
|
|
124
|
+
Required: **Secret Key** (identifies the secret to update)
|
|
125
|
+
|
|
126
|
+
All update values are optional — set only what needs to change:
|
|
127
|
+
|
|
128
|
+
**Additional Fields:**
|
|
129
|
+
|
|
130
|
+
| Field | Description |
|
|
131
|
+
| --- | --- |
|
|
132
|
+
| Secret Value | The new value |
|
|
133
|
+
| New Secret Name | Rename the secret to a new key |
|
|
134
|
+
| Secret Comment | Update the attached comment |
|
|
135
|
+
| Type | `shared` or `personal` |
|
|
136
|
+
| Skip Multiline Encoding | Disable multiline encoding for the value |
|
|
137
|
+
|
|
138
|
+
#### Update Many
|
|
139
|
+
|
|
140
|
+
Add secrets using the repeatable **Secrets** list. Each entry requires **Secret Key** (the current name).
|
|
141
|
+
|
|
142
|
+
Per-secret optional fields: Secret Value, New Secret Name, Secret Comment, Skip Multiline Encoding
|
|
143
|
+
|
|
144
|
+
**Additional Fields (optional):**
|
|
145
|
+
|
|
146
|
+
| Field | Description |
|
|
147
|
+
| --- | --- |
|
|
148
|
+
| Mode | `failOnNotFound` (default) — error if secret missing; `upsert` — create if missing; `ignore` — skip missing secrets |
|
|
149
|
+
| Secret Path Override | Use a different path than the top-level Secret Path |
|
|
150
|
+
|
|
151
|
+
Returns each updated secret as a separate output item.
|
|
152
|
+
|
|
153
|
+
#### Delete
|
|
154
|
+
|
|
155
|
+
Required: **Secret Key**
|
|
156
|
+
|
|
157
|
+
### Workspace
|
|
158
|
+
|
|
159
|
+
| Operation | Description |
|
|
160
|
+
| --- | --- |
|
|
161
|
+
| **Get Many** | List all workspaces accessible with the configured credentials |
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Usage Examples
|
|
166
|
+
|
|
167
|
+
### Fetch a single secret
|
|
168
|
+
|
|
169
|
+
1. Add the **Infisical** node
|
|
170
|
+
2. Resource: `Secret` → Operation: `Get`
|
|
171
|
+
3. Fill in **Project ID**, **Environment** (e.g. `prod`), **Secret Path** (e.g. `/`), **Secret Key** (e.g. `DATABASE_URL`)
|
|
172
|
+
4. The secret object is available in the node output
|
|
173
|
+
|
|
174
|
+
### List all secrets in a folder
|
|
175
|
+
|
|
176
|
+
1. Resource: `Secret` → Operation: `Get Many`
|
|
177
|
+
2. Fill in **Project ID**, **Environment**, **Secret Path**
|
|
178
|
+
3. Each secret is output as a separate item
|
|
179
|
+
|
|
180
|
+
### Create a secret
|
|
181
|
+
|
|
182
|
+
1. Resource: `Secret` → Operation: `Create`
|
|
183
|
+
2. Fill in **Project ID**, **Environment**, **Secret Path**, **Secret Key**, **Secret Value**
|
|
184
|
+
3. Optionally add a comment or set the type via **Additional Fields**
|
|
185
|
+
|
|
186
|
+
### Bulk-create secrets
|
|
187
|
+
|
|
188
|
+
1. Resource: `Secret` → Operation: `Create Many`
|
|
189
|
+
2. Fill in **Project ID**, **Environment**, **Secret Path**
|
|
190
|
+
3. Click **Add Secret** to add each key/value pair
|
|
191
|
+
4. Each created secret is returned as an output item
|
|
192
|
+
|
|
193
|
+
### Update a secret (rename + new value)
|
|
194
|
+
|
|
195
|
+
1. Resource: `Secret` → Operation: `Update`
|
|
196
|
+
2. Fill in **Project ID**, **Environment**, **Secret Path**, **Secret Key**
|
|
197
|
+
3. Open **Additional Fields** → set **New Secret Name** and/or **Secret Value**
|
|
198
|
+
|
|
199
|
+
### Bulk-update secrets
|
|
200
|
+
|
|
201
|
+
1. Resource: `Secret` → Operation: `Update Many`
|
|
202
|
+
2. Fill in **Project ID**, **Environment**, **Secret Path**
|
|
203
|
+
3. Click **Add Secret** and enter the key and any fields to update
|
|
204
|
+
4. In **Additional Fields** → set **Mode** (e.g. `upsert` to create missing secrets)
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Compatibility
|
|
209
|
+
|
|
210
|
+
| Component | Version |
|
|
211
|
+
| --- | --- |
|
|
212
|
+
| n8n | v1.0.0+ |
|
|
213
|
+
| Infisical | Cloud and Community Edition |
|
|
214
|
+
| Infisical API | v3 (single-secret ops), v4 (update + batch ops) |
|
|
215
|
+
| n8n Nodes API | v1 |
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Resources
|
|
220
|
+
|
|
221
|
+
- [n8n Community Nodes documentation](https://docs.n8n.io/integrations/community-nodes/)
|
|
222
|
+
- [Infisical documentation](https://infisical.com/docs)
|
|
223
|
+
- [Infisical Universal Auth (Machine Identities)](https://infisical.com/docs/documentation/platform/identities/universal-auth)
|
|
224
|
+
- [Infisical API reference](https://infisical.com/docs/api-reference/overview/introduction)
|
|
225
|
+
- [Original repository — kennis-ai/n8n-nodes-infisical](https://github.com/kennis-ai/n8n-nodes-infisical)
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## License
|
|
230
|
+
|
|
231
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InfisicalApi = void 0;
|
|
4
|
+
class InfisicalApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'infisicalApi';
|
|
7
|
+
this.displayName = 'Infisical API';
|
|
8
|
+
this.icon = 'file:infisical.png';
|
|
9
|
+
this.documentationUrl = 'https://infisical.com/docs/documentation/platform/identities/universal-auth';
|
|
10
|
+
this.properties = [
|
|
11
|
+
{
|
|
12
|
+
displayName: 'API URL',
|
|
13
|
+
name: 'apiUrl',
|
|
14
|
+
type: 'string',
|
|
15
|
+
default: 'https://app.infisical.com/api',
|
|
16
|
+
required: true,
|
|
17
|
+
description: 'The URL of your Infisical instance API (e.g., https://app.infisical.com/api for Cloud or your self-hosted URL)',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
displayName: 'Authentication Type',
|
|
21
|
+
name: 'authType',
|
|
22
|
+
type: 'options',
|
|
23
|
+
options: [
|
|
24
|
+
{
|
|
25
|
+
name: 'Universal Auth (Machine Identity)',
|
|
26
|
+
value: 'universalAuth',
|
|
27
|
+
description: 'Recommended — uses Client ID and Client Secret',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'Service Token (Legacy)',
|
|
31
|
+
value: 'serviceToken',
|
|
32
|
+
description: 'Deprecated by Infisical — use Universal Auth instead',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
default: 'universalAuth',
|
|
36
|
+
description: 'The authentication method to use with Infisical',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
displayName: 'Client ID',
|
|
40
|
+
name: 'clientId',
|
|
41
|
+
type: 'string',
|
|
42
|
+
displayOptions: {
|
|
43
|
+
show: {
|
|
44
|
+
authType: ['universalAuth'],
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
default: '',
|
|
48
|
+
required: true,
|
|
49
|
+
description: 'The Client ID of your Infisical Machine Identity. Create one in Organization Settings > Identities.',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
displayName: 'Client Secret',
|
|
53
|
+
name: 'clientSecret',
|
|
54
|
+
type: 'string',
|
|
55
|
+
typeOptions: {
|
|
56
|
+
password: true,
|
|
57
|
+
},
|
|
58
|
+
displayOptions: {
|
|
59
|
+
show: {
|
|
60
|
+
authType: ['universalAuth'],
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
default: '',
|
|
64
|
+
required: true,
|
|
65
|
+
description: 'The Client Secret of your Infisical Machine Identity (Universal Auth)',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
displayName: 'Service Token',
|
|
69
|
+
name: 'apiKey',
|
|
70
|
+
type: 'string',
|
|
71
|
+
typeOptions: {
|
|
72
|
+
password: true,
|
|
73
|
+
},
|
|
74
|
+
displayOptions: {
|
|
75
|
+
show: {
|
|
76
|
+
authType: ['serviceToken'],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
default: '',
|
|
80
|
+
required: true,
|
|
81
|
+
description: 'Your Infisical Service Token (legacy). Create one in Project Settings > Service Tokens.',
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.InfisicalApi = InfisicalApi;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ICredentialTestFunctions, ICredentialsDecrypted, IExecuteFunctions, INodeCredentialTestResult, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
export declare class Infisical implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
methods: {
|
|
5
|
+
credentialTest: {
|
|
6
|
+
testInfisicalApiCredentials(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise<INodeCredentialTestResult>;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,743 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Infisical = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
async function getInfisicalToken(helpers, credentials) {
|
|
6
|
+
const apiUrl = credentials.apiUrl.replace(/\/$/, '');
|
|
7
|
+
const authType = credentials.authType || 'serviceToken';
|
|
8
|
+
if (authType === 'universalAuth') {
|
|
9
|
+
const clientId = credentials.clientId;
|
|
10
|
+
const clientSecret = credentials.clientSecret;
|
|
11
|
+
const tokenResponse = await helpers.httpRequest({
|
|
12
|
+
method: 'POST',
|
|
13
|
+
url: `${apiUrl}/v1/auth/universal-auth/login`,
|
|
14
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
15
|
+
body: `clientId=${encodeURIComponent(clientId)}&clientSecret=${encodeURIComponent(clientSecret)}`,
|
|
16
|
+
});
|
|
17
|
+
return { apiUrl, accessToken: tokenResponse.accessToken };
|
|
18
|
+
}
|
|
19
|
+
return { apiUrl, accessToken: credentials.apiKey };
|
|
20
|
+
}
|
|
21
|
+
class Infisical {
|
|
22
|
+
constructor() {
|
|
23
|
+
this.description = {
|
|
24
|
+
displayName: 'Infisical',
|
|
25
|
+
name: 'infisical',
|
|
26
|
+
icon: 'file:infisical.png',
|
|
27
|
+
group: ['transform'],
|
|
28
|
+
version: 1,
|
|
29
|
+
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
|
30
|
+
description: 'Interact with Infisical secrets management API',
|
|
31
|
+
defaults: {
|
|
32
|
+
name: 'Infisical',
|
|
33
|
+
},
|
|
34
|
+
inputs: ['main'],
|
|
35
|
+
outputs: ['main'],
|
|
36
|
+
credentials: [
|
|
37
|
+
{
|
|
38
|
+
name: 'infisicalApi',
|
|
39
|
+
required: true,
|
|
40
|
+
testedBy: 'testInfisicalApiCredentials',
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
properties: [
|
|
44
|
+
// ─── Resource ────────────────────────────────────────────────────────────
|
|
45
|
+
{
|
|
46
|
+
displayName: 'Resource',
|
|
47
|
+
name: 'resource',
|
|
48
|
+
type: 'options',
|
|
49
|
+
noDataExpression: true,
|
|
50
|
+
options: [
|
|
51
|
+
{ name: 'Secret', value: 'secret' },
|
|
52
|
+
{ name: 'Workspace', value: 'workspace' },
|
|
53
|
+
],
|
|
54
|
+
default: 'secret',
|
|
55
|
+
},
|
|
56
|
+
// ─── Secret operations ───────────────────────────────────────────────────
|
|
57
|
+
{
|
|
58
|
+
displayName: 'Operation',
|
|
59
|
+
name: 'operation',
|
|
60
|
+
type: 'options',
|
|
61
|
+
noDataExpression: true,
|
|
62
|
+
displayOptions: { show: { resource: ['secret'] } },
|
|
63
|
+
options: [
|
|
64
|
+
{
|
|
65
|
+
name: 'Create',
|
|
66
|
+
value: 'create',
|
|
67
|
+
description: 'Create a single secret',
|
|
68
|
+
action: 'Create a secret',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'Create Many',
|
|
72
|
+
value: 'createMany',
|
|
73
|
+
description: 'Create multiple secrets in one request',
|
|
74
|
+
action: 'Create many secrets',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'Delete',
|
|
78
|
+
value: 'delete',
|
|
79
|
+
description: 'Delete a secret',
|
|
80
|
+
action: 'Delete a secret',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'Get',
|
|
84
|
+
value: 'get',
|
|
85
|
+
description: 'Get a single secret by key',
|
|
86
|
+
action: 'Get a secret',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'Get Many',
|
|
90
|
+
value: 'getAll',
|
|
91
|
+
description: 'List all secrets in a path',
|
|
92
|
+
action: 'Get many secrets',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'Update',
|
|
96
|
+
value: 'update',
|
|
97
|
+
description: 'Update a single secret',
|
|
98
|
+
action: 'Update a secret',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'Update Many',
|
|
102
|
+
value: 'updateMany',
|
|
103
|
+
description: 'Update multiple secrets in one request',
|
|
104
|
+
action: 'Update many secrets',
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
default: 'get',
|
|
108
|
+
},
|
|
109
|
+
// ─── Workspace operations ────────────────────────────────────────────────
|
|
110
|
+
{
|
|
111
|
+
displayName: 'Operation',
|
|
112
|
+
name: 'operation',
|
|
113
|
+
type: 'options',
|
|
114
|
+
noDataExpression: true,
|
|
115
|
+
displayOptions: { show: { resource: ['workspace'] } },
|
|
116
|
+
options: [
|
|
117
|
+
{
|
|
118
|
+
name: 'Get Many',
|
|
119
|
+
value: 'getAll',
|
|
120
|
+
description: 'List all accessible workspaces',
|
|
121
|
+
action: 'Get many workspaces',
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
default: 'getAll',
|
|
125
|
+
},
|
|
126
|
+
// ─── Shared secret fields ────────────────────────────────────────────────
|
|
127
|
+
{
|
|
128
|
+
displayName: 'Project ID',
|
|
129
|
+
name: 'workspaceId',
|
|
130
|
+
type: 'string',
|
|
131
|
+
required: true,
|
|
132
|
+
displayOptions: { show: { resource: ['secret'] } },
|
|
133
|
+
default: '',
|
|
134
|
+
description: 'The ID of the Infisical project (workspace)',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
displayName: 'Environment',
|
|
138
|
+
name: 'environment',
|
|
139
|
+
type: 'string',
|
|
140
|
+
required: true,
|
|
141
|
+
displayOptions: { show: { resource: ['secret'] } },
|
|
142
|
+
default: 'dev',
|
|
143
|
+
description: 'The environment slug (e.g., dev, staging, prod)',
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
displayName: 'Secret Path',
|
|
147
|
+
name: 'secretPath',
|
|
148
|
+
type: 'string',
|
|
149
|
+
displayOptions: { show: { resource: ['secret'] } },
|
|
150
|
+
default: '/',
|
|
151
|
+
description: 'Folder path inside the environment (default: /)',
|
|
152
|
+
},
|
|
153
|
+
// ─── Single-secret key (get / create / update / delete) ──────────────────
|
|
154
|
+
{
|
|
155
|
+
displayName: 'Secret Key',
|
|
156
|
+
name: 'secretKey',
|
|
157
|
+
type: 'string',
|
|
158
|
+
required: true,
|
|
159
|
+
displayOptions: {
|
|
160
|
+
show: {
|
|
161
|
+
resource: ['secret'],
|
|
162
|
+
operation: ['get', 'create', 'update', 'delete'],
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
default: '',
|
|
166
|
+
description: 'The name of the secret',
|
|
167
|
+
},
|
|
168
|
+
// ─── Create: required value + optional comment/type ──────────────────────
|
|
169
|
+
{
|
|
170
|
+
displayName: 'Secret Value',
|
|
171
|
+
name: 'secretValue',
|
|
172
|
+
type: 'string',
|
|
173
|
+
required: true,
|
|
174
|
+
typeOptions: { password: true },
|
|
175
|
+
displayOptions: {
|
|
176
|
+
show: {
|
|
177
|
+
resource: ['secret'],
|
|
178
|
+
operation: ['create'],
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
default: '',
|
|
182
|
+
description: 'The value of the secret',
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
displayName: 'Additional Fields',
|
|
186
|
+
name: 'createOptions',
|
|
187
|
+
type: 'collection',
|
|
188
|
+
placeholder: 'Add Field',
|
|
189
|
+
default: {},
|
|
190
|
+
displayOptions: {
|
|
191
|
+
show: {
|
|
192
|
+
resource: ['secret'],
|
|
193
|
+
operation: ['create'],
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
options: [
|
|
197
|
+
{
|
|
198
|
+
displayName: 'Secret Comment',
|
|
199
|
+
name: 'secretComment',
|
|
200
|
+
type: 'string',
|
|
201
|
+
default: '',
|
|
202
|
+
description: 'An optional comment to attach to the secret',
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
displayName: 'Skip Multiline Encoding',
|
|
206
|
+
name: 'skipMultilineEncoding',
|
|
207
|
+
type: 'boolean',
|
|
208
|
+
default: false,
|
|
209
|
+
description: 'Whether to disable multiline encoding for the secret value',
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
displayName: 'Type',
|
|
213
|
+
name: 'type',
|
|
214
|
+
type: 'options',
|
|
215
|
+
options: [
|
|
216
|
+
{ name: 'Shared', value: 'shared' },
|
|
217
|
+
{ name: 'Personal', value: 'personal' },
|
|
218
|
+
],
|
|
219
|
+
default: 'shared',
|
|
220
|
+
description: 'Whether the secret is shared or personal',
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
},
|
|
224
|
+
// ─── Update: all fields optional ─────────────────────────────────────────
|
|
225
|
+
{
|
|
226
|
+
displayName: 'Additional Fields',
|
|
227
|
+
name: 'updateOptions',
|
|
228
|
+
type: 'collection',
|
|
229
|
+
placeholder: 'Add Field',
|
|
230
|
+
default: {},
|
|
231
|
+
displayOptions: {
|
|
232
|
+
show: {
|
|
233
|
+
resource: ['secret'],
|
|
234
|
+
operation: ['update'],
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
options: [
|
|
238
|
+
{
|
|
239
|
+
displayName: 'New Secret Name',
|
|
240
|
+
name: 'newSecretName',
|
|
241
|
+
type: 'string',
|
|
242
|
+
default: '',
|
|
243
|
+
description: 'Rename the secret to this new key name',
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
displayName: 'Secret Comment',
|
|
247
|
+
name: 'secretComment',
|
|
248
|
+
type: 'string',
|
|
249
|
+
default: '',
|
|
250
|
+
description: 'Update the comment attached to the secret',
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
displayName: 'Secret Value',
|
|
254
|
+
name: 'secretValue',
|
|
255
|
+
type: 'string',
|
|
256
|
+
typeOptions: { password: true },
|
|
257
|
+
default: '',
|
|
258
|
+
description: 'The new value for the secret',
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
displayName: 'Skip Multiline Encoding',
|
|
262
|
+
name: 'skipMultilineEncoding',
|
|
263
|
+
type: 'boolean',
|
|
264
|
+
default: false,
|
|
265
|
+
description: 'Whether to disable multiline encoding for the secret value',
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
displayName: 'Type',
|
|
269
|
+
name: 'type',
|
|
270
|
+
type: 'options',
|
|
271
|
+
options: [
|
|
272
|
+
{ name: 'Shared', value: 'shared' },
|
|
273
|
+
{ name: 'Personal', value: 'personal' },
|
|
274
|
+
],
|
|
275
|
+
default: 'shared',
|
|
276
|
+
description: 'Whether the secret is shared or personal',
|
|
277
|
+
},
|
|
278
|
+
],
|
|
279
|
+
},
|
|
280
|
+
// ─── Create Many: secrets fixedCollection ─────────────────────────────────
|
|
281
|
+
{
|
|
282
|
+
displayName: 'Secrets',
|
|
283
|
+
name: 'secrets',
|
|
284
|
+
type: 'fixedCollection',
|
|
285
|
+
typeOptions: { multipleValues: true },
|
|
286
|
+
required: true,
|
|
287
|
+
default: {},
|
|
288
|
+
displayOptions: {
|
|
289
|
+
show: {
|
|
290
|
+
resource: ['secret'],
|
|
291
|
+
operation: ['createMany'],
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
options: [
|
|
295
|
+
{
|
|
296
|
+
displayName: 'Secret',
|
|
297
|
+
name: 'values',
|
|
298
|
+
values: [
|
|
299
|
+
{
|
|
300
|
+
displayName: 'Secret Key',
|
|
301
|
+
name: 'secretKey',
|
|
302
|
+
type: 'string',
|
|
303
|
+
required: true,
|
|
304
|
+
default: '',
|
|
305
|
+
description: 'The name of the secret',
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
displayName: 'Secret Value',
|
|
309
|
+
name: 'secretValue',
|
|
310
|
+
type: 'string',
|
|
311
|
+
required: true,
|
|
312
|
+
typeOptions: { password: true },
|
|
313
|
+
default: '',
|
|
314
|
+
description: 'The value of the secret',
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
displayName: 'Secret Comment',
|
|
318
|
+
name: 'secretComment',
|
|
319
|
+
type: 'string',
|
|
320
|
+
default: '',
|
|
321
|
+
description: 'An optional comment for this secret',
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
displayName: 'Skip Multiline Encoding',
|
|
325
|
+
name: 'skipMultilineEncoding',
|
|
326
|
+
type: 'boolean',
|
|
327
|
+
default: false,
|
|
328
|
+
description: 'Whether to disable multiline encoding for this secret value',
|
|
329
|
+
},
|
|
330
|
+
],
|
|
331
|
+
},
|
|
332
|
+
],
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
displayName: 'Additional Fields',
|
|
336
|
+
name: 'createManyOptions',
|
|
337
|
+
type: 'collection',
|
|
338
|
+
placeholder: 'Add Field',
|
|
339
|
+
default: {},
|
|
340
|
+
displayOptions: {
|
|
341
|
+
show: {
|
|
342
|
+
resource: ['secret'],
|
|
343
|
+
operation: ['createMany'],
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
options: [
|
|
347
|
+
{
|
|
348
|
+
displayName: 'Secret Path Override',
|
|
349
|
+
name: 'secretPath',
|
|
350
|
+
type: 'string',
|
|
351
|
+
default: '/',
|
|
352
|
+
description: 'Override the top-level Secret Path for this batch request',
|
|
353
|
+
},
|
|
354
|
+
],
|
|
355
|
+
},
|
|
356
|
+
// ─── Update Many: secrets fixedCollection ─────────────────────────────────
|
|
357
|
+
{
|
|
358
|
+
displayName: 'Secrets',
|
|
359
|
+
name: 'secretsToUpdate',
|
|
360
|
+
type: 'fixedCollection',
|
|
361
|
+
typeOptions: { multipleValues: true },
|
|
362
|
+
required: true,
|
|
363
|
+
default: {},
|
|
364
|
+
displayOptions: {
|
|
365
|
+
show: {
|
|
366
|
+
resource: ['secret'],
|
|
367
|
+
operation: ['updateMany'],
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
options: [
|
|
371
|
+
{
|
|
372
|
+
displayName: 'Secret',
|
|
373
|
+
name: 'values',
|
|
374
|
+
values: [
|
|
375
|
+
{
|
|
376
|
+
displayName: 'Secret Key',
|
|
377
|
+
name: 'secretKey',
|
|
378
|
+
type: 'string',
|
|
379
|
+
required: true,
|
|
380
|
+
default: '',
|
|
381
|
+
description: 'The current name of the secret to update',
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
displayName: 'Secret Value',
|
|
385
|
+
name: 'secretValue',
|
|
386
|
+
type: 'string',
|
|
387
|
+
typeOptions: { password: true },
|
|
388
|
+
default: '',
|
|
389
|
+
description: 'The new value (leave blank to keep existing)',
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
displayName: 'New Secret Name',
|
|
393
|
+
name: 'newSecretName',
|
|
394
|
+
type: 'string',
|
|
395
|
+
default: '',
|
|
396
|
+
description: 'Rename the secret to this key name',
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
displayName: 'Secret Comment',
|
|
400
|
+
name: 'secretComment',
|
|
401
|
+
type: 'string',
|
|
402
|
+
default: '',
|
|
403
|
+
description: 'Update the comment for this secret',
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
displayName: 'Skip Multiline Encoding',
|
|
407
|
+
name: 'skipMultilineEncoding',
|
|
408
|
+
type: 'boolean',
|
|
409
|
+
default: false,
|
|
410
|
+
description: 'Whether to disable multiline encoding for this secret value',
|
|
411
|
+
},
|
|
412
|
+
],
|
|
413
|
+
},
|
|
414
|
+
],
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
displayName: 'Additional Fields',
|
|
418
|
+
name: 'updateManyOptions',
|
|
419
|
+
type: 'collection',
|
|
420
|
+
placeholder: 'Add Field',
|
|
421
|
+
default: {},
|
|
422
|
+
displayOptions: {
|
|
423
|
+
show: {
|
|
424
|
+
resource: ['secret'],
|
|
425
|
+
operation: ['updateMany'],
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
options: [
|
|
429
|
+
{
|
|
430
|
+
displayName: 'Mode',
|
|
431
|
+
name: 'mode',
|
|
432
|
+
type: 'options',
|
|
433
|
+
options: [
|
|
434
|
+
{
|
|
435
|
+
name: 'Fail On Not Found',
|
|
436
|
+
value: 'failOnNotFound',
|
|
437
|
+
description: 'Throw an error if any secret does not exist (default)',
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
name: 'Upsert',
|
|
441
|
+
value: 'upsert',
|
|
442
|
+
description: 'Create the secret if it does not exist',
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
name: 'Ignore',
|
|
446
|
+
value: 'ignore',
|
|
447
|
+
description: 'Skip secrets that do not exist',
|
|
448
|
+
},
|
|
449
|
+
],
|
|
450
|
+
default: 'failOnNotFound',
|
|
451
|
+
description: 'How to handle secrets that are not found',
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
displayName: 'Secret Path Override',
|
|
455
|
+
name: 'secretPath',
|
|
456
|
+
type: 'string',
|
|
457
|
+
default: '/',
|
|
458
|
+
description: 'Override the top-level Secret Path for this batch request',
|
|
459
|
+
},
|
|
460
|
+
],
|
|
461
|
+
},
|
|
462
|
+
],
|
|
463
|
+
};
|
|
464
|
+
this.methods = {
|
|
465
|
+
credentialTest: {
|
|
466
|
+
async testInfisicalApiCredentials(credential) {
|
|
467
|
+
const creds = credential.data;
|
|
468
|
+
const apiUrl = creds.apiUrl.replace(/\/$/, '');
|
|
469
|
+
const authType = creds.authType || 'serviceToken';
|
|
470
|
+
try {
|
|
471
|
+
let accessToken;
|
|
472
|
+
if (authType === 'universalAuth') {
|
|
473
|
+
const tokenResponse = await this.helpers.request({
|
|
474
|
+
method: 'POST',
|
|
475
|
+
uri: `${apiUrl}/v1/auth/universal-auth/login`,
|
|
476
|
+
form: {
|
|
477
|
+
clientId: creds.clientId,
|
|
478
|
+
clientSecret: creds.clientSecret,
|
|
479
|
+
},
|
|
480
|
+
json: true,
|
|
481
|
+
});
|
|
482
|
+
accessToken = tokenResponse.accessToken;
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
accessToken = creds.apiKey;
|
|
486
|
+
}
|
|
487
|
+
await this.helpers.request({
|
|
488
|
+
method: 'GET',
|
|
489
|
+
uri: `${apiUrl}/v1/workspace`,
|
|
490
|
+
headers: {
|
|
491
|
+
Authorization: `Bearer ${accessToken}`,
|
|
492
|
+
Accept: 'application/json',
|
|
493
|
+
},
|
|
494
|
+
json: true,
|
|
495
|
+
});
|
|
496
|
+
return { status: 'OK', message: 'Authentication successful' };
|
|
497
|
+
}
|
|
498
|
+
catch (error) {
|
|
499
|
+
return {
|
|
500
|
+
status: 'Error',
|
|
501
|
+
message: (0, n8n_workflow_1.ensureError)(error).message,
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
},
|
|
505
|
+
},
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
async execute() {
|
|
509
|
+
var _a, _b, _c;
|
|
510
|
+
const items = this.getInputData();
|
|
511
|
+
const returnData = [];
|
|
512
|
+
const resource = this.getNodeParameter('resource', 0);
|
|
513
|
+
const operation = this.getNodeParameter('operation', 0);
|
|
514
|
+
const credentials = await this.getCredentials('infisicalApi');
|
|
515
|
+
const { apiUrl, accessToken } = await getInfisicalToken(this.helpers, credentials);
|
|
516
|
+
const baseHeaders = {
|
|
517
|
+
Authorization: `Bearer ${accessToken}`,
|
|
518
|
+
Accept: 'application/json',
|
|
519
|
+
'Content-Type': 'application/json',
|
|
520
|
+
};
|
|
521
|
+
for (let i = 0; i < items.length; i++) {
|
|
522
|
+
try {
|
|
523
|
+
// ── Secret resource ─────────────────────────────────────────────────────
|
|
524
|
+
if (resource === 'secret') {
|
|
525
|
+
const workspaceId = this.getNodeParameter('workspaceId', i);
|
|
526
|
+
const environment = this.getNodeParameter('environment', i);
|
|
527
|
+
const secretPath = this.getNodeParameter('secretPath', i);
|
|
528
|
+
// ── get ───────────────────────────────────────────────────────────────
|
|
529
|
+
if (operation === 'get') {
|
|
530
|
+
const secretKey = this.getNodeParameter('secretKey', i);
|
|
531
|
+
const response = await this.helpers.httpRequest({
|
|
532
|
+
method: 'GET',
|
|
533
|
+
url: `${apiUrl}/v3/secrets/raw/${encodeURIComponent(secretKey)}`,
|
|
534
|
+
headers: baseHeaders,
|
|
535
|
+
qs: { workspaceId, environment, secretPath },
|
|
536
|
+
});
|
|
537
|
+
returnData.push({ json: response, pairedItem: { item: i } });
|
|
538
|
+
// ── getAll ────────────────────────────────────────────────────────────
|
|
539
|
+
}
|
|
540
|
+
else if (operation === 'getAll') {
|
|
541
|
+
const response = await this.helpers.httpRequest({
|
|
542
|
+
method: 'GET',
|
|
543
|
+
url: `${apiUrl}/v3/secrets/raw`,
|
|
544
|
+
headers: baseHeaders,
|
|
545
|
+
qs: { workspaceId, environment, secretPath },
|
|
546
|
+
});
|
|
547
|
+
const secrets = response.secrets;
|
|
548
|
+
for (const secret of secrets) {
|
|
549
|
+
returnData.push({ json: secret, pairedItem: { item: i } });
|
|
550
|
+
}
|
|
551
|
+
// ── create ────────────────────────────────────────────────────────────
|
|
552
|
+
}
|
|
553
|
+
else if (operation === 'create') {
|
|
554
|
+
const secretKey = this.getNodeParameter('secretKey', i);
|
|
555
|
+
const secretValue = this.getNodeParameter('secretValue', i);
|
|
556
|
+
const createOptions = this.getNodeParameter('createOptions', i, {});
|
|
557
|
+
const body = {
|
|
558
|
+
workspaceId,
|
|
559
|
+
environment,
|
|
560
|
+
secretName: secretKey,
|
|
561
|
+
secretValue,
|
|
562
|
+
secretPath,
|
|
563
|
+
type: (_a = createOptions.type) !== null && _a !== void 0 ? _a : 'shared',
|
|
564
|
+
};
|
|
565
|
+
if (createOptions.secretComment)
|
|
566
|
+
body.secretComment = createOptions.secretComment;
|
|
567
|
+
if (createOptions.skipMultilineEncoding) {
|
|
568
|
+
body.skipMultilineEncoding = createOptions.skipMultilineEncoding;
|
|
569
|
+
}
|
|
570
|
+
const response = await this.helpers.httpRequest({
|
|
571
|
+
method: 'POST',
|
|
572
|
+
url: `${apiUrl}/v3/secrets/raw/${encodeURIComponent(secretKey)}`,
|
|
573
|
+
headers: baseHeaders,
|
|
574
|
+
body: JSON.stringify(body),
|
|
575
|
+
});
|
|
576
|
+
returnData.push({ json: response, pairedItem: { item: i } });
|
|
577
|
+
// ── update (v4) ───────────────────────────────────────────────────────
|
|
578
|
+
}
|
|
579
|
+
else if (operation === 'update') {
|
|
580
|
+
const secretKey = this.getNodeParameter('secretKey', i);
|
|
581
|
+
const updateOptions = this.getNodeParameter('updateOptions', i, {});
|
|
582
|
+
const body = {
|
|
583
|
+
projectId: workspaceId,
|
|
584
|
+
environment,
|
|
585
|
+
secretPath,
|
|
586
|
+
};
|
|
587
|
+
// Spread only non-empty optional fields
|
|
588
|
+
if (updateOptions.secretValue !== undefined && updateOptions.secretValue !== '') {
|
|
589
|
+
body.secretValue = updateOptions.secretValue;
|
|
590
|
+
}
|
|
591
|
+
if (updateOptions.newSecretName)
|
|
592
|
+
body.newSecretName = updateOptions.newSecretName;
|
|
593
|
+
if (updateOptions.secretComment !== undefined)
|
|
594
|
+
body.secretComment = updateOptions.secretComment;
|
|
595
|
+
if (updateOptions.type)
|
|
596
|
+
body.type = updateOptions.type;
|
|
597
|
+
if (updateOptions.skipMultilineEncoding) {
|
|
598
|
+
body.skipMultilineEncoding = updateOptions.skipMultilineEncoding;
|
|
599
|
+
}
|
|
600
|
+
const response = await this.helpers.httpRequest({
|
|
601
|
+
method: 'PATCH',
|
|
602
|
+
url: `${apiUrl}/v4/secrets/${encodeURIComponent(secretKey)}`,
|
|
603
|
+
headers: baseHeaders,
|
|
604
|
+
body: JSON.stringify(body),
|
|
605
|
+
});
|
|
606
|
+
returnData.push({ json: response, pairedItem: { item: i } });
|
|
607
|
+
// ── delete ────────────────────────────────────────────────────────────
|
|
608
|
+
}
|
|
609
|
+
else if (operation === 'delete') {
|
|
610
|
+
const secretKey = this.getNodeParameter('secretKey', i);
|
|
611
|
+
const response = await this.helpers.httpRequest({
|
|
612
|
+
method: 'DELETE',
|
|
613
|
+
url: `${apiUrl}/v3/secrets/raw/${encodeURIComponent(secretKey)}`,
|
|
614
|
+
headers: baseHeaders,
|
|
615
|
+
qs: { workspaceId, environment, secretPath },
|
|
616
|
+
});
|
|
617
|
+
returnData.push({ json: response, pairedItem: { item: i } });
|
|
618
|
+
// ── createMany (v4 batch POST) ────────────────────────────────────────
|
|
619
|
+
}
|
|
620
|
+
else if (operation === 'createMany') {
|
|
621
|
+
const secretsParam = this.getNodeParameter('secrets', i, {});
|
|
622
|
+
const secretItems = (_b = secretsParam.values) !== null && _b !== void 0 ? _b : [];
|
|
623
|
+
if (secretItems.length === 0) {
|
|
624
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'At least one secret must be added in the Secrets list', { itemIndex: i });
|
|
625
|
+
}
|
|
626
|
+
const createManyOptions = this.getNodeParameter('createManyOptions', i, {});
|
|
627
|
+
const effectivePath = createManyOptions.secretPath || secretPath;
|
|
628
|
+
const secrets = secretItems.map((item) => {
|
|
629
|
+
const s = {
|
|
630
|
+
secretKey: item.secretKey,
|
|
631
|
+
secretValue: item.secretValue,
|
|
632
|
+
};
|
|
633
|
+
if (item.secretComment)
|
|
634
|
+
s.secretComment = item.secretComment;
|
|
635
|
+
if (item.skipMultilineEncoding)
|
|
636
|
+
s.skipMultilineEncoding = item.skipMultilineEncoding;
|
|
637
|
+
return s;
|
|
638
|
+
});
|
|
639
|
+
const body = {
|
|
640
|
+
projectId: workspaceId,
|
|
641
|
+
environment,
|
|
642
|
+
secretPath: effectivePath,
|
|
643
|
+
secrets,
|
|
644
|
+
};
|
|
645
|
+
const response = await this.helpers.httpRequest({
|
|
646
|
+
method: 'POST',
|
|
647
|
+
url: `${apiUrl}/v4/secrets/batch`,
|
|
648
|
+
headers: baseHeaders,
|
|
649
|
+
body: JSON.stringify(body),
|
|
650
|
+
});
|
|
651
|
+
const responseData = response;
|
|
652
|
+
if (Array.isArray(responseData.secrets)) {
|
|
653
|
+
for (const secret of responseData.secrets) {
|
|
654
|
+
returnData.push({ json: secret, pairedItem: { item: i } });
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
// Approval / policy-gated response
|
|
659
|
+
returnData.push({ json: responseData, pairedItem: { item: i } });
|
|
660
|
+
}
|
|
661
|
+
// ── updateMany (v4 batch PATCH) ───────────────────────────────────────
|
|
662
|
+
}
|
|
663
|
+
else if (operation === 'updateMany') {
|
|
664
|
+
const secretsToUpdateParam = this.getNodeParameter('secretsToUpdate', i, {});
|
|
665
|
+
const secretItems = (_c = secretsToUpdateParam.values) !== null && _c !== void 0 ? _c : [];
|
|
666
|
+
if (secretItems.length === 0) {
|
|
667
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'At least one secret must be added in the Secrets list', { itemIndex: i });
|
|
668
|
+
}
|
|
669
|
+
const updateManyOptions = this.getNodeParameter('updateManyOptions', i, {});
|
|
670
|
+
const effectivePath = updateManyOptions.secretPath || secretPath;
|
|
671
|
+
const secrets = secretItems.map((item) => {
|
|
672
|
+
const s = { secretKey: item.secretKey };
|
|
673
|
+
if (item.secretValue !== undefined && item.secretValue !== '') {
|
|
674
|
+
s.secretValue = item.secretValue;
|
|
675
|
+
}
|
|
676
|
+
if (item.newSecretName)
|
|
677
|
+
s.newSecretName = item.newSecretName;
|
|
678
|
+
if (item.secretComment !== undefined && item.secretComment !== '') {
|
|
679
|
+
s.secretComment = item.secretComment;
|
|
680
|
+
}
|
|
681
|
+
if (item.skipMultilineEncoding)
|
|
682
|
+
s.skipMultilineEncoding = item.skipMultilineEncoding;
|
|
683
|
+
return s;
|
|
684
|
+
});
|
|
685
|
+
const body = {
|
|
686
|
+
projectId: workspaceId,
|
|
687
|
+
environment,
|
|
688
|
+
secretPath: effectivePath,
|
|
689
|
+
secrets,
|
|
690
|
+
};
|
|
691
|
+
if (updateManyOptions.mode)
|
|
692
|
+
body.mode = updateManyOptions.mode;
|
|
693
|
+
const response = await this.helpers.httpRequest({
|
|
694
|
+
method: 'PATCH',
|
|
695
|
+
url: `${apiUrl}/v4/secrets/batch`,
|
|
696
|
+
headers: baseHeaders,
|
|
697
|
+
body: JSON.stringify(body),
|
|
698
|
+
});
|
|
699
|
+
const responseData = response;
|
|
700
|
+
if (Array.isArray(responseData.secrets)) {
|
|
701
|
+
for (const secret of responseData.secrets) {
|
|
702
|
+
returnData.push({ json: secret, pairedItem: { item: i } });
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
else {
|
|
706
|
+
// Approval / policy-gated response
|
|
707
|
+
returnData.push({ json: responseData, pairedItem: { item: i } });
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
// ── Workspace resource ──────────────────────────────────────────────────
|
|
711
|
+
}
|
|
712
|
+
else if (resource === 'workspace') {
|
|
713
|
+
if (operation === 'getAll') {
|
|
714
|
+
const response = await this.helpers.httpRequest({
|
|
715
|
+
method: 'GET',
|
|
716
|
+
url: `${apiUrl}/v1/workspace`,
|
|
717
|
+
headers: baseHeaders,
|
|
718
|
+
});
|
|
719
|
+
const workspaces = response.workspaces;
|
|
720
|
+
for (const workspace of workspaces) {
|
|
721
|
+
returnData.push({ json: workspace, pairedItem: { item: i } });
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
catch (error) {
|
|
727
|
+
const e = (0, n8n_workflow_1.ensureError)(error);
|
|
728
|
+
if (this.continueOnFail()) {
|
|
729
|
+
returnData.push({
|
|
730
|
+
json: { error: e.message },
|
|
731
|
+
pairedItem: { item: i },
|
|
732
|
+
});
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
if (error instanceof n8n_workflow_1.NodeOperationError)
|
|
736
|
+
throw error;
|
|
737
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: e.message }, { itemIndex: i });
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
return [returnData];
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
exports.Infisical = Infisical;
|
|
@@ -0,0 +1,5 @@
|
|
|
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>
|
|
Binary file
|
|
@@ -0,0 +1,5 @@
|
|
|
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>
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tadnt2003/n8n-nodes-infisical",
|
|
3
|
+
"version": "0.2.0",
|
|
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": "TadNT2003",
|
|
16
|
+
"email": "ntdatwows2003@gmail.com"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/TadNT2003/n8n-nodes-infisical.git"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc && gulp build",
|
|
24
|
+
"dev": "tsc --watch",
|
|
25
|
+
"format": "prettier nodes credentials --write",
|
|
26
|
+
"lint": "eslint nodes credentials package.json",
|
|
27
|
+
"lintfix": "eslint nodes credentials package.json --fix",
|
|
28
|
+
"prepublishOnly": "npm run build && npm run lint"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist"
|
|
32
|
+
],
|
|
33
|
+
"n8n": {
|
|
34
|
+
"n8nNodesApiVersion": 1,
|
|
35
|
+
"credentials": [
|
|
36
|
+
"dist/credentials/InfisicalApi.credentials.js"
|
|
37
|
+
],
|
|
38
|
+
"nodes": [
|
|
39
|
+
"dist/nodes/Infisical/Infisical.node.js"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
|
44
|
+
"@typescript-eslint/parser": "^5.45.0",
|
|
45
|
+
"eslint": "^8.29.0",
|
|
46
|
+
"eslint-plugin-n8n-nodes-base": "^1.11.0",
|
|
47
|
+
"gulp": "^4.0.2",
|
|
48
|
+
"n8n-workflow": "^1.120.0",
|
|
49
|
+
"prettier": "^2.7.1",
|
|
50
|
+
"typescript": "~5.3.0"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"n8n-workflow": "*"
|
|
54
|
+
}
|
|
55
|
+
}
|