chrisdrakeford 2 years ago
parent
commit
c21337f41c

+ 4 - 0
.env

@@ -0,0 +1,4 @@
+SSH_USER=cdrakeford
+SSH_PASSWORD=1e157b9e83
+BOOLE=boole.tchpc.tcd.ie
+GROVE=grove2.computing.dcu.ie

+ 2 - 1
dockerfile

@@ -1,6 +1,7 @@
-FROM node:9-slim
+FROM node:latest
 WORKDIR /heliosapi
 COPY package.json /heliosapi/
 RUN npm install
 COPY . /heliosapi/
+EXPOSE 443
 CMD ["npm","start"]

+ 17 - 20
index.js

@@ -1,9 +1,12 @@
 const express = require("express");
-const fs = require('fs');
+const fs = require("fs");
 const fileUpload = require("express-fileupload");
+const ssh = require("./ssh");
 
 const app = express();
 
+let sshClient;
+
 // Necessary to have access to the file in the req object
 app.use(
   fileUpload({
@@ -11,31 +14,25 @@ app.use(
   })
 );
 
-app.get("/",(req,res) => {
-//console.log(conn1);
+app.post("/api", async function (req, res) {
+  if (!req.files) {
+    res.send("File was not found");
+    return;
+  }
 
+  fs.writeFile("/Users/chrisdrakeford/Apitest/" + req.files.file1234.name,req.files.file1234.data,function(err){
+    if(err) throw err;
+    console.log("receieved");
+ 
 });
 
-app.post("/upload", function(req, res) {
-  const ssh = require("./ssh")
-  console.log(req.files);
-
-  if(!req.files)
-    {
-      res.send("File was not found");
-      return;
-    }
+  await sshClient.uploadFile(req.files.file1234.name,"/home/cdrakeford/apitest/");
 
-fs.writeFile("/home/cdrakeford/apitest/" + req.files.file1234.name ,req.files.file1234.data,function(err){
-    if(err) throw err;
-    console.log("receieved");
 
-});
-  // Send the file to the other server
-  // scp send file
   res.status(200).end();
 });
 
-app.listen(443, () => {
+app.listen(443, async () => {
   console.log("App listening on 443");
-});
+  sshClient = await ssh.start();
+});

+ 16 - 0
node_modules/.package-lock.json

@@ -536,6 +536,14 @@
         "node": ">=8"
       }
     },
+    "node_modules/dotenv": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
+      "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/duplexer3": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
@@ -1181,6 +1189,14 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/node-scp": {
+      "version": "0.0.15",
+      "resolved": "https://registry.npmjs.org/node-scp/-/node-scp-0.0.15.tgz",
+      "integrity": "sha512-iQQpzVqsczdM35tI5JzFkM3DFMJb2VIeHGBVXU9d5EIgSrP4HGE44SScGv83D5aEtYCaY2r5uR1bPhUQwp6hRg==",
+      "dependencies": {
+        "ssh2": "^1.1.0"
+      }
+    },
     "node_modules/nodemon": {
       "version": "2.0.12",
       "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.12.tgz",

+ 210 - 0
node_modules/dotenv/CHANGELOG.md

@@ -0,0 +1,210 @@
+# Changelog
+
+All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+## [10.0.0](https://github.com/motdotla/dotenv/compare/v9.0.2...v10.0.0) (2021-05-20)
+
+### Added
+
+- Add generic support to parse function
+- Allow for import "dotenv/config.js"
+- Add support to resolve home directory in path via ~
+
+## [9.0.2](https://github.com/motdotla/dotenv/compare/v9.0.1...v9.0.2) (2021-05-10)
+
+### Changed
+
+- Support windows newlines with debug mode
+
+## [9.0.1](https://github.com/motdotla/dotenv/compare/v9.0.0...v9.0.1) (2021-05-08)
+
+### Changed
+
+- Updates to README
+
+## [9.0.0](https://github.com/motdotla/dotenv/compare/v8.6.0...v9.0.0) (2021-05-05)
+
+### Changed
+
+- _Breaking:_ drop support for Node v8
+
+## [8.6.0](https://github.com/motdotla/dotenv/compare/v8.5.1...v8.6.0) (2021-05-05)
+
+### Added
+
+- define package.json in exports
+
+## [8.5.1](https://github.com/motdotla/dotenv/compare/v8.5.0...v8.5.1) (2021-05-05)
+
+### Changed
+
+- updated dev dependencies via npm audit
+
+## [8.5.0](https://github.com/motdotla/dotenv/compare/v8.4.0...v8.5.0) (2021-05-05)
+
+### Added
+
+- allow for `import "dotenv/config"`
+
+## [8.4.0](https://github.com/motdotla/dotenv/compare/v8.3.0...v8.4.0) (2021-05-05)
+
+### Changed
+
+- point to exact types file to work with VS Code
+
+## [8.3.0](https://github.com/motdotla/dotenv/compare/v8.2.0...v8.3.0) (2021-05-05)
+
+### Changed
+
+- _Breaking:_ drop support for Node v8 (mistake to be released as minor bump. later bumped to 9.0.0. see above.)
+
+## [8.2.0](https://github.com/motdotla/dotenv/compare/v8.1.0...v8.2.0) (2019-10-16)
+
+### Added
+
+- TypeScript types
+
+## [8.1.0](https://github.com/motdotla/dotenv/compare/v8.0.0...v8.1.0) (2019-08-18)
+
+### Changed
+
+- _Breaking:_ drop support for Node v6 ([#392](https://github.com/motdotla/dotenv/issues/392))
+
+# [8.0.0](https://github.com/motdotla/dotenv/compare/v7.0.0...v8.0.0) (2019-05-02)
+
+### Changed
+
+- _Breaking:_ drop support for Node v6 ([#302](https://github.com/motdotla/dotenv/issues/392))
+
+## [7.0.0] - 2019-03-12
+
+### Fixed
+
+- Fix removing unbalanced quotes ([#376](https://github.com/motdotla/dotenv/pull/376))
+
+### Removed
+
+- Removed `load` alias for `config` for consistency throughout code and documentation.
+
+## [6.2.0] - 2018-12-03
+
+### Added
+
+- Support preload configuration via environment variables ([#351](https://github.com/motdotla/dotenv/issues/351))
+
+## [6.1.0] - 2018-10-08
+
+### Added
+
+- `debug` option for `config` and `parse` methods will turn on logging
+
+## [6.0.0] - 2018-06-02
+
+### Changed
+
+- _Breaking:_ drop support for Node v4 ([#304](https://github.com/motdotla/dotenv/pull/304))
+
+## [5.0.0] - 2018-01-29
+
+### Added
+
+- Testing against Node v8 and v9
+- Documentation on trim behavior of values
+- Documentation on how to use with `import`
+
+### Changed
+
+- _Breaking_: default `path` is now `path.resolve(process.cwd(), '.env')`
+- _Breaking_: does not write over keys already in `process.env` if the key has a falsy value
+- using `const` and `let` instead of `var`
+
+### Removed
+
+- Testing against Node v7
+
+## [4.0.0] - 2016-12-23
+
+### Changed
+
+- Return Object with parsed content or error instead of false ([#165](https://github.com/motdotla/dotenv/pull/165)).
+
+### Removed
+
+- `verbose` option removed in favor of returning result.
+
+## [3.0.0] - 2016-12-20
+
+### Added
+
+- `verbose` option will log any error messages. Off by default.
+- parses email addresses correctly
+- allow importing config method directly in ES6
+
+### Changed
+
+- Suppress error messages by default ([#154](https://github.com/motdotla/dotenv/pull/154))
+- Ignoring more files for NPM to make package download smaller
+
+### Fixed
+
+- False positive test due to case-sensitive variable ([#124](https://github.com/motdotla/dotenv/pull/124))
+
+### Removed
+
+- `silent` option removed in favor of `verbose`
+
+## [2.0.0] - 2016-01-20
+
+### Added
+
+- CHANGELOG to ["make it easier for users and contributors to see precisely what notable changes have been made between each release"](http://keepachangelog.com/). Linked to from README
+- LICENSE to be more explicit about what was defined in `package.json`. Linked to from README
+- Testing nodejs v4 on travis-ci
+- added examples of how to use dotenv in different ways
+- return parsed object on success rather than boolean true
+
+### Changed
+
+- README has shorter description not referencing ruby gem since we don't have or want feature parity
+
+### Removed
+
+- Variable expansion and escaping so environment variables are encouraged to be fully orthogonal
+
+## [1.2.0] - 2015-06-20
+
+### Added
+
+- Preload hook to require dotenv without including it in your code
+
+### Changed
+
+- clarified license to be "BSD-2-Clause" in `package.json`
+
+### Fixed
+
+- retain spaces in string vars
+
+## [1.1.0] - 2015-03-31
+
+### Added
+
+- Silent option to silence `console.log` when `.env` missing
+
+## [1.0.0] - 2015-03-13
+
+### Removed
+
+- support for multiple `.env` files. should always use one `.env` file for the current environment
+
+[7.0.0]: https://github.com/motdotla/dotenv/compare/v6.2.0...v7.0.0
+[6.2.0]: https://github.com/motdotla/dotenv/compare/v6.1.0...v6.2.0
+[6.1.0]: https://github.com/motdotla/dotenv/compare/v6.0.0...v6.1.0
+[6.0.0]: https://github.com/motdotla/dotenv/compare/v5.0.0...v6.0.0
+[5.0.0]: https://github.com/motdotla/dotenv/compare/v4.0.0...v5.0.0
+[4.0.0]: https://github.com/motdotla/dotenv/compare/v3.0.0...v4.0.0
+[3.0.0]: https://github.com/motdotla/dotenv/compare/v2.0.0...v3.0.0
+[2.0.0]: https://github.com/motdotla/dotenv/compare/v1.2.0...v2.0.0
+[1.2.0]: https://github.com/motdotla/dotenv/compare/v1.1.0...v1.2.0
+[1.1.0]: https://github.com/motdotla/dotenv/compare/v1.0.0...v1.1.0
+[1.0.0]: https://github.com/motdotla/dotenv/compare/v0.4.0...v1.0.0

+ 23 - 0
node_modules/dotenv/LICENSE

@@ -0,0 +1,23 @@
+Copyright (c) 2015, Scott Motte
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 275 - 0
node_modules/dotenv/README.md

@@ -0,0 +1,275 @@
+<p align="center">
+<strong>Announcement 📣</strong><br/>From the makers that brought you Dotenv, introducing <a href="https://sync.dotenv.org">Dotenv Sync</a>.<br/>Sync your .env files between machines, environments, and team members.<br/><a href="https://sync.dotenv.org">Join the early access list. 🕶</a>
+</p>
+
+# dotenv
+
+<img src="https://raw.githubusercontent.com/motdotla/dotenv/master/dotenv.png" alt="dotenv" align="right" />
+
+Dotenv is a zero-dependency module that loads environment variables from a `.env` file into [`process.env`](https://nodejs.org/docs/latest/api/process.html#process_process_env). Storing configuration in the environment separate from code is based on [The Twelve-Factor App](http://12factor.net/config) methodology.
+
+[![BuildStatus](https://img.shields.io/travis/motdotla/dotenv/master.svg?style=flat-square)](https://travis-ci.org/motdotla/dotenv)
+[![Build status](https://ci.appveyor.com/api/projects/status/github/motdotla/dotenv?svg=true)](https://ci.appveyor.com/project/motdotla/dotenv/branch/master)
+[![NPM version](https://img.shields.io/npm/v/dotenv.svg?style=flat-square)](https://www.npmjs.com/package/dotenv)
+[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard)
+[![Coverage Status](https://img.shields.io/coveralls/motdotla/dotenv/master.svg?style=flat-square)](https://coveralls.io/github/motdotla/dotenv?branch=coverall-intergration)
+[![LICENSE](https://img.shields.io/github/license/motdotla/dotenv.svg)](LICENSE)
+[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org)
+
+## Install
+
+```bash
+# with npm
+npm install dotenv
+
+# or with Yarn
+yarn add dotenv
+```
+
+## Usage
+
+As early as possible in your application, require and configure dotenv.
+
+```javascript
+require('dotenv').config()
+```
+
+Create a `.env` file in the root directory of your project. Add
+environment-specific variables on new lines in the form of `NAME=VALUE`.
+For example:
+
+```dosini
+DB_HOST=localhost
+DB_USER=root
+DB_PASS=s1mpl3
+```
+
+`process.env` now has the keys and values you defined in your `.env` file.
+
+```javascript
+const db = require('db')
+db.connect({
+  host: process.env.DB_HOST,
+  username: process.env.DB_USER,
+  password: process.env.DB_PASS
+})
+```
+
+### Preload
+
+You can use the `--require` (`-r`) [command line option](https://nodejs.org/api/cli.html#cli_r_require_module) to preload dotenv. By doing this, you do not need to require and load dotenv in your application code. This is the preferred approach when using `import` instead of `require`.
+
+```bash
+$ node -r dotenv/config your_script.js
+```
+
+The configuration options below are supported as command line arguments in the format `dotenv_config_<option>=value`
+
+```bash
+$ node -r dotenv/config your_script.js dotenv_config_path=/custom/path/to/.env
+```
+
+Additionally, you can use environment variables to set configuration options. Command line arguments will precede these.
+
+```bash
+$ DOTENV_CONFIG_<OPTION>=value node -r dotenv/config your_script.js
+```
+
+```bash
+$ DOTENV_CONFIG_ENCODING=latin1 node -r dotenv/config your_script.js dotenv_config_path=/custom/path/to/.env
+```
+
+## Config
+
+`config` will read your `.env` file, parse the contents, assign it to
+[`process.env`](https://nodejs.org/docs/latest/api/process.html#process_process_env),
+and return an Object with a `parsed` key containing the loaded content or an `error` key if it failed.
+
+```js
+const result = dotenv.config()
+
+if (result.error) {
+  throw result.error
+}
+
+console.log(result.parsed)
+```
+
+You can additionally, pass options to `config`.
+
+### Options
+
+#### Path
+
+Default: `path.resolve(process.cwd(), '.env')`
+
+You may specify a custom path if your file containing environment variables is located elsewhere.
+
+```js
+require('dotenv').config({ path: '/custom/path/to/.env' })
+```
+
+#### Encoding
+
+Default: `utf8`
+
+You may specify the encoding of your file containing environment variables.
+
+```js
+require('dotenv').config({ encoding: 'latin1' })
+```
+
+#### Debug
+
+Default: `false`
+
+You may turn on logging to help debug why certain keys or values are not being set as you expect.
+
+```js
+require('dotenv').config({ debug: process.env.DEBUG })
+```
+
+## Parse
+
+The engine which parses the contents of your file containing environment
+variables is available to use. It accepts a String or Buffer and will return
+an Object with the parsed keys and values.
+
+```js
+const dotenv = require('dotenv')
+const buf = Buffer.from('BASIC=basic')
+const config = dotenv.parse(buf) // will return an object
+console.log(typeof config, config) // object { BASIC : 'basic' }
+```
+
+### Options
+
+#### Debug
+
+Default: `false`
+
+You may turn on logging to help debug why certain keys or values are not being set as you expect.
+
+```js
+const dotenv = require('dotenv')
+const buf = Buffer.from('hello world')
+const opt = { debug: true }
+const config = dotenv.parse(buf, opt)
+// expect a debug message because the buffer is not in KEY=VAL form
+```
+
+### Rules
+
+The parsing engine currently supports the following rules:
+
+- `BASIC=basic` becomes `{BASIC: 'basic'}`
+- empty lines are skipped
+- lines beginning with `#` are treated as comments
+- empty values become empty strings (`EMPTY=` becomes `{EMPTY: ''}`)
+- inner quotes are maintained (think JSON) (`JSON={"foo": "bar"}` becomes `{JSON:"{\"foo\": \"bar\"}"`)
+- whitespace is removed from both ends of unquoted values (see more on [`trim`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)) (`FOO=  some value  ` becomes `{FOO: 'some value'}`)
+- single and double quoted values are escaped (`SINGLE_QUOTE='quoted'` becomes `{SINGLE_QUOTE: "quoted"}`)
+- single and double quoted values maintain whitespace from both ends (`FOO="  some value  "` becomes `{FOO: '  some value  '}`)
+- double quoted values expand new lines (`MULTILINE="new\nline"` becomes
+
+```
+{MULTILINE: 'new
+line'}
+```
+
+## FAQ
+
+### Should I commit my `.env` file?
+
+No. We **strongly** recommend against committing your `.env` file to version
+control. It should only include environment-specific values such as database
+passwords or API keys. Your production database should have a different
+password than your development database.
+
+### Should I have multiple `.env` files?
+
+No. We **strongly** recommend against having a "main" `.env` file and an "environment" `.env` file like `.env.test`. Your config should vary between deploys, and you should not be sharing values between environments.
+
+> In a twelve-factor app, env vars are granular controls, each fully orthogonal to other env vars. They are never grouped together as “environments”, but instead are independently managed for each deploy. This is a model that scales up smoothly as the app naturally expands into more deploys over its lifetime.
+>
+> – [The Twelve-Factor App](http://12factor.net/config)
+
+### What happens to environment variables that were already set?
+
+We will never modify any environment variables that have already been set. In particular, if there is a variable in your `.env` file which collides with one that already exists in your environment, then that variable will be skipped. This behavior allows you to override all `.env` configurations with a machine-specific environment, although it is not recommended.
+
+If you want to override `process.env` you can do something like this:
+
+```javascript
+const fs = require('fs')
+const dotenv = require('dotenv')
+const envConfig = dotenv.parse(fs.readFileSync('.env.override'))
+for (const k in envConfig) {
+  process.env[k] = envConfig[k]
+}
+```
+
+### Can I customize/write plugins for dotenv?
+
+For `dotenv@2.x.x`: Yes. `dotenv.config()` now returns an object representing
+the parsed `.env` file. This gives you everything you need to continue
+setting values on `process.env`. For example:
+
+```js
+const dotenv = require('dotenv')
+const variableExpansion = require('dotenv-expand')
+const myEnv = dotenv.config()
+variableExpansion(myEnv)
+```
+
+### What about variable expansion?
+
+Try [dotenv-expand](https://github.com/motdotla/dotenv-expand)
+
+### How do I use dotenv with `import`?
+
+ES2015 and beyond offers modules that allow you to `export` any top-level `function`, `class`, `var`, `let`, or `const`.
+
+> When you run a module containing an `import` declaration, the modules it imports are loaded first, then each module body is executed in a depth-first traversal of the dependency graph, avoiding cycles by skipping anything already executed.
+>
+> – [ES6 In Depth: Modules](https://hacks.mozilla.org/2015/08/es6-in-depth-modules/)
+
+You must run `dotenv.config()` before referencing any environment variables. Here's an example of problematic code:
+
+`errorReporter.js`:
+
+```js
+import { Client } from 'best-error-reporting-service'
+
+export const client = new Client(process.env.BEST_API_KEY)
+```
+
+`index.js`:
+
+```js
+import dotenv from 'dotenv'
+import errorReporter from './errorReporter'
+
+dotenv.config()
+errorReporter.client.report(new Error('faq example'))
+```
+
+`client` will not be configured correctly because it was constructed before `dotenv.config()` was executed. There are (at least) 3 ways to make this work.
+
+1. Preload dotenv: `node --require dotenv/config index.js` (_Note: you do not need to `import` dotenv with this approach_)
+2. Import `dotenv/config` instead of `dotenv` (_Note: you do not need to call `dotenv.config()` and must pass options via the command line or environment variables with this approach_)
+3. Create a separate file that will execute `config` first as outlined in [this comment on #133](https://github.com/motdotla/dotenv/issues/133#issuecomment-255298822)
+
+## Contributing Guide
+
+See [CONTRIBUTING.md](CONTRIBUTING.md)
+
+## Change Log
+
+See [CHANGELOG.md](CHANGELOG.md)
+
+## Who's using dotenv?
+
+[These npm modules depend on it.](https://www.npmjs.com/browse/depended/dotenv)
+
+Projects that expand it often use the [keyword "dotenv" on npm](https://www.npmjs.com/search?q=keywords:dotenv).

+ 11 - 0
node_modules/dotenv/config.js

@@ -0,0 +1,11 @@
+/* @flow */
+
+(function () {
+  require('./lib/main').config(
+    Object.assign(
+      {},
+      require('./lib/env-options'),
+      require('./lib/cli-options')(process.argv)
+    )
+  )
+})()

+ 13 - 0
node_modules/dotenv/lib/cli-options.js

@@ -0,0 +1,13 @@
+/* @flow */
+
+const re = /^dotenv_config_(encoding|path|debug)=(.+)$/
+
+module.exports = function optionMatcher (args /*: Array<string> */) {
+  return args.reduce(function (acc, cur) {
+    const matches = cur.match(re)
+    if (matches) {
+      acc[matches[1]] = matches[2]
+    }
+    return acc
+  }, {})
+}

+ 18 - 0
node_modules/dotenv/lib/env-options.js

@@ -0,0 +1,18 @@
+/* @flow */
+
+// ../config.js accepts options via environment variables
+const options = {}
+
+if (process.env.DOTENV_CONFIG_ENCODING != null) {
+  options.encoding = process.env.DOTENV_CONFIG_ENCODING
+}
+
+if (process.env.DOTENV_CONFIG_PATH != null) {
+  options.path = process.env.DOTENV_CONFIG_PATH
+}
+
+if (process.env.DOTENV_CONFIG_DEBUG != null) {
+  options.debug = process.env.DOTENV_CONFIG_DEBUG
+}
+
+module.exports = options

+ 118 - 0
node_modules/dotenv/lib/main.js

@@ -0,0 +1,118 @@
+/* @flow */
+/*::
+
+type DotenvParseOptions = {
+  debug?: boolean
+}
+
+// keys and values from src
+type DotenvParseOutput = { [string]: string }
+
+type DotenvConfigOptions = {
+  path?: string, // path to .env file
+  encoding?: string, // encoding of .env file
+  debug?: string // turn on logging for debugging purposes
+}
+
+type DotenvConfigOutput = {
+  parsed?: DotenvParseOutput,
+  error?: Error
+}
+
+*/
+
+const fs = require('fs')
+const path = require('path')
+const os = require('os')
+
+function log (message /*: string */) {
+  console.log(`[dotenv][DEBUG] ${message}`)
+}
+
+const NEWLINE = '\n'
+const RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*(.*)?\s*$/
+const RE_NEWLINES = /\\n/g
+const NEWLINES_MATCH = /\r\n|\n|\r/
+
+// Parses src into an Object
+function parse (src /*: string | Buffer */, options /*: ?DotenvParseOptions */) /*: DotenvParseOutput */ {
+  const debug = Boolean(options && options.debug)
+  const obj = {}
+
+  // convert Buffers before splitting into lines and processing
+  src.toString().split(NEWLINES_MATCH).forEach(function (line, idx) {
+    // matching "KEY' and 'VAL' in 'KEY=VAL'
+    const keyValueArr = line.match(RE_INI_KEY_VAL)
+    // matched?
+    if (keyValueArr != null) {
+      const key = keyValueArr[1]
+      // default undefined or missing values to empty string
+      let val = (keyValueArr[2] || '')
+      const end = val.length - 1
+      const isDoubleQuoted = val[0] === '"' && val[end] === '"'
+      const isSingleQuoted = val[0] === "'" && val[end] === "'"
+
+      // if single or double quoted, remove quotes
+      if (isSingleQuoted || isDoubleQuoted) {
+        val = val.substring(1, end)
+
+        // if double quoted, expand newlines
+        if (isDoubleQuoted) {
+          val = val.replace(RE_NEWLINES, NEWLINE)
+        }
+      } else {
+        // remove surrounding whitespace
+        val = val.trim()
+      }
+
+      obj[key] = val
+    } else if (debug) {
+      log(`did not match key and value when parsing line ${idx + 1}: ${line}`)
+    }
+  })
+
+  return obj
+}
+
+function resolveHome (envPath) {
+  return envPath[0] === '~' ? path.join(os.homedir(), envPath.slice(1)) : envPath
+}
+
+// Populates process.env from .env file
+function config (options /*: ?DotenvConfigOptions */) /*: DotenvConfigOutput */ {
+  let dotenvPath = path.resolve(process.cwd(), '.env')
+  let encoding /*: string */ = 'utf8'
+  let debug = false
+
+  if (options) {
+    if (options.path != null) {
+      dotenvPath = resolveHome(options.path)
+    }
+    if (options.encoding != null) {
+      encoding = options.encoding
+    }
+    if (options.debug != null) {
+      debug = true
+    }
+  }
+
+  try {
+    // specifying an encoding returns a string instead of a buffer
+    const parsed = parse(fs.readFileSync(dotenvPath, { encoding }), { debug })
+
+    Object.keys(parsed).forEach(function (key) {
+      if (!Object.prototype.hasOwnProperty.call(process.env, key)) {
+        process.env[key] = parsed[key]
+      } else if (debug) {
+        log(`"${key}" is already defined in \`process.env\` and will not be overwritten`)
+      }
+    })
+
+    return { parsed }
+  } catch (e) {
+    return { error: e }
+  }
+}
+
+module.exports.config = config
+module.exports.parse = parse

+ 57 - 0
node_modules/dotenv/package.json

@@ -0,0 +1,57 @@
+{
+  "name": "dotenv",
+  "version": "10.0.0",
+  "description": "Loads environment variables from .env file",
+  "main": "lib/main.js",
+  "exports": {
+    ".": "./lib/main.js",
+    "./config": "./config.js",
+    "./config.js": "./config.js",
+    "./package.json": "./package.json"
+  },
+  "types": "types/index.d.ts",
+  "scripts": {
+    "flow": "flow",
+    "dtslint": "dtslint types",
+    "lint": "standard",
+    "postlint": "standard-markdown",
+    "pretest": "npm run lint && npm run dtslint",
+    "test": "tap tests/*.js --100",
+    "prerelease": "npm test",
+    "release": "standard-version"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/motdotla/dotenv.git"
+  },
+  "keywords": [
+    "dotenv",
+    "env",
+    ".env",
+    "environment",
+    "variables",
+    "config",
+    "settings"
+  ],
+  "readmeFilename": "README.md",
+  "license": "BSD-2-Clause",
+  "devDependencies": {
+    "decache": "^4.5.1",
+    "dtslint": "^0.9.8",
+    "flow-bin": "^0.109.0",
+    "sinon": "^7.5.0",
+    "standard": "^13.1.0",
+    "standard-markdown": "^5.1.0",
+    "standard-version": "^7.0.0",
+    "tap": "^14.7.0"
+  },
+  "dependencies": {},
+  "engines": {
+    "node": ">=10"
+  },
+  "standard": {
+    "ignore": [
+      "flow-typed/"
+    ]
+  }
+}

+ 59 - 0
node_modules/dotenv/types/index.d.ts

@@ -0,0 +1,59 @@
+// TypeScript Version: 3.0
+/// <reference types="node" />
+
+export interface DotenvParseOptions {
+  /**
+   * You may turn on logging to help debug why certain keys or values are not being set as you expect.
+   */
+  debug?: boolean;
+}
+
+export interface DotenvParseOutput {
+  [name: string]: string;
+}
+
+/**
+ * Parses a string or buffer in the .env file format into an object.
+ *
+ * @param src - contents to be parsed
+ * @param options - additional options
+ * @returns an object with keys and values based on `src`
+ */
+export function parse<T extends DotenvParseOutput = DotenvParseOutput>(
+  src: string | Buffer,
+  options?: DotenvParseOptions
+): T;
+
+export interface DotenvConfigOptions {
+  /**
+   * You may specify a custom path if your file containing environment variables is located elsewhere.
+   */
+  path?: string;
+
+  /**
+   * You may specify the encoding of your file containing environment variables.
+   */
+  encoding?: string;
+
+  /**
+   * You may turn on logging to help debug why certain keys or values are not being set as you expect.
+   */
+  debug?: boolean;
+}
+
+export interface DotenvConfigOutput {
+  error?: Error;
+  parsed?: DotenvParseOutput;
+}
+
+/**
+ * Loads `.env` file contents into {@link https://nodejs.org/api/process.html#process_process_env `process.env`}.
+ * Example: 'KEY=value' becomes { parsed: { KEY: 'value' } }
+ *
+ * @param options - controls behavior
+ * @returns an object with a `parsed` key if successful or `error` key if an error occurred
+ *
+ */
+export function config(options?: DotenvConfigOptions): DotenvConfigOutput;
+/** @deprecated since v7.0.0 Use config instead. */
+export const load: typeof config;

+ 19 - 0
node_modules/dotenv/types/test.ts

@@ -0,0 +1,19 @@
+import { config, parse } from "dotenv";
+
+const env = config();
+const dbUrl: string | null =
+  env.error || !env.parsed ? null : env.parsed["BASIC"];
+
+config({
+  path: ".env-example",
+  encoding: "utf8",
+  debug: true
+});
+
+const parsed = parse("NODE_ENV=production\nDB_HOST=a.b.c");
+const dbHost: string = parsed["DB_HOST"];
+
+const parsedFromBuffer = parse(new Buffer("JUSTICE=league\n"), {
+  debug: true
+});
+const justice: string = parsedFromBuffer["JUSTICE"];

+ 15 - 0
node_modules/dotenv/types/tsconfig.json

@@ -0,0 +1,15 @@
+{
+  "compilerOptions": {
+    "module": "commonjs",
+    "lib": ["es6"],
+    "noImplicitAny": true,
+    "noImplicitThis": true,
+    "strictNullChecks": true,
+    "strictFunctionTypes": true,
+    "noEmit": true,
+    "baseUrl": ".",
+    "paths": {
+      "dotenv": ["."]
+    }
+  }
+}

+ 6 - 0
node_modules/dotenv/types/tslint.json

@@ -0,0 +1,6 @@
+{
+  "extends": "dtslint/dtslint.json",
+  "rules": {
+    "strict-export-declare-modifiers": false
+  }
+}

+ 4 - 0
node_modules/node-scp/.husky/commit-msg

@@ -0,0 +1,4 @@
+#!/bin/sh
+. "$(dirname "$0")/_/husky.sh"
+
+npx --no-install commitlint --edit "$1"

+ 21 - 0
node_modules/node-scp/CHANGELOG.md

@@ -0,0 +1,21 @@
+# Changelog
+
+All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+### 0.0.15 (2021-07-08)
+
+
+### Features
+
+* add download for remote dir ([e9ea319](https://github.com/maitrungduc1410/node-scp-async/commit/e9ea319cbe09b1202baa684e5077c01bac32d284))
+* add support for readyTimeout and keepalive ([0fc1742](https://github.com/maitrungduc1410/node-scp-async/commit/0fc1742819776cbae6ae6623cdce4b6e2de21cb5))
+
+
+### Bug Fixes
+
+* use export default instead of module exports ([0bfbc8c](https://github.com/maitrungduc1410/node-scp-async/commit/0bfbc8c87088c9184b6c485231ea6fb7a585484e))
+
+# [0.0.7](#) (2020-09-18)
+### Features
+- Add authentication using `privateKey`
+- Add `forceIPv4` and `forceIPv6` when connecting to remote server

+ 21 - 0
node_modules/node-scp/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 Mai Trung Duc
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 534 - 0
node_modules/node-scp/README.md

@@ -0,0 +1,534 @@
+# New SCP module for NodeJS
+A lightweight, fast and secure module to perform SCP commands for NodeJS based on SSH2
+# What's new
+All functionalities are written using Promise. That means you can use it with `Promise` or `Async/Await` (No more callback hell, Yeah :) )
+
+And other new features:
+- Scp a directory from local to remote server and from remote to local
+- Perform commands on remote server: `mkdir`, `stat`, check if path exists
+
+# Table of Contents
+
+* [Installation](#installation)
+* [Guide](#guide)
+  * [Scp file from local to remote server](#scp-file-from-local-to-remote-server)
+  * [Scp file from remote server to local](#Scp-file-from-remote-server-to-local)
+  * [Scp a directory from local to remote server](#Scp-a-directory-from-local-to-remote-server)
+  * [Scp a directory from remote server to local](#Scp-a-directory-from-remote-server-to-local)
+  * [Create a directory on remote server](#Create-a-directory-on-remote-server)
+  * [Check if a path exists on remote server](#Check-if-a-path-exists-on-remote-server)
+  * [Get stats of a path on remote server](#Get-stats-of-a-path-on-remote-server)
+  * [List all files in remote directory](#List-all-files-in-remote-directory)
+  * [Convert relative path to absolute path on remote server](#Convert-relative-path-to-absolute-path-on-remote-server)
+  * [Connection options](#Connection-options)
+
+# Installation
+```
+npm install --save node-scp
+# or
+yarn add node-scp
+```
+# Guide
+## Scp file from local to remote server
+Using `Promise`
+```js
+// with commonJS
+const { Client } = require('node-scp')
+
+// with ES Module
+import { Client } from 'node-scp'
+
+Client({
+  host: 'your host',
+  port: 22,
+  username: 'username',
+  password: 'password',
+  // privateKey: fs.readFileSync('./key.pem'),
+  // passphrase: 'your key passphrase',
+}).then(client => {
+  client.uploadFile('./test.txt', '/workspace/test.txt')
+        .then(response => {
+          client.close() // remember to close connection after you finish
+        })
+        .catch(error => {})
+}).catch(e => console.log(e))
+```
+
+Using `async/await`:
+```js
+// with commonJS
+const { Client } = require('node-scp')
+
+// with ES Module
+import { Client } from 'node-scp'
+
+async function test() {
+  try {
+    const client = await Client({
+      host: 'your host',
+      port: 22,
+      username: 'username',
+      password: 'password',
+      // privateKey: fs.readFileSync('./key.pem'),
+      // passphrase: 'your key passphrase',
+    })
+    await client.uploadFile('./test.txt', '/workspace/test.txt')
+    // you can perform upload multiple times
+    await client.uploadFile('./test1.txt', '/workspace/test1.txt')
+    client.close() // remember to close connection after you finish
+  } catch (e) {
+    console.log(e)
+  }
+}
+
+test()
+```
+
+## Scp file from remote server to local
+Using `Promise`
+```js
+// with commonJS
+const { Client } = require('node-scp')
+
+// with ES Module
+import { Client } from 'node-scp'
+
+Client({
+  host: 'your host',
+  port: 22,
+  username: 'username',
+  password: 'password',
+  // privateKey: fs.readFileSync('./key.pem'),
+  // passphrase: 'your key passphrase',
+}).then(client => {
+  client.downloadFile('/workspace/test.txt', './test.txt')
+        .then(response => {
+          client.close() // remember to close connection after you finish
+        })
+        .catch(error => {})
+}).catch(e => console.log(e))
+```
+
+Using `async/await`:
+```js
+// with commonJS
+const { Client } = require('node-scp')
+
+// with ES Module
+import { Client } from 'node-scp'
+
+async function test () {
+  try {
+    const client = await Client({
+      host: 'your host',
+      port: 22,
+      username: 'username',
+      password: 'password',
+      // privateKey: fs.readFileSync('./key.pem'),
+      // passphrase: 'your key passphrase',
+    })
+    await client.downloadFile('/workspace/test.txt', './test.txt')
+    client.close() // remember to close connection after you finish
+  } catch(e) {
+    console.log(e)
+  }
+}
+
+test()
+```
+
+## Scp a directory from local to remote server
+Using `Promise`
+```js
+// with commonJS
+const { Client } = require('node-scp')
+
+// with ES Module
+import { Client } from 'node-scp'
+
+Client({
+  host: 'your host',
+  port: 22,
+  username: 'username',
+  password: 'password',
+  // privateKey: fs.readFileSync('./key.pem'),
+  // passphrase: 'your key passphrase',
+}).then(client => {
+  client.uploadDir('./local/dir', '/server/path')
+        .then(response => {
+          client.close() // remember to close connection after you finish
+        })
+        .catch(error => {})
+}).catch(e => console.log(e))
+```
+
+Using `async/await`:
+```js
+// with commonJS
+const { Client } = require('node-scp')
+
+// with ES Module
+import { Client } from 'node-scp'
+
+async funtion test () {
+  try {
+    const client = await Client({
+      host: 'your host',
+      port: 22,
+      username: 'username',
+      password: 'password',
+      // privateKey: fs.readFileSync('./key.pem'),
+      // passphrase: 'your key passphrase',
+    })
+    await client.uploadDir('./local/dir', '/server/path')
+    client.close() // remember to close connection after you finish
+  } catch (e) {
+    console.log(e)
+  }
+}
+
+test()
+```
+
+## Scp a directory from remote server to local
+Using `Promise`
+```js
+// with commonJS
+const { Client } = require('node-scp')
+
+// with ES Module
+import { Client } from 'node-scp'
+
+Client({
+  host: 'your host',
+  port: 22,
+  username: 'username',
+  password: 'password',
+  // privateKey: fs.readFileSync('./key.pem'),
+  // passphrase: 'your key passphrase',
+}).then(client => {
+  client.downloadDir('/server/path', 'local/path')
+        .then(response => {
+          client.close() // remember to close connection after you finish
+        })
+        .catch(error => {})
+}).catch(e => console.log(e))
+```
+
+Using `async/await`:
+```js
+// with commonJS
+const { Client } = require('node-scp')
+
+// with ES Module
+import { Client } from 'node-scp'
+
+async funtion test () {
+  try {
+    const client = await Client({
+      host: 'your host',
+      port: 22,
+      username: 'username',
+      password: 'password',
+      // privateKey: fs.readFileSync('./key.pem'),
+      // passphrase: 'your key passphrase',
+    })
+    await client.downloadDir('./local/dir', '/server/path')
+    client.close() // remember to close connection after you finish
+  } catch (e) {
+    console.log(e)
+  }
+}
+
+test()
+```
+
+## Create a directory on remote server
+Using `Promise`
+```js
+// with commonJS
+const { Client } = require('node-scp')
+
+// with ES Module
+import { Client } from 'node-scp'
+
+Client({
+  host: 'your host',
+  port: 22,
+  username: 'username',
+  password: 'password',
+  // privateKey: fs.readFileSync('./key.pem'),
+  // passphrase: 'your key passphrase',
+}).then(client => {
+  client.mkdir('/server/path')
+        .then(response => {
+          client.close() // remember to close connection after you finish
+        })
+        .catch(error => {})
+}).catch(e => console.log(e))
+```
+
+Using `async/await`:
+```js
+// with commonJS
+const { Client } = require('node-scp')
+
+// with ES Module
+import { Client } from 'node-scp'
+
+async function test() {
+  try {
+    const client = await Client({
+      host: 'your host',
+      port: 22,
+      username: 'username',
+      password: 'password',
+      // privateKey: fs.readFileSync('./key.pem'),
+      // passphrase: 'your key passphrase',
+    })
+    await client.mkdir('/server/path')
+    client.close() // remember to close connection after you finish
+  } catch (e) {
+    console.log(e)
+  }
+}
+
+test()
+```
+
+## Check if a path exists on remote server
+Using `Promise`
+```js
+// with commonJS
+const { Client } = require('node-scp')
+
+// with ES Module
+import { Client } from 'node-scp'
+
+Client({
+  host: 'your host',
+  port: 22,
+  username: 'username',
+  password: 'password',
+  // privateKey: fs.readFileSync('./key.pem'),
+  // passphrase: 'your key passphrase',
+}).then(client => {
+  const result = client.exists('/server/path')
+        .then(result => {
+          console.log(result)
+          client.close() // remember to close connection after you finish
+        })
+        .catch(error => {})
+}).catch(e => console.log(e))
+```
+
+Using `async/await`:
+```js
+// with commonJS
+const { Client } = require('node-scp')
+
+// with ES Module
+import { Client } from 'node-scp'
+async function test() {
+  try {
+    const client = await Client({
+      host: 'your host',
+      port: 22,
+      username: 'username',
+      password: 'password',
+      // privateKey: fs.readFileSync('./key.pem'),
+      // passphrase: 'your key passphrase',
+    })
+    const result = await client.exists('/server/path')
+    console.log(result)
+    client.close() // remember to close connection after you finish
+  } catch (e) {
+    console.log(e)
+  }
+}
+
+test()
+```
+
+## Get stats of a path on remote server
+Using `Promise`
+```js
+// with commonJS
+const { Client } = require('node-scp')
+
+// with ES Module
+import { Client } from 'node-scp'
+
+Client({
+  host: 'your host',
+  port: 22,
+  username: 'username',
+  password: 'password',
+  // privateKey: fs.readFileSync('./key.pem'),
+  // passphrase: 'your key passphrase',
+}).then(client => {
+  client.stat('/server/path')
+        .then(result => {
+          console.log(result)
+          client.close() // remember to close connection after you finish
+        })
+        .catch(error => {})
+}).catch(e => console.log(e))
+```
+
+Using `async/await`:
+```js
+// with commonJS
+const { Client } = require('node-scp')
+
+// with ES Module
+import { Client } from 'node-scp'
+
+async function test() {
+  try {
+    const client = await Client({
+      host: 'your host',
+      port: 22,
+      username: 'username',
+      password: 'password',
+      // privateKey: fs.readFileSync('./key.pem'),
+      // passphrase: 'your key passphrase',
+    })
+    cosnt result = await client.stat('/server/path')
+    console.log(result)
+    client.close() // remember to close connection after you finish
+  } catch (e) {
+    console.log(e)
+  }
+}
+
+test()
+```
+
+## List all files in remote directory
+Using `Promise`
+```js
+// with commonJS
+const { Client } = require('node-scp')
+
+// with ES Module
+import { Client } from 'node-scp'
+
+Client({
+  host: 'your host',
+  port: 22,
+  username: 'username',
+  password: 'password',
+  // privateKey: fs.readFileSync('./key.pem'),
+  // passphrase: 'your key passphrase',
+}).then(client => {
+  client.list('/server/path')
+        .then(result => {
+          console.log(result)
+          client.close() // remember to close connection after you finish
+        })
+        .catch(error => {})
+}).catch(e => console.log(e))
+```
+
+Using `async/await`:
+```js
+// with commonJS
+const { Client } = require('node-scp')
+
+// with ES Module
+import { Client } from 'node-scp'
+
+async function test() {
+  try {
+    const client = await Client({
+      host: 'your host',
+      port: 22,
+      username: 'username',
+      password: 'password',
+      // privateKey: fs.readFileSync('./key.pem'),
+      // passphrase: 'your key passphrase',
+    })
+    cosnt result = await client.list('/server/path')
+    console.log(result)
+    client.close() // remember to close connection after you finish
+  } catch (e) {
+    console.log(e)
+  }
+}
+
+test()
+```
+
+## Convert relative path to absolute path on remote server
+Using `Promise`
+```js
+// with commonJS
+const { Client } = require('node-scp')
+
+// with ES Module
+import { Client } from 'node-scp'
+
+Client({
+  host: 'your host',
+  port: 22,
+  username: 'username',
+  password: 'password',
+  // privateKey: fs.readFileSync('./key.pem'),
+  // passphrase: 'your key passphrase',
+}).then(client => {
+  client.realPath('/server/path')
+        .then(result => {
+          console.log(result)
+          client.close() // remember to close connection after you finish
+        })
+        .catch(error => {})
+}).catch(e => console.log(e))
+```
+
+Using `async/await`:
+```js
+// with commonJS
+const { Client } = require('node-scp')
+
+// with ES Module
+import { Client } from 'node-scp'
+
+async function test() {
+  try {
+    const client = await Client({
+      host: 'your host',
+      port: 22,
+      username: 'username',
+      password: 'password',
+      // privateKey: fs.readFileSync('./key.pem'),
+      // passphrase: 'your key passphrase',
+    })
+    cosnt result = await client.realPath('/server/path')
+    console.log(result)
+    client.close() // remember to close connection after you finish
+  } catch (e) {
+    console.log(e)
+  }
+}
+
+test()
+```
+
+## Connection options
+Below are available options you can pass when connecting to server:
+- **host**: - *string* - Hostname or IP address of the server. **Default**: `localhost`
+- **port** - *integer* - Port number of the server. **Default**: `22`
+- **forceIPv4** - *boolean* - Only connect via resolved IPv4 address for `host`. **Default**: `false`
+- **forceIPv6** - *boolean* - Only connect via resolved IPv6 address for `host`. **Default**: `false`
+- **username** - *string* - Username for authentication. **Default**: (none)
+- **password** - *string* - Password for password-based user authentication. **Default**: (none)
+- **privateKey** - *mixed* - `Buffer` or `string` that contains a private key for either key-based or hostbased user authentication (OpenSSH format). **Default**: (none)
+- **passphrase** - *string* - For an encrypted private key, this is the passphrase used to decrypt it. **Default**: (none)
+- **readyTimeout** - *integer* - FHow long (in milliseconds) to wait for the SSH handshake to complete. **Default**: `20000`
+- **keepaliveInterval** - *integer* - How often (in milliseconds) to send SSH-level keepalive packets to the server (in a similar way as OpenSSH's ServerAliveInterval config option). Set to 0 to disable. **Default**: `0`
+- **keepaliveCountMax** - *integer* -  How many consecutive, unanswered SSH-level keepalive packets that can be sent to the server before disconnection (similar to OpenSSH's ServerAliveCountMax config option). **Default**: `3`
+- **remoteOsType** - string (value: `posix` | `win32`): use backslash `\` for Windows or slash `/` on posix system (Linux, MacOS) when handling remote server path. **Default**: `posix`
+- **Default authentication method order**: None -> Password -> Private Key
+# Support
+If you like this project, give me 1 ⭐️

+ 1 - 0
node_modules/node-scp/commitlint.config.js

@@ -0,0 +1 @@
+module.exports = {extends: ['@commitlint/config-conventional']}

+ 16 - 0
node_modules/node-scp/lib/constant.d.ts

@@ -0,0 +1,16 @@
+export declare enum errorCode {
+    generic = "ERR_GENERIC_CLIENT",
+    connect = "ERR_NOT_CONNECTED",
+    badPath = "ERR_BAD_PATH",
+    permission = "EACCES",
+    notexist = "ENOENT",
+    notdir = "ENOTDIR"
+}
+export declare enum targetType {
+    writeFile = 1,
+    readFile = 2,
+    writeDir = 3,
+    readDir = 4,
+    readObj = 5,
+    writeObj = 6
+}

+ 21 - 0
node_modules/node-scp/lib/constant.js

@@ -0,0 +1,21 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.targetType = exports.errorCode = void 0;
+var errorCode;
+(function (errorCode) {
+    errorCode["generic"] = "ERR_GENERIC_CLIENT";
+    errorCode["connect"] = "ERR_NOT_CONNECTED";
+    errorCode["badPath"] = "ERR_BAD_PATH";
+    errorCode["permission"] = "EACCES";
+    errorCode["notexist"] = "ENOENT";
+    errorCode["notdir"] = "ENOTDIR";
+})(errorCode = exports.errorCode || (exports.errorCode = {}));
+var targetType;
+(function (targetType) {
+    targetType[targetType["writeFile"] = 1] = "writeFile";
+    targetType[targetType["readFile"] = 2] = "readFile";
+    targetType[targetType["writeDir"] = 3] = "writeDir";
+    targetType[targetType["readDir"] = 4] = "readDir";
+    targetType[targetType["readObj"] = 5] = "readObj";
+    targetType[targetType["writeObj"] = 6] = "writeObj";
+})(targetType = exports.targetType || (exports.targetType = {}));

+ 42 - 0
node_modules/node-scp/lib/index.d.ts

@@ -0,0 +1,42 @@
+/// <reference types="node" />
+import { EventEmitter } from 'events';
+import { SFTPWrapper } from 'ssh2';
+import { Stats } from 'ssh2-streams';
+export interface IScpOptions {
+    host?: string;
+    port?: number;
+    username?: string;
+    password?: string;
+    privateKey?: Buffer | string;
+    passphrase?: string;
+    forceIPv4?: boolean;
+    forceIPv6?: boolean;
+    readyTimeout?: number;
+    keepaliveInterval?: number;
+    keepaliveCountMax?: number;
+    remoteOsType?: 'posix' | 'win32';
+}
+export declare class ScpClient extends EventEmitter {
+    sftpWrapper: SFTPWrapper | null;
+    private sshClient;
+    remotePathSep: string;
+    endCalled: boolean;
+    errorHandled: boolean;
+    constructor(options: IScpOptions);
+    uploadFile(localPath: string, remotePath: string): Promise<void>;
+    downloadFile(remotePath: string, localPath: string): Promise<void>;
+    emptyDir(dir: string): Promise<void>;
+    uploadDir(src: string, dest: string): Promise<void>;
+    downloadDir(remotePath: string, localPath: string): Promise<string>;
+    stat(remotePath: string): Promise<Stats>;
+    unlink(remotePath: string): Promise<void>;
+    _rmdir(remotePath: string): Promise<void>;
+    rmdir(remotePath: string): Promise<void>;
+    mkdir(remotePath: string): Promise<void>;
+    exists(remotePath: string): Promise<string | boolean>;
+    close(): void;
+    list(remotePath: string, pattern?: RegExp): Promise<any>;
+    realPath(remotePath: string): Promise<string>;
+}
+export declare function Client(options: IScpOptions): Promise<ScpClient>;
+export default Client;

+ 591 - 0
node_modules/node-scp/lib/index.js

@@ -0,0 +1,591 @@
+"use strict";
+var __extends = (this && this.__extends) || (function () {
+    var extendStatics = function (d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+    return function (d, b) {
+        if (typeof b !== "function" && b !== null)
+            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    };
+})();
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+}) : (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+    Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+    o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+    __setModuleDefault(result, mod);
+    return result;
+};
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+    function verb(n) { return function (v) { return step([n, v]); }; }
+    function step(op) {
+        if (f) throw new TypeError("Generator is already executing.");
+        while (_) try {
+            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+            if (y = 0, t) op = [op[0] & 2, t.value];
+            switch (op[0]) {
+                case 0: case 1: t = op; break;
+                case 4: _.label++; return { value: op[1], done: false };
+                case 5: _.label++; y = op[1]; op = [0]; continue;
+                case 7: op = _.ops.pop(); _.trys.pop(); continue;
+                default:
+                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+                    if (t[2]) _.ops.pop();
+                    _.trys.pop(); continue;
+            }
+            op = body.call(thisArg, _);
+        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+    }
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.Client = exports.ScpClient = void 0;
+var events_1 = require("events");
+var fs_1 = require("fs");
+var path_1 = require("path");
+var ssh2_1 = require("ssh2");
+var constant_1 = require("./constant");
+var utils = __importStar(require("./utils"));
+var ScpClient = /** @class */ (function (_super) {
+    __extends(ScpClient, _super);
+    function ScpClient(options) {
+        var _this = _super.call(this) || this;
+        _this.sftpWrapper = null;
+        _this.sshClient = null;
+        _this.remotePathSep = path_1.posix.sep;
+        _this.endCalled = false;
+        _this.errorHandled = false;
+        var ssh = new ssh2_1.Client();
+        ssh.on('connect', function () {
+            _this.emit('connect');
+        });
+        ssh.on('ready', function () {
+            ssh.sftp(function (err, sftp) {
+                if (err) {
+                    throw err;
+                }
+                // save for reuse
+                _this.sftpWrapper = sftp;
+                _this.emit('ready');
+            });
+        });
+        ssh.on('error', function (err) {
+            _this.emit('error', err);
+        });
+        ssh.on('end', function () {
+            _this.emit('end');
+        });
+        ssh.on('close', function () {
+            if (!_this.endCalled) {
+                _this.sftpWrapper = null;
+            }
+            _this.emit('close');
+        });
+        ssh.on('keyboard-interactive', function (name, instructions, instructionsLang, prompts, finish) {
+            _this.emit('keyboard-interactive', name, instructions, instructionsLang, prompts, finish);
+        });
+        ssh.on('change password', function (message, language, done) {
+            _this.emit('change password', message, language, done);
+        });
+        ssh.on('tcp connection', function (details, accept, reject) {
+            _this.emit('tcp connection', details, accept, reject);
+        });
+        ssh.connect(options);
+        _this.sshClient = ssh;
+        if (options.remoteOsType === 'win32') {
+            _this.remotePathSep = path_1.win32.sep;
+        }
+        return _this;
+    }
+    ScpClient.prototype.uploadFile = function (localPath, remotePath) {
+        return __awaiter(this, void 0, void 0, function () {
+            var _this = this;
+            return __generator(this, function (_a) {
+                utils.haveConnection(this, 'uploadFile');
+                return [2 /*return*/, new Promise(function (resolve, reject) {
+                        _this.sftpWrapper.fastPut(localPath, remotePath, function (err) {
+                            if (err) {
+                                reject(err);
+                            }
+                            else {
+                                resolve();
+                            }
+                        });
+                    })];
+            });
+        });
+    };
+    ScpClient.prototype.downloadFile = function (remotePath, localPath) {
+        return __awaiter(this, void 0, void 0, function () {
+            var _this = this;
+            return __generator(this, function (_a) {
+                utils.haveConnection(this, 'downloadFile');
+                return [2 /*return*/, new Promise(function (resolve, reject) {
+                        _this.sftpWrapper.fastGet(remotePath, localPath, function (err) {
+                            if (err) {
+                                reject(err);
+                            }
+                            else {
+                                resolve();
+                            }
+                        });
+                    })];
+            });
+        });
+    };
+    ScpClient.prototype.emptyDir = function (dir) {
+        return __awaiter(this, void 0, void 0, function () {
+            var isExist, error_1;
+            return __generator(this, function (_a) {
+                switch (_a.label) {
+                    case 0:
+                        utils.haveConnection(this, 'uploadDir');
+                        _a.label = 1;
+                    case 1:
+                        _a.trys.push([1, 8, , 9]);
+                        return [4 /*yield*/, this.exists(dir)];
+                    case 2:
+                        isExist = _a.sent();
+                        if (!!isExist) return [3 /*break*/, 4];
+                        return [4 /*yield*/, this.mkdir(dir)];
+                    case 3:
+                        _a.sent();
+                        return [3 /*break*/, 7];
+                    case 4:
+                        if (!(isExist === 'd')) return [3 /*break*/, 7];
+                        return [4 /*yield*/, this.rmdir(dir)];
+                    case 5:
+                        _a.sent();
+                        return [4 /*yield*/, this.mkdir(dir)];
+                    case 6:
+                        _a.sent();
+                        _a.label = 7;
+                    case 7: return [3 /*break*/, 9];
+                    case 8:
+                        error_1 = _a.sent();
+                        throw error_1;
+                    case 9: return [2 /*return*/];
+                }
+            });
+        });
+    };
+    ScpClient.prototype.uploadDir = function (src, dest) {
+        return __awaiter(this, void 0, void 0, function () {
+            var isExist, dirEntries, _i, dirEntries_1, e, newSrc, newDst, newSrc, newDst, error_2;
+            return __generator(this, function (_a) {
+                switch (_a.label) {
+                    case 0:
+                        utils.haveConnection(this, 'uploadDir');
+                        _a.label = 1;
+                    case 1:
+                        _a.trys.push([1, 11, , 12]);
+                        return [4 /*yield*/, this.exists(dest)];
+                    case 2:
+                        isExist = _a.sent();
+                        if (!!isExist) return [3 /*break*/, 4];
+                        return [4 /*yield*/, this.mkdir(dest)];
+                    case 3:
+                        _a.sent();
+                        _a.label = 4;
+                    case 4:
+                        dirEntries = fs_1.readdirSync(src, {
+                            encoding: 'utf8',
+                            withFileTypes: true,
+                        });
+                        _i = 0, dirEntries_1 = dirEntries;
+                        _a.label = 5;
+                    case 5:
+                        if (!(_i < dirEntries_1.length)) return [3 /*break*/, 10];
+                        e = dirEntries_1[_i];
+                        if (!e.isDirectory()) return [3 /*break*/, 7];
+                        newSrc = path_1.join(src, e.name);
+                        newDst = utils.joinRemote(this, dest, e.name);
+                        return [4 /*yield*/, this.uploadDir(newSrc, newDst)];
+                    case 6:
+                        _a.sent();
+                        return [3 /*break*/, 9];
+                    case 7:
+                        if (!e.isFile()) return [3 /*break*/, 9];
+                        newSrc = path_1.join(src, e.name);
+                        newDst = utils.joinRemote(this, dest, e.name);
+                        return [4 /*yield*/, this.uploadFile(newSrc, newDst)
+                            // this.client.emit('upload', {source: src, destination: dst})
+                        ];
+                    case 8:
+                        _a.sent();
+                        _a.label = 9;
+                    case 9:
+                        _i++;
+                        return [3 /*break*/, 5];
+                    case 10: return [3 /*break*/, 12];
+                    case 11:
+                        error_2 = _a.sent();
+                        throw error_2;
+                    case 12: return [2 /*return*/];
+                }
+            });
+        });
+    };
+    ScpClient.prototype.downloadDir = function (remotePath, localPath) {
+        return __awaiter(this, void 0, void 0, function () {
+            var remoteInfo, localInfo, fileList, _i, fileList_1, f, newSrc, newDst, src, dst, err_1;
+            return __generator(this, function (_a) {
+                switch (_a.label) {
+                    case 0:
+                        utils.haveConnection(this, 'downloadDir');
+                        _a.label = 1;
+                    case 1:
+                        _a.trys.push([1, 12, , 13]);
+                        return [4 /*yield*/, utils.checkRemotePath(this, remotePath, constant_1.targetType.readDir)];
+                    case 2:
+                        remoteInfo = _a.sent();
+                        if (!remoteInfo.valid) {
+                            throw new Error(remoteInfo.msg);
+                        }
+                        if (!fs_1.existsSync(localPath)) {
+                            fs_1.mkdirSync(localPath);
+                        }
+                        return [4 /*yield*/, utils.checkLocalPath(localPath, constant_1.targetType.writeDir)];
+                    case 3:
+                        localInfo = _a.sent();
+                        if (localInfo.valid && !localInfo.type) {
+                            fs_1.mkdirSync(localInfo.path, { recursive: true });
+                        }
+                        if (!localInfo.valid) {
+                            throw new Error(localInfo.msg);
+                        }
+                        return [4 /*yield*/, this.list(remoteInfo.path)];
+                    case 4:
+                        fileList = _a.sent();
+                        _i = 0, fileList_1 = fileList;
+                        _a.label = 5;
+                    case 5:
+                        if (!(_i < fileList_1.length)) return [3 /*break*/, 11];
+                        f = fileList_1[_i];
+                        if (!(f.type === 'd')) return [3 /*break*/, 7];
+                        newSrc = remoteInfo.path + this.remotePathSep + f.name;
+                        newDst = path_1.join(localInfo.path, f.name);
+                        return [4 /*yield*/, this.downloadDir(newSrc, newDst)];
+                    case 6:
+                        _a.sent();
+                        return [3 /*break*/, 10];
+                    case 7:
+                        if (!(f.type === '-')) return [3 /*break*/, 9];
+                        src = remoteInfo.path + this.remotePathSep + f.name;
+                        dst = path_1.join(localInfo.path, f.name);
+                        return [4 /*yield*/, this.downloadFile(src, dst)];
+                    case 8:
+                        _a.sent();
+                        this.sshClient.emit('download', { source: src, destination: dst });
+                        return [3 /*break*/, 10];
+                    case 9:
+                        console.log("downloadDir: File ignored: " + f.name + " not regular file");
+                        _a.label = 10;
+                    case 10:
+                        _i++;
+                        return [3 /*break*/, 5];
+                    case 11: return [2 /*return*/, remoteInfo.path + " downloaded to " + localInfo.path];
+                    case 12:
+                        err_1 = _a.sent();
+                        throw new Error(err_1);
+                    case 13: return [2 /*return*/];
+                }
+            });
+        });
+    };
+    ScpClient.prototype.stat = function (remotePath) {
+        return __awaiter(this, void 0, void 0, function () {
+            var _this = this;
+            return __generator(this, function (_a) {
+                utils.haveConnection(this, 'stat');
+                return [2 /*return*/, new Promise(function (resolve, reject) {
+                        _this.sftpWrapper.stat(remotePath, function (err, stats) {
+                            if (err) {
+                                reject(err);
+                            }
+                            else {
+                                resolve(stats);
+                            }
+                        });
+                    })];
+            });
+        });
+    };
+    ScpClient.prototype.unlink = function (remotePath) {
+        return __awaiter(this, void 0, void 0, function () {
+            var _this = this;
+            return __generator(this, function (_a) {
+                utils.haveConnection(this, 'unlink');
+                return [2 /*return*/, new Promise(function (resolve, reject) {
+                        _this.sftpWrapper.unlink(remotePath, function (err) {
+                            if (err) {
+                                reject(err);
+                            }
+                            else {
+                                resolve();
+                            }
+                        });
+                    })];
+            });
+        });
+    };
+    // _rmdir - only works with an empty directory
+    ScpClient.prototype._rmdir = function (remotePath) {
+        return __awaiter(this, void 0, void 0, function () {
+            var _this = this;
+            return __generator(this, function (_a) {
+                return [2 /*return*/, new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
+                        return __generator(this, function (_a) {
+                            this.sftpWrapper.rmdir(remotePath, function (err) {
+                                if (err) {
+                                    reject(err);
+                                }
+                                else {
+                                    resolve();
+                                }
+                            });
+                            return [2 /*return*/];
+                        });
+                    }); })];
+            });
+        });
+    };
+    ScpClient.prototype.rmdir = function (remotePath) {
+        return __awaiter(this, void 0, void 0, function () {
+            var files, _i, files_1, file, fullFilename;
+            return __generator(this, function (_a) {
+                switch (_a.label) {
+                    case 0: return [4 /*yield*/, this.list(remotePath)];
+                    case 1:
+                        files = _a.sent();
+                        _i = 0, files_1 = files;
+                        _a.label = 2;
+                    case 2:
+                        if (!(_i < files_1.length)) return [3 /*break*/, 7];
+                        file = files_1[_i];
+                        fullFilename = utils.joinRemote(this, remotePath, file.name);
+                        if (!(file.type === 'd')) return [3 /*break*/, 4];
+                        return [4 /*yield*/, this.rmdir(fullFilename)];
+                    case 3:
+                        _a.sent();
+                        return [3 /*break*/, 6];
+                    case 4: return [4 /*yield*/, this.unlink(fullFilename)];
+                    case 5:
+                        _a.sent();
+                        _a.label = 6;
+                    case 6:
+                        _i++;
+                        return [3 /*break*/, 2];
+                    case 7: return [4 /*yield*/, this._rmdir(remotePath)];
+                    case 8:
+                        _a.sent();
+                        return [2 /*return*/];
+                }
+            });
+        });
+    };
+    ScpClient.prototype.mkdir = function (remotePath) {
+        return __awaiter(this, void 0, void 0, function () {
+            var _this = this;
+            return __generator(this, function (_a) {
+                utils.haveConnection(this, 'mkdir');
+                return [2 /*return*/, new Promise(function (resolve, reject) {
+                        _this.sftpWrapper.mkdir(remotePath, function (err) {
+                            if (err) {
+                                reject(err);
+                            }
+                            else {
+                                resolve();
+                            }
+                        });
+                    })];
+            });
+        });
+    };
+    ScpClient.prototype.exists = function (remotePath) {
+        return __awaiter(this, void 0, void 0, function () {
+            var stats, error_3;
+            return __generator(this, function (_a) {
+                switch (_a.label) {
+                    case 0:
+                        utils.haveConnection(this, 'exists');
+                        _a.label = 1;
+                    case 1:
+                        _a.trys.push([1, 3, , 4]);
+                        return [4 /*yield*/, this.stat(remotePath)];
+                    case 2:
+                        stats = _a.sent();
+                        if (stats.isDirectory()) {
+                            return [2 /*return*/, 'd'];
+                        }
+                        if (stats.isSymbolicLink()) {
+                            return [2 /*return*/, 'l'];
+                        }
+                        if (stats.isFile()) {
+                            return [2 /*return*/, '-'];
+                        }
+                        return [2 /*return*/, false];
+                    case 3:
+                        error_3 = _a.sent();
+                        return [2 /*return*/, false];
+                    case 4: return [2 /*return*/];
+                }
+            });
+        });
+    };
+    ScpClient.prototype.close = function () {
+        if (this.sftpWrapper) {
+            this.sftpWrapper.end();
+            this.sftpWrapper = null;
+        }
+        if (this.sshClient) {
+            this.sshClient.end();
+            this.sshClient = null;
+        }
+        this.endCalled = true;
+    };
+    ScpClient.prototype.list = function (remotePath, pattern) {
+        if (pattern === void 0) { pattern = /.*/; }
+        return __awaiter(this, void 0, void 0, function () {
+            var _list, pathInfo, err_2;
+            var _this = this;
+            return __generator(this, function (_a) {
+                switch (_a.label) {
+                    case 0:
+                        _list = function (aPath, filter) {
+                            return new Promise(function (resolve, reject) {
+                                var reg = /-/gi;
+                                _this.sftpWrapper.readdir(aPath, function (err, fileList) {
+                                    if (err) {
+                                        reject(err);
+                                    }
+                                    else {
+                                        var newList = [];
+                                        // reset file info
+                                        if (fileList) {
+                                            newList = fileList.map(function (item) {
+                                                return {
+                                                    type: item.longname.substr(0, 1),
+                                                    name: item.filename,
+                                                    size: item.attrs.size,
+                                                    modifyTime: item.attrs.mtime * 1000,
+                                                    accessTime: item.attrs.atime * 1000,
+                                                    rights: {
+                                                        user: item.longname.substr(1, 3).replace(reg, ''),
+                                                        group: item.longname.substr(4, 3).replace(reg, ''),
+                                                        other: item.longname.substr(7, 3).replace(reg, '')
+                                                    },
+                                                    owner: item.attrs.uid,
+                                                    group: item.attrs.gid
+                                                };
+                                            });
+                                        }
+                                        // provide some compatibility for auxList
+                                        var regex_1;
+                                        if (filter instanceof RegExp) {
+                                            regex_1 = filter;
+                                        }
+                                        else {
+                                            var newPattern = filter.replace(/\*([^*])*?/gi, '.*');
+                                            regex_1 = new RegExp(newPattern);
+                                        }
+                                        resolve(newList.filter(function (item) { return regex_1.test(item.name); }));
+                                    }
+                                });
+                            });
+                        };
+                        _a.label = 1;
+                    case 1:
+                        _a.trys.push([1, 3, , 4]);
+                        utils.haveConnection(this, 'list');
+                        return [4 /*yield*/, utils.checkRemotePath(this, remotePath, constant_1.targetType.readDir)];
+                    case 2:
+                        pathInfo = _a.sent();
+                        if (!pathInfo.valid) {
+                            throw new Error('Remote path is invalid');
+                        }
+                        return [2 /*return*/, _list(pathInfo.path, pattern)];
+                    case 3:
+                        err_2 = _a.sent();
+                        throw new Error(err_2);
+                    case 4: return [2 /*return*/];
+                }
+            });
+        });
+    };
+    ScpClient.prototype.realPath = function (remotePath) {
+        var _this = this;
+        return new Promise(function (resolve, reject) {
+            var closeListener = utils.makeCloseListener(_this, reject, 'realPath');
+            _this.sshClient.prependListener('close', closeListener);
+            var errorListener = utils.makeErrorListener(reject, _this, 'realPath');
+            _this.sshClient.prependListener('error', errorListener);
+            if (utils.haveConnection(_this, 'realPath', reject)) {
+                _this.sftpWrapper.realpath(remotePath, function (err, absPath) {
+                    if (err) {
+                        if (err.code === 2) {
+                            resolve('');
+                        }
+                        else {
+                            reject(utils.formatError(err.message + " " + remotePath, 'realPath', err.code));
+                        }
+                    }
+                    resolve(absPath);
+                    _this.removeListener('error', errorListener);
+                    _this.removeListener('close', closeListener);
+                });
+            }
+        });
+    };
+    return ScpClient;
+}(events_1.EventEmitter));
+exports.ScpClient = ScpClient;
+function Client(options) {
+    return __awaiter(this, void 0, void 0, function () {
+        var client;
+        return __generator(this, function (_a) {
+            client = new ScpClient(options);
+            return [2 /*return*/, new Promise(function (resolve, reject) {
+                    client.on('ready', function () {
+                        resolve(client);
+                    });
+                    client.on('error', function (err) {
+                        reject(err);
+                    });
+                })];
+        });
+    });
+}
+exports.Client = Client;
+exports.default = Client;

+ 14 - 0
node_modules/node-scp/lib/types.d.ts

@@ -0,0 +1,14 @@
+export declare class ErrorCustom extends Error {
+    custom?: boolean;
+    code?: string;
+    level?: string;
+    hostname?: string;
+    address?: string;
+}
+export interface CheckResult {
+    path: string;
+    type?: string;
+    valid?: boolean;
+    msg?: string;
+    code?: string;
+}

+ 26 - 0
node_modules/node-scp/lib/types.js

@@ -0,0 +1,26 @@
+"use strict";
+var __extends = (this && this.__extends) || (function () {
+    var extendStatics = function (d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+    return function (d, b) {
+        if (typeof b !== "function" && b !== null)
+            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    };
+})();
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.ErrorCustom = void 0;
+var ErrorCustom = /** @class */ (function (_super) {
+    __extends(ErrorCustom, _super);
+    function ErrorCustom() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    return ErrorCustom;
+}(Error));
+exports.ErrorCustom = ErrorCustom;

+ 172 - 0
node_modules/node-scp/lib/utils.d.ts

@@ -0,0 +1,172 @@
+/// <reference types="node" />
+import { errorCode, targetType } from './constant';
+import { EventEmitter } from 'events';
+import { ScpClient } from '.';
+import { CheckResult, ErrorCustom } from './types';
+/**
+ * Generate a new Error object with a reformatted error message which
+ * is a little more informative and useful to users.
+ *
+ * @param {Error|string} err - The Error object the new error will be based on
+ * @param {number} retryCount - For those functions which use retry. Number of
+ *                              attempts to complete before giving up
+ * @returns {Error} New error with custom error message
+ */
+export declare function formatError(err: ErrorCustom | string, name?: string, eCode?: errorCode, retryCount?: number): ErrorCustom;
+/**
+ * Tests an error to see if it is one which has already been customised
+ * by this module or not. If not, applies appropriate customisation.
+ *
+ * @param {Error} err - an Error object
+ * @param {String} name - name to be used in customised error message
+ * @param {Function} reject - If defined, call this function instead of
+ *                            throwing the error
+ * @throws {Error}
+ */
+export declare function handleError(err: ErrorCustom, name: string, reject: (e: any) => void): void;
+/**
+ * Remove all ready, error and end listeners.
+ *
+ * @param {Emitter} emitter - The emitter object to remove listeners from
+ */
+/**
+ * Simple default error listener. Will reformat the error message and
+ * throw a new error.
+ *
+ * @param {Error} err - source for defining new error
+ * @throws {Error} Throws new error
+ */
+export declare function makeErrorListener(reject: (e: any) => void, client: ScpClient, name: string): (err: Error) => void;
+export declare function makeEndListener(client: ScpClient): () => void;
+export declare function makeCloseListener(client: ScpClient, reject?: (e: any) => void, name?: string): () => void;
+/**
+ * @async
+ *
+ * Tests to see if a path identifies an existing item. Returns either
+ * 'd' = directory, 'l' = sym link or '-' regular file if item exists. Returns
+ * false if it does not
+ *
+ * @param {String} localPath
+ * @returns {Boolean | String}
+ */
+export declare function localExists(localPath: string): Promise<string>;
+/**
+ * Used by checkRemotePath and checkLocalPath to help ensure consistent
+ * error messages.
+ *
+ * @param {Error} err - original error
+ * @param {String} testPath - path associated with the error
+ * @returns {Object} with properties of 'msg' and 'code'.
+ */
+export declare function classifyError(err: ErrorCustom, testPath: string): {
+    msg: string;
+    code: string;
+};
+export declare function localAccess(localPath: string, mode: number): Promise<CheckResult>;
+export declare function checkLocalReadFile(localPath: string, localType: string): Promise<CheckResult>;
+export declare function checkLocalReadDir(localPath: string, localType: string): Promise<CheckResult>;
+export declare function checkLocalWriteFile(localPath: string, localType: string): Promise<CheckResult>;
+export declare function checkLocalWriteDir(localPath: string, localType: string): Promise<CheckResult>;
+export declare function checkLocalPath(lPath: string, target?: targetType): Promise<CheckResult>;
+export declare function normalizeRemotePath(client: ScpClient, aPath: string): Promise<string>;
+export declare function checkReadObject(aPath: string, type: string): {
+    path: string;
+    type: string;
+    valid: boolean;
+    msg: string | undefined;
+    code: errorCode | undefined;
+};
+export declare function checkReadFile(aPath: string, type: string): {
+    path: string;
+    type: string;
+    valid: boolean;
+    msg: string;
+    code: errorCode;
+} | {
+    path: string;
+    type: string;
+    valid: boolean;
+    msg?: undefined;
+    code?: undefined;
+};
+export declare function checkReadDir(aPath: string, type: string): {
+    path: string;
+    type: string;
+    valid: boolean;
+    msg: string;
+    code: errorCode;
+} | {
+    path: string;
+    type: string;
+    valid: boolean;
+    msg?: undefined;
+    code?: undefined;
+};
+export declare function checkWriteFile(client: ScpClient, aPath: string, type: string): Promise<{
+    path: string;
+    type: string;
+    valid: boolean;
+    msg: string;
+    code: errorCode;
+} | {
+    path: string;
+    type: boolean;
+    valid: boolean;
+    msg: string;
+    code: errorCode;
+} | {
+    path: string;
+    type: string;
+    valid: boolean;
+    msg?: undefined;
+    code?: undefined;
+}>;
+export declare function checkWriteDir(client: ScpClient, aPath: string, type: string): Promise<{
+    path: string;
+    type: string;
+    valid: boolean;
+    msg: string;
+    code: errorCode;
+} | {
+    path: string;
+    type: string;
+    valid: boolean;
+    msg?: undefined;
+    code?: undefined;
+} | {
+    path: string;
+    type: boolean;
+    valid: boolean;
+    msg: string;
+    code: errorCode;
+}>;
+export declare function checkWriteObject(aPath: string, type: string): {
+    path: string;
+    type: string;
+    valid: boolean;
+};
+export declare function checkRemotePath(client: ScpClient, rPath: string, target?: targetType): Promise<{
+    path: string;
+    type: boolean;
+    valid: boolean;
+    msg: string;
+    code: errorCode;
+} | {
+    path: string;
+    type: string;
+    valid: boolean;
+}>;
+/**
+ * Check to see if there is an active sftp connection
+ *
+ * @param {Object} client - current sftp object
+ * @param {String} name - name given to this connection
+ * @param {Function} reject - if defined, call this rather than throw
+ *                            an error
+ * @returns {Boolean} True if connection OK
+ * @throws {Error}
+ */
+export declare function haveConnection(client: ScpClient, name: string, reject?: (e: any) => void): boolean;
+export declare function dumpListeners(emitter: EventEmitter): void;
+export declare function hasListener(emitter: EventEmitter, eventName: string, listenerName: string): boolean;
+export declare function joinRemote(client: ScpClient, ...args: string[]): string;

File diff suppressed because it is too large
+ 831 - 0
node_modules/node-scp/lib/utils.js


+ 49 - 0
node_modules/node-scp/package.json

@@ -0,0 +1,49 @@
+{
+  "name": "node-scp",
+  "title": "SCP module for NodeJS",
+  "description": "Lightweight, fast and secure SCP function for NodeJS",
+  "version": "0.0.15",
+  "main": "lib/index.js",
+  "types": "lib/index.d.ts",
+  "license": "MIT",
+  "licenseFilename": "LICENSE",
+  "readmeFilename": "README.md",
+  "author": {
+    "name": "Mai Trung Duc",
+    "email": "maitrungduc1410@gmail.com"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/maitrungduc1410/node-scp-async.git",
+    "baseUrl": "https://github.com/maitrungduc1410/node-scp-async"
+  },
+  "bugs": {
+    "url": "https://github.com/maitrungduc1410/node-scp-async/issues"
+  },
+  "homepage": "https://github.com/maitrungduc1410/node-scp-async",
+  "keywords": [
+    "node scp",
+    "node scp async",
+    "node-scp",
+    "node-scp-async",
+    "scp node",
+    "scp nodejs"
+  ],
+  "scripts": {
+    "watch": "tsc -w",
+    "build": "tsc",
+    "release": "standard-version"
+  },
+  "dependencies": {
+    "ssh2": "^1.1.0"
+  },
+  "devDependencies": {
+    "@commitlint/cli": "^12.1.4",
+    "@commitlint/config-conventional": "^12.1.4",
+    "@types/node": "^16.0.1",
+    "@types/ssh2": "^0.5.47",
+    "husky": "^7.0.1",
+    "standard-version": "^9.3.0",
+    "typescript": "^4.3.5"
+  }
+}

+ 31 - 0
package-lock.json

@@ -9,10 +9,12 @@
       "version": "1.0.0",
       "license": "ISC",
       "dependencies": {
+        "dotenv": "^10.0.0",
         "express": "^4.17.1",
         "express-fileupload": "^1.2.1",
         "fs": "^0.0.1-security",
         "multer": "^1.4.3",
+        "node-scp": "^0.0.15",
         "nodemon": "^2.0.12",
         "ssh2": "^1.4.0",
         "tunnel-ssh": "^4.1.4"
@@ -563,6 +565,14 @@
         "node": ">=8"
       }
     },
+    "node_modules/dotenv": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
+      "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/duplexer3": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
@@ -1214,6 +1224,14 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/node-scp": {
+      "version": "0.0.15",
+      "resolved": "https://registry.npmjs.org/node-scp/-/node-scp-0.0.15.tgz",
+      "integrity": "sha512-iQQpzVqsczdM35tI5JzFkM3DFMJb2VIeHGBVXU9d5EIgSrP4HGE44SScGv83D5aEtYCaY2r5uR1bPhUQwp6hRg==",
+      "dependencies": {
+        "ssh2": "^1.1.0"
+      }
+    },
     "node_modules/nodemon": {
       "version": "2.0.12",
       "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.12.tgz",
@@ -2361,6 +2379,11 @@
         "is-obj": "^2.0.0"
       }
     },
+    "dotenv": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
+      "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q=="
+    },
     "duplexer3": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
@@ -2859,6 +2882,14 @@
       "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
       "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
     },
+    "node-scp": {
+      "version": "0.0.15",
+      "resolved": "https://registry.npmjs.org/node-scp/-/node-scp-0.0.15.tgz",
+      "integrity": "sha512-iQQpzVqsczdM35tI5JzFkM3DFMJb2VIeHGBVXU9d5EIgSrP4HGE44SScGv83D5aEtYCaY2r5uR1bPhUQwp6hRg==",
+      "requires": {
+        "ssh2": "^1.1.0"
+      }
+    },
     "nodemon": {
       "version": "2.0.12",
       "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.12.tgz",

+ 2 - 0
package.json

@@ -11,10 +11,12 @@
   "author": "",
   "license": "ISC",
   "dependencies": {
+    "dotenv": "^10.0.0",
     "express": "^4.17.1",
     "express-fileupload": "^1.2.1",
     "fs": "^0.0.1-security",
     "multer": "^1.4.3",
+    "node-scp": "^0.0.15",
     "nodemon": "^2.0.12",
     "ssh2": "^1.4.0",
     "tunnel-ssh": "^4.1.4"

+ 43 - 40
ssh.js

@@ -1,46 +1,49 @@
-const { Client } = require('ssh2');
+const { Client } = require("node-scp");
+const dotenv = require("dotenv").config();
 
-const conn1 = new Client();
-const conn2 = new Client();
+const client1 = new Client();
 
-// Checks uptime on 10.1.1.40 via 192.168.1.1
 
-conn1.on('ready', () => {
-  console.log('FIRST :: connection ready');
-  // Alternatively, you could use something like netcat or socat with exec()
-  // instead of forwardOut(), depending on what the server allows
-  conn1.forwardOut('boole.tchpc.tcd.ie', 12345, 'grove2.computing.dcu.ie', 22, (err, stream) => {
-    if (err) {
-      console.log('FIRST :: forwardOut error: ' + err);
-      return conn1.end();
-    }
-    conn2.connect({
-      sock: stream,
-      username: 'cdrakeford',
-      password: '*****',
-	  tryKeyboard: true
+async function startClient1() {
+  await client1.on('ready', () => {
+    console.log('FIRST :: connection ready');
+    // Alternatively, you could use something like netcat or socat with exec()
+    // instead of forwardOut(), depending on what the server allows
+    conn1.forwardOut(process.env.BOOLE, 12345, process.env.GROVE, 22, (err, stream) => {
+      if (err) {
+        console.log('FIRST :: forwardOut error: ' + err);
+        return conn1.end();
+      }
+      conn2.connect({
+        sock: stream,
+        username: process.env.SSH_USER,
+        password: process.env.SSH_PASSWORD,
+        tryKeyboard: true,
+      });
     });
+  }).connect({
+    host: process.env.BOOLE,
+    port: 22,
+    username: process.env.SSH_USER,
+    password: process.env.SSH_PASSWORD,
+    tryKeyboard: true,
   });
-}).connect({
-  host: 'boole.tchpc.tcd.ie',
-  username: 'cdrakeford',
-  password: '*****',
-  tryKeyboard: true
-});
-
-conn2.on('ready', () => {
-  // This connection is the one to 10.1.1.40
-
-  console.log('SECOND :: connection ready');
-  conn2.exec('uptime', (err, stream) => {
-    if (err) {
-      console.log('SECOND :: exec error: ' + err);
-      return conn1.end();
-    }
-    stream.on('close', () => {
-      conn1.end(); // close parent (and this) connection
-    }).on('data', (data) => {
-      console.log(data.toString());
+  
+  const client2.on = await Client('ready', () => {
+    console.log('Client :: ready');
+    conn.shell((err, stream) => {
+      if (err) throw err;
+      stream.on('close', () => {
+        console.log('Stream :: close');
+        conn.end();
+      }).on('data', (data) => {
+        console.log('OUTPUT: ' + data);
+      });
+      stream.end('ls -l\nexit\n');
     });
-  });
-});
+  })
+}
+  return client2;
+module.exports = {
+  startClient1,
+};