{"id":13606247,"url":"https://github.com/QuorumDMS/ftp-srv","last_synced_at":"2025-04-12T08:30:44.121Z","repository":{"id":37014127,"uuid":"83277517","full_name":"QuorumDMS/ftp-srv","owner":"QuorumDMS","description":"📮 Modern FTP Server","archived":false,"fork":false,"pushed_at":"2024-05-13T19:43:52.000Z","size":1965,"stargazers_count":379,"open_issues_count":47,"forks_count":110,"subscribers_count":16,"default_branch":"main","last_synced_at":"2024-05-19T20:02:46.369Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/QuorumDMS.png","metadata":{"files":{"readme":"README.md","changelog":"changelog/v2_to_v3_migation.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-02-27T06:45:10.000Z","updated_at":"2024-06-18T12:36:30.169Z","dependencies_parsed_at":"2024-01-16T23:30:10.709Z","dependency_job_id":"ef2ac9f1-9ae9-4a69-b05f-eb6d32a2f7df","html_url":"https://github.com/QuorumDMS/ftp-srv","commit_stats":null,"previous_names":["autovance/ftp-srv","trs/ftp-srv"],"tags_count":74,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/QuorumDMS%2Fftp-srv","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/QuorumDMS%2Fftp-srv/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/QuorumDMS%2Fftp-srv/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/QuorumDMS%2Fftp-srv/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/QuorumDMS","download_url":"https://codeload.github.com/QuorumDMS/ftp-srv/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248539755,"owners_count":21121228,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T19:01:07.538Z","updated_at":"2025-04-12T08:30:39.111Z","avatar_url":"https://github.com/QuorumDMS.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","Networking Protocols"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/autovance/ftp-srv\"\u003e\n    \u003cimg alt=\"ftp-srv\" src=\"logo.png\" width=\"600px\"  /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\n\u003cp align=\"center\"\u003e\n  Modern, extensible FTP Server\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.com/package/ftp-srv\"\u003e\n    \u003cimg alt=\"npm\" src=\"https://img.shields.io/npm/dm/ftp-srv.svg?style=for-the-badge\" /\u003e\n  \u003c/a\u003e\n\n  \u003ca href=\"https://circleci.com/gh/autovance/workflows/ftp-srv/tree/master\"\u003e\n    \u003cimg alt=\"circleci\" src=\"https://img.shields.io/circleci/project/github/autovance/ftp-srv/master.svg?style=for-the-badge\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## Overview\n`ftp-srv` is a modern and extensible FTP server designed to be simple yet configurable.\n\n## Features\n- Extensible [file systems](#file-system) per connection\n- Passive and active transfers\n- [Explicit](https://en.wikipedia.org/wiki/FTPS#Explicit) \u0026 [Implicit](https://en.wikipedia.org/wiki/FTPS#Implicit) TLS connections\n- Promise based API\n\n## Install\n`npm install ftp-srv --save`\n\n## Usage\n\n```js\n// Quick start, create an active ftp server.\nconst FtpSrv = require('ftp-srv');\n\nconst port=21;\nconst ftpServer = new FtpSrv({\n    url: \"ftp://0.0.0.0:\" + port,\n    anonymous: true\n});\n\nftpServer.on('login', ({ connection, username, password }, resolve, reject) =\u003e { \n    if(username === 'anonymous' \u0026\u0026 password === 'anonymous'){\n        return resolve({ root:\"/\" });    \n    }\n    return reject(new errors.GeneralError('Invalid username or password', 401));\n});\n\nftpServer.listen().then(() =\u003e { \n    console.log('Ftp server is starting...')\n});\n```\n\n## API\n\n### `new FtpSrv({options})`\n#### url\n[URL string](https://nodejs.org/api/url.html#url_url_strings_and_url_objects) indicating the protocol, hostname, and port to listen on for connections.\nSupported protocols:\n- `ftp` Plain FTP\n- `ftps` Implicit FTP over TLS\n\n_Note:_ The hostname must be the external IP address to accept external connections. `0.0.0.0` will listen on any available hosts for server and passive connections.  \n__Default:__ `\"ftp://127.0.0.1:21\"`\n\n#### `pasv_url`\n`FTP-srv` provides an IP address to the client when a `PASV` command is received in the handshake for a passive connection. Reference [PASV verb](https://cr.yp.to/ftp/retr.html#pasv). This can be one of two options:\n- A function which takes one parameter containing the remote IP address of the FTP client. This can be useful when the user wants to return a different IP address depending if the user is connecting from Internet or from an LAN address.\nExample:\n ```js\nconst { networkInterfaces } = require('os');\nconst { Netmask } = require('netmask');\n\nconst nets = networkInterfaces();\nfunction getNetworks() {\n    let networks = {};\n    for (const name of Object.keys(nets)) {\n        for (const net of nets[name]) {\n            if (net.family === 'IPv4' \u0026\u0026 !net.internal) {\n                networks[net.address + \"/24\"] = net.address\n            }\n        }\n    }\n    return networks;\n}\n\nconst resolverFunction = (address) =\u003e {\n    // const networks = {\n    //     '$GATEWAY_IP/32': `${public_ip}`, \n    //     '10.0.0.0/8'    : `${lan_ip}`\n    // } \n    const networks = getNetworks();\n    for (const network in networks) {\n        if (new Netmask(network).contains(address)) {\n            return networks[network];\n        }\n    }\n    return \"127.0.0.1\";\n}\n\nnew FtpSrv({pasv_url: resolverFunction});\n```\n\n- A static IP address (ie. an external WAN **IP address** that the FTP server is bound to). In this case, only connections from localhost are handled differently returning `127.0.0.1` to the client. \n\nIf not provided, clients can only connect using an `Active` connection.\n\n#### `pasv_min`\nThe starting port to accept passive connections.  \n__Default:__ `1024`\n\n#### `pasv_max`\nThe ending port to accept passive connections.  \nThe range is then queried for an available port to use when required.  \n__Default:__ `65535`\n\n#### `greeting`\nA human readable array of lines or string to send when a client connects.  \n__Default:__ `null`\n\n#### `tls`\nNode [TLS secure context object](https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options) used for implicit (`ftps` protocol) or explicit (`AUTH TLS`) connections.  \n__Default:__ `false`\n\n#### `anonymous`\nIf true, will allow clients to authenticate using the username `anonymous`, not requiring a password from the user.  \nCan also set as a string which allows users to authenticate using the username provided.  \nThe `login` event is then sent with the provided username and `@anonymous` as the password.  \n__Default:__ `false`\n\n#### `blacklist`\nArray of commands that are not allowed.  \nResponse code `502` is sent to clients sending one of these commands.  \n__Example:__ `['RMD', 'RNFR', 'RNTO']` will not allow users to delete directories or rename any files.  \n__Default:__ `[]`\n\n#### `whitelist`\nArray of commands that are only allowed.  \nResponse code `502` is sent to clients sending any other command.  \n__Default:__ `[]`\n\n#### `file_format`\nSets the format to use for file stat queries such as `LIST`.  \n__Default:__ `\"ls\"`  \n__Allowable values:__\n  - `ls` [bin/ls format](https://cr.yp.to/ftp/list/binls.html)\n  - `ep` [Easily Parsed LIST format](https://cr.yp.to/ftp/list/eplf.html)\n  - `function () {}` A custom function returning a format or promise for one.\n    - Only one argument is passed in: a node [file stat](https://nodejs.org/api/fs.html#fs_class_fs_stats) object with additional file `name` parameter\n\n#### `log`\nA [bunyan logger](https://github.com/trentm/node-bunyan) instance. Created by default.\n\n#### `timeout`\nSets the timeout (in ms) after that an idle connection is closed by the server  \n__Default:__ `0`\n\n## CLI\n\n`ftp-srv` also comes with a builtin CLI.\n\n```bash\n$ ftp-srv [url] [options]\n```\n\n```bash\n$ ftp-srv ftp://0.0.0.0:9876 --root ~/Documents\n```\n\n#### `url`\nSet the listening URL.\n\nDefaults to `ftp://127.0.0.1:21`\n\n#### `--pasv_url`\nThe hostname to provide a client when attempting a passive connection (`PASV`).  \nIf not provided, clients can only connect using an `Active` connection.\n\n#### `--pasv_min`\nThe starting port to accept passive connections.  \n__Default:__ `1024`\n\n#### `--pasv_max`\nThe ending port to accept passive connections.  \nThe range is then queried for an available port to use when required.  \n__Default:__ `65535`\n\n#### `--root` / `-r`\nSet the default root directory for users.\n\nDefaults to the current directory.\n\n#### `--credentials` / `-c`\nSet the path to a json credentials file.\n\nFormat:\n\n```js\n[\n  {\n    \"username\": \"...\",\n    \"password\": \"...\",\n    \"root\": \"...\" // Root directory\n  },\n  ...\n]\n```\n\n#### `--username`\nSet the username for the only user. Do not provide an argument to allow anonymous login.\n\n#### `--password`\nSet the password for the given `username`.\n\n#### `--read-only`\nDisable write actions such as upload, delete, etc.\n\n## Events\n\nThe `FtpSrv` class extends the [node net.Server](https://nodejs.org/api/net.html#net_class_net_server). Some custom events can be resolved or rejected, such as `login`.\n\n### `client-error`\n```js\nftpServer.on('client-error', ({connection, context, error}) =\u003e { ... });\n```\n\nOccurs when an error arises in the client connection.\n\n`connection` [client class object](src/connection.js)  \n`context` string of where the error occurred  \n`error` error object\n\n### `disconnect`\n```js\nftpServer.on('disconnect', ({connection, id, newConnectionCount}) =\u003e { ... });\n```\n\nOccurs when a client has disconnected.\n\n`connection` [client class object](src/connection.js)  \n`id` string of the disconnected connection id  \n`id` number of the new connection count (exclusive the disconnected client connection)\n\n### `closed`\n```js\nftpServer.on('closed', ({}) =\u003e { ... });\n```\n\nOccurs when the FTP server has been closed.\n\n### `closing`\n```js\nftpServer.on('closing', ({}) =\u003e { ... });\n```\n\nOccurs when the FTP server has started closing.\n\n### `login`\n```js\nftpServer.on('login', ({connection, username, password}, resolve, reject) =\u003e { ... });\n```\n\nOccurs when a client is attempting to login. Here you can resolve the login request by username and password.\n\n`connection` [client class object](src/connection.js)  \n`username` string of username from `USER` command  \n`password` string of password from `PASS` command  \n`resolve` takes an object of arguments:  \n- `fs`\n  - Set a custom file system class for this connection to use.\n  - See [File System](#file-system) for implementation details.\n- `root`\n  - If `fs` is not provided, this will set the root directory for the connection.\n  - The user cannot traverse lower than this directory.\n- `cwd`\n  - If `fs` is not provided, will set the starting directory for the connection\n  - This is relative to the `root` directory.\n- `blacklist`\n  - Commands that are forbidden for only this connection\n- `whitelist`\n  - If set, this connection will only be able to use the provided commands\n\n`reject` takes an error object\n\n### `server-error`\n```js\nftpServer.on('server-error', ({error}) =\u003e { ... });\n```\n\nOccurs when an error arises in the FTP server.\n \n`error` error object\n\n### `RETR`\n```js\nconnection.on('RETR', (error, filePath) =\u003e { ... });\n```\n\nOccurs when a file is downloaded.\n\n`error` if successful, will be `null`  \n`filePath` location to which file was downloaded\n\n### `STOR`\n```js\nconnection.on('STOR', (error, fileName) =\u003e { ... });\n```\n\nOccurs when a file is uploaded.\n\n`error` if successful, will be `null`  \n`fileName` name of the file that was uploaded\n\n### `RNTO`\n```js\nconnection.on('RNTO', (error, fileName) =\u003e { ... });\n```\n\nOccurs when a file is renamed.\n\n`error` if successful, will be `null`  \n`fileName` name of the file that was renamed\n\n## Supported Commands\n\nSee the [command registry](src/commands/registration) for a list of all implemented FTP commands.\n\n## File System\nThe default [file system](src/fs.js) can be overwritten to use your own implementation.  \nThis can allow for virtual file systems, and more.  \nEach connection can set it's own file system based on the user.  \n\nThe default file system is exported and can be extended as needed:  \n```js\nconst {FtpSrv, FileSystem} = require('ftp-srv');\n\nclass MyFileSystem extends FileSystem {\n  constructor() {\n    super(...arguments);\n  }\n\n  get(fileName) {\n    ...\n  }\n}\n```\n\nCustom file systems can implement the following variables depending on the developers needs:\n\n### Methods\n#### [`currentDirectory()`](src/fs.js#L40)\nReturns a string of the current working directory  \n__Used in:__ `PWD`\n\n#### [`get(fileName)`](src/fs.js#L44)\nReturns a file stat object of file or directory  \n__Used in:__ `LIST`, `NLST`, `STAT`, `SIZE`, `RNFR`, `MDTM`\n\n#### [`list(path)`](src/fs.js#L50)\nReturns array of file and directory stat objects  \n__Used in:__ `LIST`, `NLST`, `STAT`\n\n#### [`chdir(path)`](src/fs.js#L67)\nReturns new directory relative to current directory  \n__Used in:__ `CWD`, `CDUP`\n\n#### [`mkdir(path)`](src/fs.js#L114)\nReturns a path to a newly created directory  \n__Used in:__ `MKD`\n\n#### [`write(fileName, {append, start})`](src/fs.js#L79)\nReturns a writable stream  \nOptions:  \n `append` if true, append to existing file  \n `start` if set, specifies the byte offset to write to  \n__Used in:__ `STOR`, `APPE`\n\n#### [`read(fileName, {start})`](src/fs.js#L90)\nReturns a readable stream  \nOptions:  \n `start` if set, specifies the byte offset to read from  \n__Used in:__ `RETR`\n\n#### [`delete(path)`](src/fs.js#L105)\nDelete a file or directory  \n__Used in:__ `DELE`\n\n#### [`rename(from, to)`](src/fs.js#L120)\nRenames a file or directory  \n__Used in:__ `RNFR`, `RNTO`\n\n#### [`chmod(path)`](src/fs.js#L126)\nModifies a file or directory's permissions  \n__Used in:__ `SITE CHMOD`\n\n#### [`getUniqueName(fileName)`](src/fs.js#L131)\nReturns a unique file name to write to. Client requested filename available if you want to base your function on it. \n__Used in:__ `STOU`\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## License\n\nThis software is licensed under the MIT Licence. See [LICENSE](LICENSE).\n\n## References\n\n- [https://cr.yp.to/ftp.html](https://cr.yp.to/ftp.html)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FQuorumDMS%2Fftp-srv","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FQuorumDMS%2Fftp-srv","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FQuorumDMS%2Fftp-srv/lists"}