n8n-nodes-sftp-custom 0.1.0 → 0.1.2
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.
|
@@ -4,7 +4,7 @@ exports.SftpApi = void 0;
|
|
|
4
4
|
class SftpApi {
|
|
5
5
|
constructor() {
|
|
6
6
|
this.name = 'sftpApi';
|
|
7
|
-
this.displayName = 'SFTP
|
|
7
|
+
this.displayName = 'SFTP Account';
|
|
8
8
|
this.properties = [
|
|
9
9
|
{
|
|
10
10
|
displayName: 'Host',
|
|
@@ -31,6 +31,14 @@ class SftpApi {
|
|
|
31
31
|
typeOptions: { password: true },
|
|
32
32
|
default: '',
|
|
33
33
|
},
|
|
34
|
+
{
|
|
35
|
+
displayName: 'Host Key Algorithms',
|
|
36
|
+
name: 'algorithms',
|
|
37
|
+
type: 'string',
|
|
38
|
+
default: 'ssh-dss,ssh-rsa,rsa-sha2-256,ecdsa-sha2-nistp256',
|
|
39
|
+
description: 'Comma-separated list of host key algorithms (e.g., ssh-dss,ssh-rsa)',
|
|
40
|
+
placeholder: 'ssh-dss,ssh-rsa,ecdsa-sha2-nistp256',
|
|
41
|
+
},
|
|
34
42
|
];
|
|
35
43
|
}
|
|
36
44
|
}
|
|
@@ -11,7 +11,7 @@ class Sftp {
|
|
|
11
11
|
icon: 'file:sftp.svg',
|
|
12
12
|
group: ['transform'],
|
|
13
13
|
version: 1,
|
|
14
|
-
description: 'SFTP
|
|
14
|
+
description: 'n8n node for SFTP with custom algorithms',
|
|
15
15
|
defaults: { name: 'SFTP' },
|
|
16
16
|
inputs: ['main'],
|
|
17
17
|
outputs: ['main'],
|
|
@@ -27,34 +27,213 @@ class Sftp {
|
|
|
27
27
|
name: 'operation',
|
|
28
28
|
type: 'options',
|
|
29
29
|
options: [
|
|
30
|
-
{ name: 'Connect
|
|
30
|
+
{ name: 'Connect', value: 'connect', description: 'Test Connection' },
|
|
31
|
+
{ name: 'Upload', value: 'upload', description: 'Upload A File' },
|
|
32
|
+
{ name: 'Download', value: 'download', description: 'Download A File' },
|
|
31
33
|
],
|
|
32
34
|
default: 'connect',
|
|
33
35
|
},
|
|
36
|
+
{
|
|
37
|
+
displayName: 'Input Data Field Name',
|
|
38
|
+
name: 'binaryPropertyName',
|
|
39
|
+
type: 'string',
|
|
40
|
+
default: 'data',
|
|
41
|
+
required: true,
|
|
42
|
+
displayOptions: {
|
|
43
|
+
show: {
|
|
44
|
+
operation: ['upload'],
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
description: 'Name Of The Binary Property Containing The File',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
displayName: 'Remote Folder Path',
|
|
51
|
+
name: 'remotePath',
|
|
52
|
+
type: 'string',
|
|
53
|
+
default: '/upload/',
|
|
54
|
+
required: true,
|
|
55
|
+
displayOptions: {
|
|
56
|
+
show: {
|
|
57
|
+
operation: ['upload'],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
description: 'Remote Folder Path Where To Upload The File',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
displayName: 'Remote File Path',
|
|
64
|
+
name: 'remoteFilePath',
|
|
65
|
+
type: 'string',
|
|
66
|
+
default: '',
|
|
67
|
+
required: true,
|
|
68
|
+
displayOptions: {
|
|
69
|
+
show: {
|
|
70
|
+
operation: ['download'],
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
description: 'Full Path Of The File To Download (e.g., /path/file.txt)',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
displayName: 'Output Property Name',
|
|
77
|
+
name: 'binaryPropertyOutput',
|
|
78
|
+
type: 'string',
|
|
79
|
+
default: 'data',
|
|
80
|
+
required: true,
|
|
81
|
+
displayOptions: {
|
|
82
|
+
show: {
|
|
83
|
+
operation: ['download'],
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
description: 'Name For The Binary Property To Store The Downloaded File',
|
|
87
|
+
},
|
|
34
88
|
],
|
|
35
89
|
};
|
|
36
90
|
}
|
|
37
91
|
async execute() {
|
|
38
92
|
const credentials = await this.getCredentials('sftpApi');
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
93
|
+
const operation = this.getNodeParameter('operation', 0);
|
|
94
|
+
switch (operation) {
|
|
95
|
+
case 'connect':
|
|
96
|
+
// Test the connection
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
const conn = new ssh2_1.Client();
|
|
99
|
+
conn.on('ready', () => {
|
|
100
|
+
conn.end();
|
|
101
|
+
resolve([[{ json: { success: true } }]]);
|
|
102
|
+
});
|
|
103
|
+
conn.on('error', (err) => {
|
|
104
|
+
reject(err);
|
|
105
|
+
});
|
|
106
|
+
conn.connect({
|
|
107
|
+
host: credentials.host,
|
|
108
|
+
port: credentials.port || 22,
|
|
109
|
+
username: credentials.username,
|
|
110
|
+
password: credentials.password,
|
|
111
|
+
algorithms: {
|
|
112
|
+
// serverHostKey: ['ssh-dss', 'ssh-rsa', 'ecdsa-sha2-nistp256'],
|
|
113
|
+
serverHostKey: credentials.algorithms.split(',').map(x => x.trim()).filter(x => x),
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
case 'upload':
|
|
118
|
+
// Upload the file
|
|
119
|
+
const items = this.getInputData();
|
|
120
|
+
const returnData = [];
|
|
121
|
+
for (let i = 0; i < items.length; i++) {
|
|
122
|
+
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i);
|
|
123
|
+
const remotePath = this.getNodeParameter('remotePath', i);
|
|
124
|
+
const item = items[i];
|
|
125
|
+
if (!item.binary || !item.binary[binaryPropertyName]) {
|
|
126
|
+
throw new Error(`No binary data property "${binaryPropertyName}" found`);
|
|
127
|
+
}
|
|
128
|
+
const binaryData = item.binary[binaryPropertyName];
|
|
129
|
+
const fileBuffer = Buffer.from(binaryData.data, 'base64');
|
|
130
|
+
await new Promise((resolve, reject) => {
|
|
131
|
+
const conn = new ssh2_1.Client();
|
|
132
|
+
conn.on('ready', () => {
|
|
133
|
+
conn.sftp((err, sftp) => {
|
|
134
|
+
if (err) {
|
|
135
|
+
conn.end();
|
|
136
|
+
return reject(err);
|
|
137
|
+
}
|
|
138
|
+
const writeStream = sftp.createWriteStream(remotePath + binaryData.fileName);
|
|
139
|
+
writeStream.on('close', () => {
|
|
140
|
+
conn.end();
|
|
141
|
+
resolve(true);
|
|
142
|
+
});
|
|
143
|
+
writeStream.on('error', (err) => {
|
|
144
|
+
conn.end();
|
|
145
|
+
reject(err);
|
|
146
|
+
});
|
|
147
|
+
writeStream.write(fileBuffer);
|
|
148
|
+
writeStream.end();
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
conn.on('error', (err) => {
|
|
152
|
+
reject(err);
|
|
153
|
+
});
|
|
154
|
+
conn.connect({
|
|
155
|
+
host: credentials.host,
|
|
156
|
+
port: credentials.port || 22,
|
|
157
|
+
username: credentials.username,
|
|
158
|
+
password: credentials.password,
|
|
159
|
+
algorithms: {
|
|
160
|
+
serverHostKey: credentials.algorithms.split(',').map(x => x.trim()).filter(x => x),
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
returnData.push({
|
|
165
|
+
json: {
|
|
166
|
+
success: true,
|
|
167
|
+
fileName: binaryData.fileName,
|
|
168
|
+
remotePath: remotePath + binaryData.fileName,
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
return [returnData];
|
|
172
|
+
}
|
|
173
|
+
break;
|
|
174
|
+
case 'download':
|
|
175
|
+
const download_items = this.getInputData();
|
|
176
|
+
const download_returnData = [];
|
|
177
|
+
for (let i = 0; i < download_items.length; i++) {
|
|
178
|
+
const remoteFilePath = this.getNodeParameter('remoteFilePath', i);
|
|
179
|
+
const binaryPropertyOutput = this.getNodeParameter('binaryPropertyOutput', i);
|
|
180
|
+
const fileData = await new Promise((resolve, reject) => {
|
|
181
|
+
const conn = new ssh2_1.Client();
|
|
182
|
+
conn.on('ready', () => {
|
|
183
|
+
conn.sftp((err, sftp) => {
|
|
184
|
+
if (err) {
|
|
185
|
+
conn.end();
|
|
186
|
+
return reject(err);
|
|
187
|
+
}
|
|
188
|
+
const chunks = [];
|
|
189
|
+
const readStream = sftp.createReadStream(remoteFilePath);
|
|
190
|
+
readStream.on('data', (chunk) => {
|
|
191
|
+
chunks.push(chunk);
|
|
192
|
+
});
|
|
193
|
+
readStream.on('end', () => {
|
|
194
|
+
conn.end();
|
|
195
|
+
resolve(Buffer.concat(chunks));
|
|
196
|
+
});
|
|
197
|
+
readStream.on('error', (err) => {
|
|
198
|
+
conn.end();
|
|
199
|
+
reject(err);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
conn.on('error', (err) => {
|
|
204
|
+
reject(err);
|
|
205
|
+
});
|
|
206
|
+
conn.connect({
|
|
207
|
+
host: credentials.host,
|
|
208
|
+
port: credentials.port || 22,
|
|
209
|
+
username: credentials.username,
|
|
210
|
+
password: credentials.password,
|
|
211
|
+
algorithms: {
|
|
212
|
+
serverHostKey: credentials.algorithms
|
|
213
|
+
.split(',')
|
|
214
|
+
.map(a => a.trim())
|
|
215
|
+
.filter(a => a),
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
const fileName = remoteFilePath.split('/').pop() || 'downloaded-file';
|
|
220
|
+
download_returnData.push({
|
|
221
|
+
json: {
|
|
222
|
+
fileName,
|
|
223
|
+
fileSize: fileData.length,
|
|
224
|
+
},
|
|
225
|
+
binary: {
|
|
226
|
+
[binaryPropertyOutput]: {
|
|
227
|
+
data: fileData.toString('base64'),
|
|
228
|
+
fileName,
|
|
229
|
+
mimeType: 'application/octet-stream',
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
return [download_returnData];
|
|
235
|
+
}
|
|
236
|
+
throw new Error(`Unknown Operation: ${operation}`);
|
|
58
237
|
}
|
|
59
238
|
}
|
|
60
239
|
exports.Sftp = Sftp;
|
package/dist/nodes/Sftp/sftp.svg
CHANGED
|
@@ -1,15 +1,71 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="
|
|
1
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
|
2
2
|
|
|
3
3
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
4
4
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
|
5
|
-
<svg
|
|
5
|
+
<svg version="1.1" id="_x35_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
6
6
|
viewBox="0 0 512 512" xml:space="preserve">
|
|
7
|
-
<style type="text/css">
|
|
8
|
-
.st0{fill:#000000;}
|
|
9
|
-
</style>
|
|
10
7
|
<g>
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
<g>
|
|
9
|
+
<path style="fill:#C191C0;" d="M148.413,10.054v387.402c0,5.519-4.536,10.056-10.056,10.056H10.056
|
|
10
|
+
C4.537,407.512,0,402.975,0,397.456V10.054C0,4.535,4.537,0,10.056,0h128.302C143.877,0,148.413,4.535,148.413,10.054z"/>
|
|
11
|
+
<path style="fill:#C191C0;" d="M96.848,244.003H51.57c-16.602,0-30.185-13.583-30.185-30.185V77.983
|
|
12
|
+
c0-16.602,13.583-30.185,30.185-30.185h45.278c16.602,0,30.185,13.583,30.185,30.185v135.834
|
|
13
|
+
C127.034,230.419,113.45,244.003,96.848,244.003z"/>
|
|
14
|
+
<path style="fill:#FFFFFF;" d="M112.57,244.003H35.849c-7.955,0-14.464-6.509-14.464-14.464V62.262
|
|
15
|
+
c0-7.955,6.509-14.464,14.464-14.464h76.721c7.955,0,14.464,6.509,14.464,14.464v167.277
|
|
16
|
+
C127.034,237.494,120.525,244.003,112.57,244.003z"/>
|
|
17
|
+
<g>
|
|
18
|
+
<path style="opacity:0.2;fill:#171716;" d="M74.209,384.194c-21.486,0-38.967-17.481-38.967-38.967
|
|
19
|
+
c0-21.486,17.481-38.967,38.967-38.967s38.967,17.481,38.967,38.967C113.176,366.714,95.696,384.194,74.209,384.194z"/>
|
|
20
|
+
<circle style="fill:#FFFFFF;" cx="74.209" cy="345.227" r="27.277"/>
|
|
21
|
+
</g>
|
|
22
|
+
<rect x="40.251" y="80.499" style="fill:#C191C0;" width="67.917" height="16.35"/>
|
|
23
|
+
<rect x="40.251" y="129.55" style="fill:#C191C0;" width="67.917" height="16.35"/>
|
|
24
|
+
<rect x="40.251" y="178.601" style="fill:#C191C0;" width="67.917" height="16.35"/>
|
|
25
|
+
<rect y="268.021" style="fill:#FFFFFF;" width="148.413" height="14.818"/>
|
|
26
|
+
<path style="fill:#ED7B84;" d="M330.169,10.054v387.402c0,5.519-4.461,10.056-10.055,10.056H191.886
|
|
27
|
+
c-5.595,0-10.056-4.537-10.056-10.056V10.054C181.831,4.535,186.292,0,191.886,0h128.227C325.708,0,330.169,4.535,330.169,10.054z
|
|
28
|
+
"/>
|
|
29
|
+
<path style="fill:#ED7B84;" d="M278.639,244.003h-45.278c-16.602,0-30.185-13.583-30.185-30.185V77.983
|
|
30
|
+
c0-16.602,13.583-30.185,30.185-30.185h45.278c16.602,0,30.185,13.583,30.185,30.185v135.834
|
|
31
|
+
C308.824,230.419,295.241,244.003,278.639,244.003z"/>
|
|
32
|
+
<path style="fill:#FFFFFF;" d="M294.36,244.003h-76.721c-7.955,0-14.464-6.509-14.464-14.464V62.262
|
|
33
|
+
c0-7.955,6.509-14.464,14.464-14.464h76.721c7.955,0,14.464,6.509,14.464,14.464v167.277
|
|
34
|
+
C308.824,237.494,302.315,244.003,294.36,244.003z"/>
|
|
35
|
+
<g>
|
|
36
|
+
<path style="opacity:0.2;fill:#171716;" d="M256,384.194c-21.486,0-38.967-17.481-38.967-38.967
|
|
37
|
+
c0-21.486,17.481-38.967,38.967-38.967c21.486,0,38.967,17.481,38.967,38.967C294.967,366.714,277.486,384.194,256,384.194z"/>
|
|
38
|
+
<circle style="fill:#FFFFFF;" cx="256" cy="345.227" r="27.277"/>
|
|
39
|
+
</g>
|
|
40
|
+
<rect x="222.041" y="80.499" style="fill:#ED7B84;" width="67.917" height="16.35"/>
|
|
41
|
+
<rect x="222.041" y="129.55" style="fill:#ED7B84;" width="67.917" height="16.35"/>
|
|
42
|
+
<rect x="222.041" y="178.601" style="fill:#ED7B84;" width="67.917" height="16.35"/>
|
|
43
|
+
<rect x="181.794" y="268.018" style="fill:#FFFFFF;" width="148.412" height="14.857"/>
|
|
44
|
+
<path style="fill:#77C7C1;" d="M512,10.054v387.402c0,5.519-4.537,10.056-10.056,10.056H373.642
|
|
45
|
+
c-5.519,0-10.055-4.537-10.055-10.056V10.054C363.587,4.535,368.123,0,373.642,0h128.303C507.463,0,512,4.535,512,10.054z"/>
|
|
46
|
+
<path style="fill:#77C7C1;" d="M460.429,244.003h-45.278c-16.602,0-30.185-13.583-30.185-30.185V77.983
|
|
47
|
+
c0-16.602,13.583-30.185,30.185-30.185h45.278c16.602,0,30.185,13.583,30.185,30.185v135.834
|
|
48
|
+
C490.615,230.419,477.031,244.003,460.429,244.003z"/>
|
|
49
|
+
<path style="fill:#FFFFFF;" d="M476.151,244.003H399.43c-7.955,0-14.464-6.509-14.464-14.464V62.262
|
|
50
|
+
c0-7.955,6.509-14.464,14.464-14.464h76.721c7.955,0,14.464,6.509,14.464,14.464v167.277
|
|
51
|
+
C490.615,237.494,484.106,244.003,476.151,244.003z"/>
|
|
52
|
+
<g>
|
|
53
|
+
<path style="opacity:0.2;fill:#171716;" d="M437.79,384.194c-21.486,0-38.967-17.481-38.967-38.967
|
|
54
|
+
c0-21.486,17.481-38.967,38.967-38.967c21.486,0,38.967,17.481,38.967,38.967C476.757,366.714,459.276,384.194,437.79,384.194z"
|
|
55
|
+
/>
|
|
56
|
+
<circle style="fill:#FFFFFF;" cx="437.79" cy="345.227" r="27.277"/>
|
|
57
|
+
</g>
|
|
58
|
+
<rect x="403.832" y="80.499" style="fill:#77C7C1;" width="67.917" height="16.35"/>
|
|
59
|
+
<rect x="403.832" y="129.55" style="fill:#77C7C1;" width="67.917" height="16.35"/>
|
|
60
|
+
<rect x="403.832" y="178.601" style="fill:#77C7C1;" width="67.917" height="16.35"/>
|
|
61
|
+
<rect x="363.584" y="268.018" style="fill:#FFFFFF;" width="148.412" height="14.857"/>
|
|
62
|
+
</g>
|
|
63
|
+
<g style="opacity:0.08;">
|
|
64
|
+
<polygon style="fill:#040000;" points="148.415,407.507 148.415,311.341 52.248,407.507 "/>
|
|
65
|
+
<polygon style="fill:#040000;" points="181.794,282.874 181.794,407.507 330.206,407.507 330.206,282.874 330.206,268.017
|
|
66
|
+
330.206,129.55 181.794,277.961 "/>
|
|
67
|
+
<polygon style="fill:#040000;" points="459.751,0.004 363.584,96.172 363.584,268.017 363.584,282.874 363.584,407.507
|
|
68
|
+
511.996,407.507 511.996,282.874 511.996,268.017 511.996,0.004 "/>
|
|
69
|
+
</g>
|
|
14
70
|
</g>
|
|
15
71
|
</svg>
|