Security News
The Unpaid Backbone of Open Source: Solo Maintainers Face Increasing Security Demands
Solo open source maintainers face burnout and security challenges, with 60% unpaid and 60% considering quitting.
OroSftp Class is a wrapper of ssh2-sftp-client to work as promises async/await and typescript.
OSftp Class is a wrapper of ssh2-sftp-client to simplify their use.
ssh2-sftp-client is a SFTP client module for node.js that provides an asynchronous interface for communicating with a SFTP server.
To have the same interface using FTP, you can utilize the OroFtp class available through OroFtp
npm install oro-sftp
Example:
// cjs
const { OSftp } = require( 'oro-sftp' );
// mjs, ts
import OSFtp from 'oro-sftp';
const sftpClient = new OSftp( {
host: 'custom-server.com',
port: 22,
user: 'custom-user',
password: 'custom-password'
} );
const sftpUpload = await sftpClient.uploadOne( `./folder-from/filename`, 'folder-to/filename' );
console.log( sftpUpload );
// -> { status: true, ... }
When an error happens, instead to throw an error, it's returned a managed responseKO.
responseKO is an object with 3 fields:
interface responseKO {
status: false;
error: {
msg: string; // explaining the error
code: OSFtpErrorCode; // string
// ... // other data, it depends on method error
},
tryAgain: boolean;
}
type OSFtpErrorCode =
| 'ECONNREFUSED'
| 'UNCONNECTED'
| 'ENOTFOUND'
| 'ENTIMEOUT'
| 'ENOENT'
| 'EEXIST'
| 'ENOTEMPTY';
new OSFtp( config?: OSFtpConfig );
type OSFtpConfig = SftpClient.ConnectOptions & {
host?: string;
port?: number;
user?: string;
password?: string;
readyTimeout?: number; // def: 3000
disconnectWhenError?: boolean; // def: true
}
As parameters, you can pass the server config data (or you can also do it in .connect()
).
In addition, config
has param disconnectWhenError
(default true
), so when an error happens, connection closes automatically.
const OSftp = require( 'oro-sftp' );
const config = {
host: 'custom-server.com',
port: 22,
user: 'custom-user',
password: 'custom-password',
readyTimeout: 3000,
disconnectWhenError: true
}
const sftpClient = new OSftp( config );
sftpClient.getClient(): SftpClient;
If you want to use the library ssh2-sftp-client
, you can get the object.
const sftpClient = new OSftp( config );
const ssh2SftpClient = await sftpClient.getClient();
await sftpClient.connect( config?: OSFtpConfig ) => Promise<OSFtpConnectResponse>;
type OSFtpConfig = PromiseFtp.Options & {
host?: string;
port?: number;
user?: string;
password?: string;
readyTimeout?: number; // def: 3000
disconnectWhenError?: boolean; // def: true
}
export type OSFtpConnectResponse =
| SResponseOKBasic
| SResponseKOObjectAgain<OSFtpConnectError>;
interface SResponseOKBasic {
status: true;
}
interface SResponseKOObjectAgain {
status: false;
error: {
msg: string;
code: OSFtpErrorCode;
config: OSFtpConfig;
}
}
interface OSFtpConnectError {
msg: string;
code: OSFtpErrorCode;
config: OSFtpConfig;
}
When you create a connection, it's expected that you will disconnect it later.
This method return a response, which is an object with status: true | false
.
const sftpClient = new OSftp( config );
const connected = await sftpClient.connect();
console.log( connected );
// -> { status: true }
await sftpClient.disconnect() => Promise<OSFtpDisconnectResponse>;
export type OSFtpDisconnectResponse =
| SResponseOKBasic
| SResponseKOBasic;
interface SResponseOKBasic {
status: true;
}
interface SResponseKOBasic {
status: false;
}
Note: If you don't .disconnect()
when finished, the script still running.
Note2: There is a param in config disconnectWhenError
by default true
.
This means that if a method (like upload
or move
) return status: false
, the ftpClient will be disconnected automatically.
This method return a response, which is an object with status: true | false
.
const sftpClient = new OSftp( config );
const connected = await sftpClient.connect();
// ...
const disconnected = await sftpClient.disconnect();
console.log( disconnected );
// -> { status: true }
await sftpClient.upload( filepathFrom: string, filepathTo?: string )
=> Promise<OSFtpFileResponse>;
export type OSFtpFileResponse =
| SResponseOKObject<OSFtpFileObject>
| SResponseKOObject<OSFtpFileError>;
interface SResponseOKObject {
status: true;
filename: string;
filepath: string;
}
interface SResponseKOObject {
status: false;
error: {
msg: string;
filepathFrom: string;
filepathTo?: string;
code?: OSFtpErrorCode;
}
}
interface OSFtpFileObject {
filename: string;
filepath: string;
}
interface OSFtpFileError {
msg: string;
filepathFrom: string;
filepathTo?: string;
code?: OSFtpErrorCode;
}
upload
is the action to copy from local to ftp folder.
If filepathTo
is not declared, it takes the filename of filepathFrom
and save it on ftp main folder.
const sftpClient = new OSftp( config );
const connected = await sftpClient.connect();
if( ! connected.status ) { return connected; }
const uploaded = await sftpClient.upload( './files/custom-file.pdf' );
console.log( uploaded );
// -> { status: true, filename: 'custom-file.pdf', ... }
sftpClient.disconnect();
await sftpClient.upload( filepathFrom: string, filepathTo?: string )
=> Promise<OSFtpUploadOneResponse>;
export type OSFtpUploadOneResponse =
| SResponseOKObject<OSFtpFileObject>
| SResponseKOObject<OSFtpFileError | OSFtpConnectError>;
interface SResponseOKObject {
status: true;
filename: string;
filepath: string;
}
type SResponseKOObject =
| {
status: false;
error: {
msg: string;
filepathFrom: string;
filepathTo?: string;
code?: OSFtpErrorCode;
}
}
| {
status: false;
error: {
msg: string;
code: OSFtpErrorCode;
config: OSFtpConfig;
}
}
interface OSFtpFileObject {
filename: string;
filepath: string;
}
interface OSFtpFileError {
msg: string;
filepathFrom: string;
filepathTo?: string;
code?: OSFtpErrorCode;
}
interface OSFtpConnectError {
msg: string;
code: OSFtpErrorCode;
config: OSFtpConfig;
}
If you want to upload just one file, you can use this method and inside:
const sftpClient = new OSftp( config );
const uploaded = await sftpClient.uploadOne( './files/custom-file.pdf' );
console.log( uploaded );
// -> { status: true, filename: 'custom-file.pdf', ... }
await sftpClient.download( filepathFrom: string, filepathTo?: string )
=> Promise<OSFtpFileResponse>;
export type OSFtpFileResponse =
| SResponseOKObject<OSFtpFileObject>
| SResponseKOObject<OSFtpFileError>;
interface SResponseOKObject {
status: true;
filename: string;
filepath: string;
}
interface SResponseKOObject {
status: false;
error: {
msg: string;
filepathFrom: string;
filepathTo?: string;
code?: OSFtpErrorCode;
}
}
interface OSFtpFileObject {
filename: string;
filepath: string;
}
interface OSFtpFileError {
msg: string;
filepathFrom: string;
filepathTo?: string;
code?: OSFtpErrorCode;
}
download
is the action to copy from ftp folder to local.
If filepathTo
is not declared, it takes the filename of filepathFrom
and save it on local main folder.
const sftpClient = new OSftp( config );
const connected = await sftpClient.connect();
if( ! connected.status ) { return connected; }
const downloaded = await sftpClient.download( 'custom-file.pdf' );
console.log( downloaded );
// -> { status: true, filename: 'custom-file.pdf', ... }
sftpClient.disconnect();
await sftpClient.list( folder?: string, filters?: OSFtpListFilters )
=> Promise<OSFtpListResponse>;
interface OSFtpListFilters {
onlyFiles?: boolean | undefined; // def: false
onlyFolders?: boolean | undefined; // def: false
pattern?: ListFilterFunction | undefined;
}
type ListFilterFunction = (fileInfo: FileInfo) => boolean;
export type OSFtpListResponse =
| SResponseOKObject<OSFtpListObject>
| SResponseKOObject<OSFtpListError>;
interface SResponseOKObject {
status: true;
count: number; // list.length
list: OSFtpListFile[];
}
interface SResponseKOObject {
status: false;
error: {
msg: string;
folder: string;
filters: OSFtpListFilters;
code?: OSFtpErrorCode;
}
}
export interface OSFtpListFile {
path: string;
name: string;
type: OSFtpListFileType;
date: Date;
size: number;
owner: string;
group: string;
target: string | undefined;
rights: {
user: string;
group: string;
other: string;
}
}
type OSFtpListFileType = '-' | 'd' | 'l';
// 'file' | 'folder' | 'symlink'
export interface OSFtpListObject {
count: number; // list.length
list: OSFtpListFile[];
}
export interface OSFtpListError {
msg: string;
folder: string;
filters: OSFtpListFilters;
code?: OSFtpErrorCode;
}
list
is the action to take a look at what is in ftp folder.
const sftpClient = new OSftp( config );
const connected = await sftpClient.connect();
if( ! connected.status ) { return connected; }
const files = await sftpClient.list();
console.log( files );
// -> { status: true, count: 7, list: [ ... ] }
sftpClient.disconnect();
pattern
pattern
filter can be a regular expression (most powerful option) or
a simple glob-like string where *
will match any number of characters, e.g.
foo* => foo, foobar, foobaz
*bar => bar, foobar, tabbar
*oo* => foo, foobar, look, book
response example
{
status: true,
count: // list.length
list: [
{
type: // file type(-, d, l)
name: // file name
longname: // file name as linux promp
path: // file path
date: // file date of modified time
modifyDate: // file date of modified time
accessDate: // file date of access time
size: // file size
rights: { user: 'rwx', group: 'rwx', other: 'rwx' }
owner: // user number ID
group: // group number ID
},
...
]
}
await sftpClient.move( filepathFrom: string, filepathTo?: string )
=> Promise<OSFtpFileResponse>;
export type OSFtpFileResponse =
| SResponseOKObject<OSFtpFileObject>
| SResponseKOObject<OSFtpFileError>;
interface SResponseOKObject {
status: true;
filename: string;
filepath: string;
}
interface SResponseKOObject {
status: false;
error: {
msg: string;
filepathFrom: string;
filepathTo?: string;
code?: OSFtpErrorCode;
}
}
interface OSFtpFileObject {
filename: string;
filepath: string;
}
interface OSFtpFileError {
msg: string;
filepathFrom: string;
filepathTo?: string;
code?: OSFtpErrorCode;
}
move
is the action to move from ftp folder to ftp folder (or event rename).
const sftpClient = new OSftp( config );
const connected = await sftpClient.connect();
if( ! connected.status ) { return connected; }
const moved = await sftpClient.move( 'custom-file.pdf', 'backup/custom-file.pdf' );
console.log( moved );
// -> { status: true, filename: 'custom-file.pdf', ... }
sftpClient.disconnect();
await sftpClient.delete( filepathFrom: string, strict?: boolean )
=> Promise<OSFtpFileResponse>;
export type OSFtpFileResponse =
| SResponseOKObject<OSFtpFileObject>
| SResponseKOObject<OSFtpFileError>;
interface SResponseOKObject {
status: true;
filename: string;
filepath: string;
}
interface SResponseKOObject {
status: false;
error: {
msg: string;
filepathFrom: string;
code?: OSFtpErrorCode;
}
}
interface OSFtpFileObject {
filename: string;
filepath: string;
}
interface OSFtpFileError {
msg: string;
filepathFrom: string;
code?: OSFtpErrorCode;
}
delete
is the action to remove a file from ftp folder.
When strict = false
and not found the file, it returns { status: true }
.
const sftpClient = new OSftp( config );
const connected = await sftpClient.connect();
if( ! connected.status ) { return connected; }
const deleted = await sftpClient.delete( 'custom-file.pdf' );
console.log( deleted );
// -> { status: true, filename: 'custom-file.pdf', ... }
sftpClient.disconnect();
await sftpClient.exists( filepathFrom: string, disconnectWhenError?: boolean )
=> Promise<OSFtpExistResponse>;
export type OSFtpExistResponse =
| SResponseOKObject<OSFtpExistObject>
| SResponseKOObject<OSFtpExistError>;
interface SResponseOKObject {
status: true;
filename: string;
filepath: string;
type: string;
}
interface SResponseKOObject {
status: false;
error: {
msg: string;
filename: string;
filepath: string;
code?: OSFtpErrorCode;
}
}
interface OSFtpExistObject {
filename: string;
filepath: string;
type: string;
}
interface OSFtpExistError {
msg: string;
filename: string;
filepath: string;
code?: OSFtpErrorCode;
}
exists
is the action to check if a file or folder exists in ftp folder.
const sftpClient = new OSftp( config );
const connected = await sftpClient.connect();
if( ! connected.status ) { return connected; }
const exists = await sftpClient.exists( 'custom-file.pdf' );
console.log( exists );
// -> { status: true, filename: 'custom-file.pdf', type: 'd' ... }
sftpClient.disconnect();
await sftpClient.mkdir( folder, recursive?: boolean, strict?: boolean )
=> Promise<OSFtpFolderResponse>;
export type OSFtpFolderResponse =
| SResponseOKObject<OSFtpFolderObject>
| SResponseKOObject<OSFtpFolderError>;
interface SResponseOKObject {
status: true;
foldername: string;
folderpath: string;
}
interface SResponseKOObject {
status: false;
error: {
msg: string;
filepathFrom: string;
code?: OSFtpErrorCode;
}
}
interface OSFtpFolderObject {
foldername: string;
folderpath: string;
}
interface OSFtpFolderError {
msg: string;
folder: string;
code?: OSFtpErrorCode;
}
mkdir
is the action to create folders in ftp folder.
When recursive = true
it allows to create the subfolders too.
When strict = false
and folder already exist, it returns { status: true }
.
const sftpClient = new OSftp( config );
const connected = await sftpClient.connect();
if( ! connected.status ) { return connected; }
const created = await sftpClient.mkdir( 'custom-folder/custom-subfolder' );
console.log( created );
// -> { status: true, foldername: 'custom-subfolder', ... }
sftpClient.disconnect();
await sftpClient.rmdir( folder, recursive?: boolean, strict?: boolean )
=> Promise<OSFtpFolderResponse>;
export type OSFtpFolderResponse =
| SResponseOKObject<OSFtpFolderObject>
| SResponseKOObject<OSFtpFolderError>;
interface SResponseOKObject {
status: true;
foldername: string;
folderpath: string;
}
interface SResponseKOObject {
status: false;
error: {
msg: string;
filepathFrom: string;
code?: OSFtpErrorCode;
}
}
interface OSFtpFolderObject {
foldername: string;
folderpath: string;
}
interface OSFtpFolderError {
msg: string;
folder: string;
code?: OSFtpErrorCode;
}
rmdir
is the action to remove folders in ftp folder.
When recursive = true
it allows to remove the folder-content too.
When strict = false
and not found the folder, it returns { status: true }
.
const sftpClient = new OSftp( config );
const connected = await sftpClient.connect();
if( ! connected.status ) { return connected; }
const removed = await sftpClient.rmdir( 'custom-folder', true );
console.log( removed );
// -> { status: true, foldername: 'custom-folder', ... }
sftpClient.disconnect();
If you want to run npm run test
in local, first you need to run a sftp server (i.e. via docker):
# 'atmoz/sftp:alpine' is smaller and faster
> docker run --name oro-sftp-server -p 2222:22 -d atmoz/sftp:alpine -e osftp_user:osftp_pass:::osftp_folder
# change the login-folder to have full privileges in main folder
> docker exec oro-sftp-server sh -c "sed -i -e 's#ForceCommand internal-sftp#ForceCommand internal-sftp -d /osftp_folder#' /etc/ssh/sshd_config"
# restart container
> docker restart oro-sftp-server
Then, you have to declare your own ./test/config.json
,
You can copypaste it from ./test/config-default.json
{
"host": "localhost",
"port": 2222,
"user": "osftp_user",
"password": "osftp_pass"
}
2.1.0 / 2024-07-23
package-lock.json
.eslint.config.js
.OSftp.list
to return always a sortened list by name.actions/--@v3
by actions/--@v4
, and replacing npm install
to npm ci
.export
declarations in index files.printWidth: 120
tsup.config.ts
.src
and simplified *.test.ts
.987%
of coverage (yay!).fs-extra
from v11.1.1
to v11.2.0
.oro-functions
from v2.0.2
to v2.3.1
.ssh2-sftp-client
from v9.1.0
to v10.0.3
.@eslint/js
@trivago/prettier-plugin-sort-imports
eslint-config-prettier
eslint-plugin-jest
globals
nodemon
typescript-eslint
@babel/core
from v7.23.3
to v7.24.9
.@babel/preset-env
from v7.23.
to v7.24.8
.@babel/preset-typescript
from v7.23.3
to v7.24.7
.@types/jest
from v29.5.10
to v29.5.12
.eslint
from v8.54.0
to v^^8.57.0
.eslint-plugin-unicorn
from v49.0.0
to v54.0.0
.husky
from v8.0.3
to v9.1.1
.prettier
from v3.1.0
to v3.3.3
.tsup
from v8.0.1
to v8.2.2
.typescript
from v5.2.2
to v5.5.4
.@typescript-eslint/eslint-plugin
removed.@typescript-eslint/parser
removed.eslint-config-alloy
removed.FAQs
OroSftp Class is a wrapper of ssh2-sftp-client to work as promises async/await and typescript.
The npm package oro-sftp receives a total of 1 weekly downloads. As such, oro-sftp popularity was classified as not popular.
We found that oro-sftp demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Solo open source maintainers face burnout and security challenges, with 60% unpaid and 60% considering quitting.
Security News
License exceptions modify the terms of open source licenses, impacting how software can be used, modified, and distributed. Developers should be aware of the legal implications of these exceptions.
Security News
A developer is accusing Tencent of violating the GPL by modifying a Python utility and changing its license to BSD, highlighting the importance of copyleft compliance.