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 API';
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 operations with ssh-dss support',
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 Test', value: '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
- return new Promise((resolve, reject) => {
40
- const conn = new ssh2_1.Client();
41
- conn.on('ready', () => {
42
- conn.end();
43
- resolve([[{ json: { success: true } }]]);
44
- });
45
- conn.on('error', (err) => {
46
- reject(err);
47
- });
48
- conn.connect({
49
- host: credentials.host,
50
- port: credentials.port || 22,
51
- username: credentials.username,
52
- password: credentials.password,
53
- algorithms: {
54
- serverHostKey: ['ssh-dss', 'ssh-rsa', 'ecdsa-sha2-nistp256'],
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;
@@ -1,15 +1,71 @@
1
- <?xml version="1.0" encoding="utf-8"?>
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 height="800px" width="800px" version="1.1" id="_x32_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
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
- <path class="st0" d="M0,17.067V153.6h512V17.067H0z M110.933,110.925h-51.2v-51.2h51.2V110.925z"/>
12
- <path class="st0" d="M0,324.267h512V187.733H0V324.267z M59.733,230.391h51.2v51.2h-51.2V230.391z"/>
13
- <path class="st0" d="M0,494.933h512V358.4H0V494.933z M59.733,401.058h51.2v51.2h-51.2V401.058z"/>
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>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-sftp-custom",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "n8n node for SFTP with custom algorithms",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/Genevieve-Lee-221/n8n-nodes-sftp-custom",