@yousolution/node-red-contrib-you-ftp-sftp
Version:
A node-red node that support FTP and SFTP file transfer using $() environment variables to control the ftp connection details
363 lines (336 loc) • 12.4 kB
JavaScript
/**
* Copyright 2017 Joe Gaska
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
// --------------------------------------------------------------------------------------------------------------
/*
TODO
Add Get
Add Delete
http://ourcodeworld.com/articles/read/133/how-to-create-a-sftp-client-with-node-js-ssh2-in-electron-framework
*/
// --------------------------------------------------------------------------------------------------------------
// STILL VALIDATING CONNECTIVITY
// -- DID validate it works with list/dir
// const fs = require('fs');
module.exports = function (RED) {
'use strict';
// const sftp = require('ssh2').Client;
const fs = require('fs');
function SFtpNode(n) {
RED.nodes.createNode(this, n);
// var node = this;
// console.log('SFTP - Config - username: ' + n.username);
// console.log('SFTP - Config - hmac: ' + n.hmac);
// console.log('SFTP - Config - cipher: ' + n.cipher);
// console.log('SFTP - Config - ssh-key: ' + n.sshkey);
let keyFile = null;
let keyData = null;
if (n.sshkey) {
keyFile = 'keyFile';
keyData = n.sshkey;
}
if (process.env.SFTP_SSH_KEY_FILE) {
keyFile = process.env.SFTP_SSH_KEY_FILE;
keyFile = require('path').resolve(__dirname, '../../' + keyFile);
console.log('SFTP_SSH_KEY_FILE: ' + keyFile);
try {
keyData = fs.readFileSync(keyFile).toString();
// console.log("[http://wwww.HardingPoint.com PRIVATE KEY] " + keyData);
} catch (e) {
keyData = null;
console.log('SFTP - Read Key File [' + keyFile + '] Exception : ' + e);
}
}
if (keyFile && keyData) {
console.log('SFTP - Using key file');
// console.log('*******************');
// console.log('SFTP - Username: ' + n.username);
// console.log('SFTP - Using privateKey: ' + keyFile + ' Length: ' + keyData.toString().length);
// console.log('*******************');
this.options = {
host: n.host || 'localhost',
port: n.port || 21,
username: n.username,
password: n.password,
privateKey: keyData,
algorithms: {
kex: [
'curve25519-sha256',
'curve25519-sha256@libssh.org',
'ecdh-sha2-nistp256',
'ecdh-sha2-nistp384',
'ecdh-sha2-nistp521',
'diffie-hellman-group-exchange-sha256',
'diffie-hellman-group14-sha256',
'diffie-hellman-group15-sha512',
'diffie-hellman-group16-sha512',
'diffie-hellman-group17-sha512',
'diffie-hellman-group18-sha512',
'diffie-hellman-group-exchange-sha1',
'diffie-hellman-group14-sha1',
'diffie-hellman-group1-sha1',
],
cipher: [
'chacha20-poly1305@openssh.com',
'aes128-gcm',
'aes128-gcm@openssh.com',
'aes256-gcm',
'aes256-gcm@openssh.com',
'aes128-ctr',
'aes192-ctr',
'aes256-ctr',
'3des-cbc',
'aes256-cbc',
'aes192-cbc',
'aes128-cbc',
// 'arcfour256',
// 'arcfour128',
// 'arcfour',
// 'blowfish-cbc',
// 'ast128-cbc',
],
serverHostKey: [
'ssh-ed25519',
'ecdsa-sha2-nistp256',
'ecdsa-sha2-nistp384',
'ecdsa-sha2-nistp521',
'rsa-sha2-512',
'rsa-sha2-256',
'ssh-rsa',
'ssh-dss',
],
hmac: [
'hmac-sha2-256-etm@openssh.com',
'hmac-sha2-512-etm@openssh.com',
'hmac-sha1-etm@openssh.com',
'hmac-sha2-256',
'hmac-sha2-512',
'hmac-sha1',
'hmac-md5',
'hmac-sha2-256-96',
'hmac-sha2-512-96',
// 'hmac-ripemd160',
// 'hmac-sha1-96',
// 'hmac-md5-96',
],
},
};
} else {
// console.log('*******************');
console.log('SFTP - Using User/Pwd');
// console.log('SFTP - Username: ' + n.username);
// console.log('SFTP - Password: ' + n.password);
// console.log('SFTP - Using privateKey: ' + keyFile + ' Length: ' + keyData?.toString().length);
// console.log('*******************');
this.options = {
host: n.host || 'localhost',
port: n.port || 21,
username: n.username,
password: n.password,
algorithms: {
kex: [
'curve25519-sha256',
'curve25519-sha256@libssh.org',
'ecdh-sha2-nistp256',
'ecdh-sha2-nistp384',
'ecdh-sha2-nistp521',
'diffie-hellman-group-exchange-sha256',
'diffie-hellman-group14-sha256',
'diffie-hellman-group15-sha512',
'diffie-hellman-group16-sha512',
'diffie-hellman-group17-sha512',
'diffie-hellman-group18-sha512',
'diffie-hellman-group-exchange-sha1',
'diffie-hellman-group14-sha1',
'diffie-hellman-group1-sha1',
],
cipher: [
'chacha20-poly1305@openssh.com',
'aes128-gcm',
'aes128-gcm@openssh.com',
'aes256-gcm',
'aes256-gcm@openssh.com',
'aes128-ctr',
'aes192-ctr',
'aes256-ctr',
'3des-cbc',
'aes256-cbc',
'aes192-cbc',
'aes128-cbc',
// 'arcfour256',
// 'arcfour128',
// 'arcfour',
// 'blowfish-cbc',
// 'ast128-cbc',
],
serverHostKey: [
'ssh-ed25519',
'ecdsa-sha2-nistp256',
'ecdsa-sha2-nistp384',
'ecdsa-sha2-nistp521',
'rsa-sha2-512',
'rsa-sha2-256',
'ssh-rsa',
'ssh-dss',
],
hmac: [
'hmac-sha2-256-etm@openssh.com',
'hmac-sha2-512-etm@openssh.com',
'hmac-sha1-etm@openssh.com',
'hmac-sha2-256',
'hmac-sha2-512',
'hmac-sha1',
'hmac-md5',
'hmac-sha2-256-96',
'hmac-sha2-512-96',
// 'hmac-ripemd160',
// 'hmac-sha1-96',
// 'hmac-md5-96',
],
},
};
}
}
RED.nodes.registerType('sftp', SFtpNode);
function SFtpInNode(n) {
RED.nodes.createNode(this, n);
this.sftp = n.sftp;
this.operation = n.operation;
this.filename = n.filename;
this.fileExtension = n.fileExtension;
this.workdir = n.workdir;
this.sftpConfig = RED.nodes.getNode(this.sftp);
if (this.sftpConfig) {
const node = this;
node.on('input', async function (msg, send, done) {
try {
node.workdir = node.workdir || msg.workdir || './';
node.fileExtension = node.fileExtension || msg.fileExtension || '';
/*SFTP options*/
node.sftpConfig.options.host = msg.host || node.sftpConfig.options.host;
node.sftpConfig.options.port = msg.port || node.sftpConfig.options.port;
node.sftpConfig.options.username = msg.user || node.sftpConfig.options.username || '';
node.sftpConfig.options.password = msg.password || node.sftpConfig.options.password || '';
// console.log('========');
// console.log(node.sftpConfig.options.privateKey);
// console.log('========');
// let conn = new sftp();
// conn.on('ready', async function () {
const Client = require('ssh2-sftp-client');
const client = new Client();
try {
node.status({ fill: 'gray', shape: 'ring', text: 'connection...' });
await client.connect(node.sftpConfig.options);
node.status({ fill: 'green', shape: 'ring', text: 'connected' });
} catch (err) {
node.status({ fill: 'red', shape: 'ring', text: 'connection failed.' });
done(err);
console.error(err.message);
return;
}
switch (node.operation) {
case 'list':
try {
const result = await client.list(node.workdir);
node.status({ fill: 'green', shape: 'dot', text: 'list done' });
await client.end();
msg.payload = {};
msg.payload = result;
node.send(msg);
} catch (err) {
node.status({ fill: 'red', shape: 'ring', text: 'list failed' });
done(err);
console.error(err.message);
}
break;
case 'get':
try {
let ftpfilename = node.workdir + node.filename;
if (msg.payload.filename) ftpfilename = msg.payload.filename;
const result = await client.get(ftpfilename);
await client.end();
node.status({ fill: 'green', shape: 'dot', text: 'get done' });
// conn.end();
// console.log('SFTP Read Chunks ' + counter + ' Length: ' + buf.length);
msg.payload = {};
msg.payload.filedata = result;
msg.payload.filename = ftpfilename;
node.send(msg);
} catch (err) {
node.status({ fill: 'red', shape: 'ring', text: 'get failed' });
done(err);
console.error(err.message);
}
break;
case 'put':
let newFile = '';
if (msg.payload.filename) {
newFile = msg.payload.filename;
} else if (node.filename == '') {
let d = new Date();
let guid = d.getTime().toString();
if (node.fileExtension == '') node.fileExtension = '.txt';
newFile = node.workdir + guid + node.fileExtension;
} else {
newFile = node.workdir + node.filename;
}
let msgData = '';
msgData = msg.payload.filedata ? msg.payload.filedata : JSON.stringify(msg.payload);
// console.log(newFile);
node.status({});
try {
await client.put(msgData, newFile);
node.status({ fill: 'green', shape: 'dot', text: 'put done' });
await client.end();
msg.payload = {};
msg.payload.filename = newFile;
node.send(msg);
} catch (err) {
node.status({ fill: 'red', shape: 'ring', text: 'put failed' });
done(err);
console.error(err.message);
}
break;
case 'delete':
try {
let ftpfilename = node.workdir + node.filename;
if (msg.payload.filename) ftpfilename = msg.payload.filename;
console.log('SFTP Deleting File: ' + ftpfilename);
await client.delete(ftpfilename);
node.status({ fill: 'green', shape: 'dot', text: 'delete done' });
await client.end();
msg.payload = {};
msg.payload.filename = ftpfilename;
node.send(msg);
} catch (err) {
node.status({ fill: 'red', shape: 'ring', text: 'delete failed' });
done(err);
console.error(err.message);
}
break;
}
} catch (error) {
// node.error(error, msg);
done(error);
return;
}
});
} else {
this.error('missing sftp configuration');
done(this.error);
}
}
RED.nodes.registerType('sftp in', SFtpInNode);
};