ftpd-lite 1.0.10 → 1.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -80
- package/package.json +4 -1
- package/src/cli/banner.d.ts +6 -0
- package/src/cli/banner.js +17 -0
- package/src/cli/context.d.ts +10 -0
- package/src/cli/context.js +13 -0
- package/src/cli/registry.d.ts +7 -0
- package/src/cli/registry.js +131 -0
- package/src/cli/repl.d.ts +2 -0
- package/src/cli/repl.js +92 -0
- package/src/core/FtpServer.d.ts +10 -4
- package/src/core/FtpServer.js +145 -26
- package/src/db/database.d.ts +54 -0
- package/src/db/database.js +252 -0
- package/src/ftpd.d.ts +2 -0
- package/src/ftpd.js +16 -0
- package/src/index.d.ts +2 -1
- package/src/index.js +4 -2
- package/src/types.d.ts +14 -28
- package/src/types.js +1 -1
- package/src/userManager.d.ts +9 -8
- package/src/userManager.js +98 -82
package/README.md
CHANGED
|
@@ -1,39 +1,29 @@
|
|
|
1
1
|
# ftpd-lite
|
|
2
2
|
 
|
|
3
3
|
|
|
4
|
-
|
|
5
4
|
Simple and lightweight FTP server written in Node.js + TypeScript.
|
|
6
5
|
* multiple users
|
|
7
6
|
* permission control
|
|
8
7
|
* passive port support
|
|
9
|
-
|
|
8
|
+
* SQLite persistence (`ftpd.db`)
|
|
10
9
|
|
|
11
10
|
## Installation
|
|
11
|
+
|
|
12
12
|
```bash
|
|
13
13
|
npm install ftpd-lite
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
## Quick Start
|
|
17
|
-
|
|
17
|
+
|
|
18
|
+
The package is imported as a library (`require` / `import`). The listening process starts when you call `start()`.
|
|
18
19
|
|
|
19
20
|
```js
|
|
20
21
|
const { FtpServer } = require('ftpd-lite');
|
|
21
22
|
|
|
22
23
|
const server = new FtpServer({
|
|
23
|
-
|
|
24
|
-
port: 21,
|
|
25
|
-
dataPort: 20,
|
|
26
|
-
userFile: './users.json',
|
|
27
|
-
maxConnections: 100,
|
|
28
|
-
logLevel: 'info',
|
|
29
|
-
|
|
30
|
-
// 220 welcome message (banner).
|
|
31
|
-
// If left empty, it automatically writes "<port> FTP Server Ready".
|
|
24
|
+
dbPath: './ftpd.db', // SQLite database (users + settings tables)
|
|
32
25
|
banner: '',
|
|
33
|
-
|
|
34
|
-
// PASV (passive mode) data connection ports are selected from this range.
|
|
35
|
-
// It must be the same as the range that needs to be opened in the firewall.
|
|
36
|
-
passivePortRange: { min: 50000, max: 50050 },
|
|
26
|
+
logLevel: 'info',
|
|
37
27
|
});
|
|
38
28
|
|
|
39
29
|
server.start();
|
|
@@ -45,61 +35,53 @@ TypeScript:
|
|
|
45
35
|
import { FtpServer, type ServerConfig } from 'ftpd-lite';
|
|
46
36
|
|
|
47
37
|
const config: ServerConfig = {
|
|
48
|
-
|
|
49
|
-
dataPort: 20,
|
|
50
|
-
userFile: './users.json',
|
|
51
|
-
maxConnections: 100,
|
|
52
|
-
logLevel: 'info',
|
|
38
|
+
dbPath: './ftpd.db',
|
|
53
39
|
banner: 'My FTP Server',
|
|
54
|
-
|
|
40
|
+
logLevel: 'info',
|
|
55
41
|
};
|
|
56
42
|
|
|
57
43
|
new FtpServer(config).start();
|
|
58
44
|
```
|
|
59
45
|
|
|
60
|
-
##
|
|
46
|
+
## Configuration
|
|
47
|
+
|
|
48
|
+
Only `dbPath`, `banner` and `logLevel` are part of the public `ServerConfig`.
|
|
49
|
+
All other settings are read from the SQLite `settings` table.
|
|
61
50
|
|
|
62
|
-
|
|
|
51
|
+
| Key | Default | Description |
|
|
63
52
|
| --- | --- | --- |
|
|
64
|
-
| `port` | `21` | Control (command) port |
|
|
65
|
-
| `dataPort` | `20` | Classic data port |
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `passivePortRange` | `{ min: 50000, max: 50050 }` | Range from which PASV data ports will be selected (fallback) |
|
|
53
|
+
| `ftp.port` | `21` | Control (command) port |
|
|
54
|
+
| `ftp.dataPort` | `20` | Classic data port |
|
|
55
|
+
| `ftp.maxConnections` | `100` | Maximum number of open connections at the same time |
|
|
56
|
+
| `ftp.logLevel` | `'info'` | `debug` \| `info` \| `warn` \| `error` |
|
|
57
|
+
| `ftp.banner` | `''` | 220 welcome message; if empty, `"<port> FTP Server Ready"` |
|
|
58
|
+
| `ftp.passivePortRange` | `'50000:50050'` | PASV range as `min:max` |
|
|
71
59
|
|
|
72
60
|
If an invalid `passivePortRange` is provided, a warning is logged and the default range is used.
|
|
73
61
|
|
|
74
|
-
##
|
|
75
|
-
|
|
76
|
-
```json
|
|
77
|
-
{
|
|
78
|
-
"users": [
|
|
79
|
-
{
|
|
80
|
-
"username": "admin",
|
|
81
|
-
"password": "admin123",
|
|
82
|
-
"homeDir": "./files/admin",
|
|
83
|
-
"permissions": {
|
|
84
|
-
"read": true,
|
|
85
|
-
"write": true,
|
|
86
|
-
"list": true,
|
|
87
|
-
"create": true,
|
|
88
|
-
"delete": true,
|
|
89
|
-
"mkd": true,
|
|
90
|
-
"rmdir": true,
|
|
91
|
-
"rename": true
|
|
92
|
-
},
|
|
93
|
-
"isActive": true
|
|
94
|
-
}
|
|
95
|
-
],
|
|
96
|
-
"lastUpdated": "2026-10-29T12:30:00.000Z"
|
|
97
|
-
},
|
|
98
|
-
{...}
|
|
99
|
-
```
|
|
62
|
+
## Database
|
|
100
63
|
|
|
101
|
-
|
|
102
|
-
|
|
64
|
+
The server stores all data in a SQLite database file (default: `./ftpd.db`).
|
|
65
|
+
|
|
66
|
+
- `users` table: user credentials, home directory and permissions (Linux style, e.g. `rwxr-xr-x`).
|
|
67
|
+
- `settings` table: server settings (`ftp.*` keys).
|
|
68
|
+
|
|
69
|
+
## User
|
|
70
|
+
|
|
71
|
+
Users are persisted in the SQLite `users` table.
|
|
72
|
+
|
|
73
|
+
```sql
|
|
74
|
+
CREATE TABLE users (
|
|
75
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
76
|
+
username TEXT UNIQUE NOT NULL,
|
|
77
|
+
password TEXT NOT NULL,
|
|
78
|
+
homeDir TEXT NOT NULL,
|
|
79
|
+
permissions TEXT NOT NULL DEFAULT '{}',
|
|
80
|
+
isActive INTEGER NOT NULL DEFAULT 1,
|
|
81
|
+
createdAt TEXT NOT NULL DEFAULT (datetime('now')),
|
|
82
|
+
updatedAt TEXT NOT NULL DEFAULT (datetime('now'))
|
|
83
|
+
);
|
|
84
|
+
```
|
|
103
85
|
|
|
104
86
|
## Supported commands
|
|
105
87
|
|
|
@@ -109,28 +91,11 @@ the client cannot go outside this directory (CWD/RETR/STOR/DELE/MKD/RMD/RNFR/RNT
|
|
|
109
91
|
> TLS/SSL (`AUTH TLS`), `MLSD`/`MLST`, `SIZE` and `MDTM` are not yet implemented and
|
|
110
92
|
> are not advertised in the `FEAT` response.
|
|
111
93
|
|
|
112
|
-
##
|
|
94
|
+
## CLI / Interactive terminal
|
|
113
95
|
|
|
114
|
-
|
|
115
|
-
npm run type-check # tsc --noEmit
|
|
116
|
-
npm run build # src -> npm_build (index.js + index.d.ts)
|
|
117
|
-
npm start # runs the example server examples/basic.js
|
|
118
|
-
npm test # test client test/ftp-client.js
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
To see the package contents before publishing:
|
|
96
|
+
The `ftpd` binary starts an interactive FTP terminal:
|
|
122
97
|
|
|
123
98
|
```bash
|
|
124
|
-
|
|
99
|
+
npx ftpd
|
|
100
|
+
$: ftpd
|
|
125
101
|
```
|
|
126
|
-
|
|
127
|
-
## docker-Installation
|
|
128
|
-
```bash
|
|
129
|
-
docker pull asymmetricstar/docker-ftpd:latest
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
```bash
|
|
133
|
-
docker run -d -p 21:21 -p 20:20 -p 50000-50050:50000-50050 \
|
|
134
|
-
-e FTP_DEFAULT_USER=admin -e FTP_DEFAULT_PASS=admin123 \
|
|
135
|
-
--name docker-ftpd asymmetricstar/docker-ftpd:latest
|
|
136
|
-
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ftpd-lite",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.15",
|
|
4
4
|
"description": "TCP-based FTP server with multi-user support and permission control",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
},
|
|
13
13
|
"./package.json": "./package.json"
|
|
14
14
|
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"ftpd": "src/ftpd.js"
|
|
17
|
+
},
|
|
15
18
|
"files": [
|
|
16
19
|
"src",
|
|
17
20
|
"examples"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderFtpPrompt = renderFtpPrompt;
|
|
4
|
+
exports.renderFtpBanner = renderFtpBanner;
|
|
5
|
+
const SERVER_NAME = 'ftpd';
|
|
6
|
+
function renderFtpPrompt(info) {
|
|
7
|
+
const user = info.user || 'anonymous';
|
|
8
|
+
const dir = info.currentDir || '~';
|
|
9
|
+
return `${user}@${SERVER_NAME}:${dir} $`;
|
|
10
|
+
}
|
|
11
|
+
function renderFtpBanner(port) {
|
|
12
|
+
return [
|
|
13
|
+
`FTP server ${SERVER_NAME} ready`,
|
|
14
|
+
`Listening on port ${port}`,
|
|
15
|
+
].join('\n');
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFubmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NsaS9iYW5uZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7OztBQVdBLE1BQU0sV0FBVyxHQUFHLE1BQU0sQ0FBQztBQUszQix5QkFBZ0MsSUFBbUI7SUFDakQsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksSUFBSSxXQUFXLENBQUM7SUFDdEMsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLFVBQVUsSUFBSSxHQUFHLENBQUM7SUFFbkMsT0FBTyxHQUFHLElBQUksSUFBSSxXQUFXLElBQUksR0FBRyxJQUFJLENBQUM7QUFDM0MsQ0FBQztBQUtELHlCQUFnQyxJQUFZO0lBQzFDLE9BQU87UUFDTCxjQUFjLFdBQVcsUUFBUTtRQUNqQyxxQkFBcUIsSUFBSSxFQUFFO0tBQzVCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0FBQ2YsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogRlRQIENMSSBwcm9tcHQgdmUgYmFubmVyIMO8cmV0aWNpbGVyaVxuICpcbiAqIMOWcm5layBwcm9tcHQ6XG4gKiAgIGFkbWluQGZ0cGQ6fi9hZG1pbiAkXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRnRwUHJvbXB0SW5mbyB7XG4gIHVzZXI6IHN0cmluZyB8IG51bGw7XG4gIGN1cnJlbnREaXI6IHN0cmluZztcbn1cblxuY29uc3QgU0VSVkVSX05BTUUgPSAnZnRwZCc7XG5cbi8qKlxuICogQWt0aWYga3VsbGFuxLFjxLF5YSBnw7ZyZSBwcm9tcHQgw7xyZXRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlbmRlckZ0cFByb21wdChpbmZvOiBGdHBQcm9tcHRJbmZvKTogc3RyaW5nIHtcbiAgY29uc3QgdXNlciA9IGluZm8udXNlciB8fCAnYW5vbnltb3VzJztcbiAgY29uc3QgZGlyID0gaW5mby5jdXJyZW50RGlyIHx8ICd+JztcbiAgLy8gS3VsbGFuxLFjxLFAcHJvZ3JhbTpkaXppbiAkXG4gIHJldHVybiBgJHt1c2VyfUAke1NFUlZFUl9OQU1FfToke2Rpcn0gJGA7XG59XG5cbi8qKlxuICogU3VudWN1IGJhbm5lcifEsVxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVuZGVyRnRwQmFubmVyKHBvcnQ6IG51bWJlcik6IHN0cmluZyB7XG4gIHJldHVybiBbXG4gICAgYEZUUCBzZXJ2ZXIgJHtTRVJWRVJfTkFNRX0gcmVhZHlgLFxuICAgIGBMaXN0ZW5pbmcgb24gcG9ydCAke3BvcnR9YCxcbiAgXS5qb2luKCdcXG4nKTtcbn1cbiJdfQ==
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FtpServer } from '../core/FtpServer';
|
|
2
|
+
import { FtpConnection } from '../types';
|
|
3
|
+
export interface FtpCliContext {
|
|
4
|
+
server: FtpServer;
|
|
5
|
+
user: string | null;
|
|
6
|
+
homeDir: string;
|
|
7
|
+
currentDir: string;
|
|
8
|
+
connection: FtpConnection | null;
|
|
9
|
+
}
|
|
10
|
+
export declare function createFtpContext(server: FtpServer): FtpCliContext;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createFtpContext = createFtpContext;
|
|
4
|
+
function createFtpContext(server) {
|
|
5
|
+
return {
|
|
6
|
+
server,
|
|
7
|
+
user: null,
|
|
8
|
+
homeDir: '',
|
|
9
|
+
currentDir: '',
|
|
10
|
+
connection: null,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29udGV4dC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jbGkvY29udGV4dC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFvQkEsMEJBQWlDLE1BQWlCO0lBQ2hELE9BQU87UUFDTCxNQUFNO1FBQ04sSUFBSSxFQUFFLElBQUk7UUFDVixPQUFPLEVBQUUsRUFBRTtRQUNYLFVBQVUsRUFBRSxFQUFFO1FBQ2QsVUFBVSxFQUFFLElBQUk7S0FDakIsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBGdHBTZXJ2ZXIgfSBmcm9tICcuLi9jb3JlL0Z0cFNlcnZlcic7XG5pbXBvcnQgeyBGdHBDb25uZWN0aW9uIH0gZnJvbSAnLi4vdHlwZXMnO1xuXG4vKipcbiAqIEZUUCBDTEkgKGludGVyYWt0aWYgdGVybWluYWwpIGJhxJ9sYW3EsVxuICovXG5leHBvcnQgaW50ZXJmYWNlIEZ0cENsaUNvbnRleHQge1xuICBzZXJ2ZXI6IEZ0cFNlcnZlcjtcbiAgLyoqIEFrdGlmIEZUUCBrdWxsYW7EsWPEsSBhZMSxIChudWxsIC0+IGtpbWxpayBkb8SfcnVsYW1hc8SxIGJla2xlbml5b3IpICovXG4gIHVzZXI6IHN0cmluZyB8IG51bGw7XG4gIC8qKiBBa3RpZiBrdWxsYW7EsWPEsW7EsW4gaG9tZSBkaXppbmkgKi9cbiAgaG9tZURpcjogc3RyaW5nO1xuICAvKiogQWt0aWYga3VsbGFuxLFjxLFuxLFuIMWfdSBhbmtpIGRpemluaSAqL1xuICBjdXJyZW50RGlyOiBzdHJpbmc7XG4gIGNvbm5lY3Rpb246IEZ0cENvbm5lY3Rpb24gfCBudWxsO1xufVxuXG4vKipcbiAqIFllbmkgQ0xJIGJhxJ9sYW3EsSBvbHXFn3R1clxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlRnRwQ29udGV4dChzZXJ2ZXI6IEZ0cFNlcnZlcik6IEZ0cENsaUNvbnRleHQge1xuICByZXR1cm4ge1xuICAgIHNlcnZlcixcbiAgICB1c2VyOiBudWxsLFxuICAgIGhvbWVEaXI6ICcnLFxuICAgIGN1cnJlbnREaXI6ICcnLFxuICAgIGNvbm5lY3Rpb246IG51bGwsXG4gIH07XG59XG4iXX0=
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.handleFtpCommand = handleFtpCommand;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
function handleFtpCommand(rawLine, ctx) {
|
|
40
|
+
const line = (rawLine || '').trim();
|
|
41
|
+
if (line === '') {
|
|
42
|
+
return { lines: [] };
|
|
43
|
+
}
|
|
44
|
+
const separatorIndex = line.search(/\s/);
|
|
45
|
+
const command = (separatorIndex === -1 ? line : line.slice(0, separatorIndex)).toLowerCase();
|
|
46
|
+
const args = separatorIndex === -1 ? '' : line.slice(separatorIndex).trim();
|
|
47
|
+
switch (command) {
|
|
48
|
+
case 'help':
|
|
49
|
+
return {
|
|
50
|
+
lines: [
|
|
51
|
+
'Available commands:',
|
|
52
|
+
' help - Show this help',
|
|
53
|
+
' clear - Clear screen',
|
|
54
|
+
' exit - Exit the FTP shell (Ctrl+D / quit / q / :q)',
|
|
55
|
+
' quit - Exit the FTP shell',
|
|
56
|
+
' q - Exit the FTP shell',
|
|
57
|
+
' version - Show version',
|
|
58
|
+
' status - Show session status',
|
|
59
|
+
' whoami - Show current user',
|
|
60
|
+
' ls [path] - List directory',
|
|
61
|
+
' pwd - Print working directory',
|
|
62
|
+
' cd <path> - Change directory',
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
case 'clear':
|
|
66
|
+
return { lines: [], clear: true };
|
|
67
|
+
case 'version':
|
|
68
|
+
return { lines: ['ftpd version 0.0.2'] };
|
|
69
|
+
case 'exit':
|
|
70
|
+
case 'quit':
|
|
71
|
+
case 'q':
|
|
72
|
+
case ':q':
|
|
73
|
+
return { lines: [], exit: true };
|
|
74
|
+
case 'status':
|
|
75
|
+
case 'whoami':
|
|
76
|
+
case 'who': {
|
|
77
|
+
const user = ctx.user;
|
|
78
|
+
const lines = [];
|
|
79
|
+
lines.push(`Session: ${user ? user : 'anonymous'}`);
|
|
80
|
+
lines.push(`Home: ${ctx.homeDir || '-'}`);
|
|
81
|
+
lines.push(`CurrentDir: ${ctx.currentDir || '-'}`);
|
|
82
|
+
return { lines };
|
|
83
|
+
}
|
|
84
|
+
case 'pwd':
|
|
85
|
+
return { lines: [`Pwd: ${ctx.currentDir || ctx.homeDir || '~'}`] };
|
|
86
|
+
case 'ls': {
|
|
87
|
+
if (!ctx.user) {
|
|
88
|
+
return { lines: ['Not logged in'] };
|
|
89
|
+
}
|
|
90
|
+
const userManager = ctx.server.getUserManager();
|
|
91
|
+
if (!userManager) {
|
|
92
|
+
return { lines: ['UserManager not initialized'] };
|
|
93
|
+
}
|
|
94
|
+
const hasList = userManager.checkPermission(ctx.user, 'list');
|
|
95
|
+
if (!hasList) {
|
|
96
|
+
return { lines: ['Permission denied: ls'] };
|
|
97
|
+
}
|
|
98
|
+
const targetDir = path.resolve(ctx.currentDir || ctx.homeDir || '.');
|
|
99
|
+
const dirents = fs.readdirSync(targetDir).map((name) => {
|
|
100
|
+
const full = path.join(targetDir, name);
|
|
101
|
+
const st = fs.statSync(full);
|
|
102
|
+
const type = st.isDirectory() ? 'd' : '-';
|
|
103
|
+
const size = st.size;
|
|
104
|
+
return `${type} ${size.toString().padStart(8)} ${name}`;
|
|
105
|
+
});
|
|
106
|
+
if (dirents.length === 0) {
|
|
107
|
+
return { lines: [`${targetDir} :: (empty)`] };
|
|
108
|
+
}
|
|
109
|
+
return { lines: dirents.map((l) => l) };
|
|
110
|
+
}
|
|
111
|
+
case 'cd': {
|
|
112
|
+
if (!ctx.user) {
|
|
113
|
+
return { lines: ['Not logged in'] };
|
|
114
|
+
}
|
|
115
|
+
const userManager = ctx.server.getUserManager();
|
|
116
|
+
if (!userManager) {
|
|
117
|
+
return { lines: ['UserManager not initialized'] };
|
|
118
|
+
}
|
|
119
|
+
const hasList = userManager.checkPermission(ctx.user, 'list');
|
|
120
|
+
if (!hasList) {
|
|
121
|
+
return { lines: ['Permission denied: cd'] };
|
|
122
|
+
}
|
|
123
|
+
const targetDir = args.trim() ? path.resolve(args.trim()) : ctx.homeDir || '.';
|
|
124
|
+
ctx.currentDir = targetDir;
|
|
125
|
+
return { lines: [`Cd ok: ${targetDir}`] };
|
|
126
|
+
}
|
|
127
|
+
default:
|
|
128
|
+
return { lines: [`Command not implemented: ${command}`] };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVnaXN0cnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2xpL3JlZ2lzdHJ5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUVBLE1BQVksSUFBSSxpQ0FBYTtBQUM3QixNQUFZLEVBQUUsK0JBQVc7QUFXekIsMEJBQWlDLE9BQWUsRUFBRSxHQUFrQjtJQUNsRSxNQUFNLElBQUksR0FBRyxDQUFDLE9BQU8sSUFBSSxFQUFFLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUNwQyxJQUFJLElBQUksS0FBSyxFQUFFLEVBQUUsQ0FBQztRQUNoQixPQUFPLEVBQUUsS0FBSyxFQUFFLEVBQUUsRUFBRSxDQUFDO0lBQ3ZCLENBQUM7SUFFRCxNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3pDLE1BQU0sT0FBTyxHQUFHLENBQUMsY0FBYyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLGNBQWMsQ0FBQyxDQUFDLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDN0YsTUFBTSxJQUFJLEdBQUcsY0FBYyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsY0FBYyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUM7SUFFNUUsUUFBUSxPQUFPLEVBQUUsQ0FBQztRQUNoQixLQUFLLE1BQU07WUFDVCxPQUFPO2dCQUNMLEtBQUssRUFBRTtvQkFDTCxxQkFBcUI7b0JBQ3JCLG9DQUFvQztvQkFDcEMsa0NBQWtDO29CQUNsQyxpRUFBaUU7b0JBQ2pFLHdDQUF3QztvQkFDeEMsd0NBQXdDO29CQUN4QyxrQ0FBa0M7b0JBQ2xDLHlDQUF5QztvQkFDekMsdUNBQXVDO29CQUN2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QUFDO1lBQzNCLE9BQU8sRUFBRSxLQUFLLEVBQUUsQ0FBQyxVQUFVLFNBQVMsRUFBRSxDQUFDLEVBQUUsQ0FBQztRQUM1QyxDQUFDO1FBRUQ7WUFDRSxPQUFPLEVBQUUsS0FBSyxFQUFFLENBQUMsNEJBQTRCLE9BQU8sRUFBRSxDQUFDLEVBQUUsQ0FBQztJQUM5RCxDQUFDO0FBQ0gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEZ0cENsaUNvbnRleHQgfSBmcm9tICcuL2NvbnRleHQnO1xuaW1wb3J0IHsgRnRwU2VydmVyIH0gZnJvbSAnLi4vY29yZS9GdHBTZXJ2ZXInO1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzJztcblxuZXhwb3J0IGludGVyZmFjZSBGdHBDb21tYW5kUmVzdWx0IHtcbiAgbGluZXM6IHN0cmluZ1tdO1xuICBjbGVhcj86IGJvb2xlYW47XG4gIGV4aXQ/OiBib29sZWFuO1xufVxuXG4vKipcbiAqIEZUUCBDTEkga29tdXRsYXLEsW7EsSBpxZ9sZVxuICovXG5leHBvcnQgZnVuY3Rpb24gaGFuZGxlRnRwQ29tbWFuZChyYXdMaW5lOiBzdHJpbmcsIGN0eDogRnRwQ2xpQ29udGV4dCk6IEZ0cENvbW1hbmRSZXN1bHQge1xuICBjb25zdCBsaW5lID0gKHJhd0xpbmUgfHwgJycpLnRyaW0oKTtcbiAgaWYgKGxpbmUgPT09ICcnKSB7XG4gICAgcmV0dXJuIHsgbGluZXM6IFtdIH07XG4gIH1cblxuICBjb25zdCBzZXBhcmF0b3JJbmRleCA9IGxpbmUuc2VhcmNoKC9cXHMvKTtcbiAgY29uc3QgY29tbWFuZCA9IChzZXBhcmF0b3JJbmRleCA9PT0gLTEgPyBsaW5lIDogbGluZS5zbGljZSgwLCBzZXBhcmF0b3JJbmRleCkpLnRvTG93ZXJDYXNlKCk7XG4gIGNvbnN0IGFyZ3MgPSBzZXBhcmF0b3JJbmRleCA9PT0gLTEgPyAnJyA6IGxpbmUuc2xpY2Uoc2VwYXJhdG9ySW5kZXgpLnRyaW0oKTtcblxuICBzd2l0Y2ggKGNvbW1hbmQpIHtcbiAgICBjYXNlICdoZWxwJzpcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGxpbmVzOiBbXG4gICAgICAgICAgJ0F2YWlsYWJsZSBjb21tYW5kczonLFxuICAgICAgICAgICcgIGhlbHAgICAgICAgICAgICAtIFNob3cgdGhpcyBoZWxwJyxcbiAgICAgICAgICAnICBjbGVhciAgICAgICAgICAgLSBDbGVhciBzY3JlZW4nLFxuICAgICAgICAgICcgIGV4aXQgICAgICAgICAgICAtIEV4aXQgdGhlIEZUUCBzaGVsbCAoQ3RybCtEIC8gcXVpdCAvIHEgLyA6cSknLFxuICAgICAgICAgICcgIHF1aXQgICAgICAgICAgICAtIEV4aXQgdGhlIEZUUCBzaGVsbCcsXG4gICAgICAgICAgJyAgcSAgICAgICAgICAgICAgIC0gRXhpdCB0aGUgRlRQIHNoZWxsJyxcbiAgICAgICAgICAnICB2ZXJzaW9uICAgICAgICAgLSBTaG93IHZlcnNpb24nLFxuICAgICAgICAgICcgIHN0YXR1cyAgICAgICAgICAtIFNob3cgc2Vzc2lvbiBzdGF0dXMnLFxuICAgICAgICAgICcgIHdob2FtaSAgICAgICAgICAtIFNob3cgY3VycmVudCB1c2VyJyxcbiAgICAgICAgICAnICBscyBbcGF0aF0gICAgICAgLSBMaXN0IGRpcmVjdG9yeScsXG4gICAgICAgICAgJyAgcHdkICAgICAgICAgICAgIC0gUHJpbnQgd29ya2luZyBkaXJlY3RvcnknLFxuICAgICAgICAgICcgIGNkIDxwYXRoPiAgICAgICAtIENoYW5nZSBkaXJlY3RvcnknLFxuICAgICAgICBdLFxuICAgICAgfTtcblxuICAgIGNhc2UgJ2NsZWFyJzpcbiAgICAgIHJldHVybiB7IGxpbmVzOiBbXSwgY2xlYXI6IHRydWUgfTtcblxuICAgIGNhc2UgJ3ZlcnNpb24nOlxuICAgICAgcmV0dXJuIHsgbGluZXM6IFsnZnRwZCB2ZXJzaW9uIDAuMC4yJ10gfTtcblxuICAgIGNhc2UgJ2V4aXQnOlxuICAgIGNhc2UgJ3F1aXQnOlxuICAgIGNhc2UgJ3EnOlxuICAgIGNhc2UgJzpxJzpcbiAgICAgIHJldHVybiB7IGxpbmVzOiBbXSwgZXhpdDogdHJ1ZSB9O1xuXG4gICAgY2FzZSAnc3RhdHVzJzpcbiAgICBjYXNlICd3aG9hbWknOlxuICAgIGNhc2UgJ3dobyc6IHtcbiAgICAgIGNvbnN0IHVzZXIgPSBjdHgudXNlcjtcbiAgICAgIGNvbnN0IGxpbmVzOiBzdHJpbmdbXSA9IFtdO1xuICAgICAgbGluZXMucHVzaChgU2Vzc2lvbjogJHt1c2VyID8gdXNlciA6ICdhbm9ueW1vdXMnfWApO1xuICAgICAgbGluZXMucHVzaChgSG9tZTogJHtjdHguaG9tZURpciB8fCAnLSd9YCk7XG4gICAgICBsaW5lcy5wdXNoKGBDdXJyZW50RGlyOiAke2N0eC5jdXJyZW50RGlyIHx8ICctJ31gKTtcbiAgICAgIHJldHVybiB7IGxpbmVzIH07XG4gICAgfVxuXG4gICAgY2FzZSAncHdkJzpcbiAgICAgIHJldHVybiB7IGxpbmVzOiBbYFB3ZDogJHtjdHguY3VycmVudERpciB8fCBjdHguaG9tZURpciB8fCAnfid9YF0gfTtcblxuICAgIGNhc2UgJ2xzJzoge1xuICAgICAgaWYgKCFjdHgudXNlcikge1xuICAgICAgICByZXR1cm4geyBsaW5lczogWydOb3QgbG9nZ2VkIGluJ10gfTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHVzZXJNYW5hZ2VyID0gY3R4LnNlcnZlci5nZXRVc2VyTWFuYWdlcigpO1xuICAgICAgaWYgKCF1c2VyTWFuYWdlcikge1xuICAgICAgICByZXR1cm4geyBsaW5lczogWydVc2VyTWFuYWdlciBub3QgaW5pdGlhbGl6ZWQnXSB9O1xuICAgICAgfVxuICAgICAgY29uc3QgaGFzTGlzdCA9IHVzZXJNYW5hZ2VyLmNoZWNrUGVybWlzc2lvbihjdHgudXNlciwgJ2xpc3QnKTtcbiAgICAgIGlmICghaGFzTGlzdCkge1xuICAgICAgICByZXR1cm4geyBsaW5lczogWydQZXJtaXNzaW9uIGRlbmllZDogbHMnXSB9O1xuICAgICAgfVxuICAgICAgY29uc3QgdGFyZ2V0RGlyID0gcGF0aC5yZXNvbHZlKGN0eC5jdXJyZW50RGlyIHx8IGN0eC5ob21lRGlyIHx8ICcuJyk7XG4gICAgICBjb25zdCBkaXJlbnRzID0gZnMucmVhZGRpclN5bmModGFyZ2V0RGlyKS5tYXAoKG5hbWUpID0+IHtcbiAgICAgICAgY29uc3QgZnVsbCA9IHBhdGguam9pbih0YXJnZXREaXIsIG5hbWUpO1xuICAgICAgICBjb25zdCBzdCA9IGZzLnN0YXRTeW5jKGZ1bGwpO1xuICAgICAgICBjb25zdCB0eXBlID0gc3QuaXNEaXJlY3RvcnkoKSA/ICdkJyA6ICctJztcbiAgICAgICAgY29uc3Qgc2l6ZSA9IHN0LnNpemU7XG4gICAgICAgIHJldHVybiBgJHt0eXBlfSAke3NpemUudG9TdHJpbmcoKS5wYWRTdGFydCg4KX0gJHtuYW1lfWA7XG4gICAgICB9KTtcbiAgICAgIGlmIChkaXJlbnRzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICByZXR1cm4geyBsaW5lczogW2Ake3RhcmdldERpcn0gOjogKGVtcHR5KWBdIH07XG4gICAgICB9XG4gICAgICByZXR1cm4geyBsaW5lczogZGlyZW50cy5tYXAoKGwpID0+IGwpIH07XG4gICAgfVxuXG4gICAgY2FzZSAnY2QnOiB7XG4gICAgICBpZiAoIWN0eC51c2VyKSB7XG4gICAgICAgIHJldHVybiB7IGxpbmVzOiBbJ05vdCBsb2dnZWQgaW4nXSB9O1xuICAgICAgfVxuICAgICAgY29uc3QgdXNlck1hbmFnZXIgPSBjdHguc2VydmVyLmdldFVzZXJNYW5hZ2VyKCk7XG4gICAgICBpZiAoIXVzZXJNYW5hZ2VyKSB7XG4gICAgICAgIHJldHVybiB7IGxpbmVzOiBbJ1VzZXJNYW5hZ2VyIG5vdCBpbml0aWFsaXplZCddIH07XG4gICAgICB9XG4gICAgICBjb25zdCBoYXNMaXN0ID0gdXNlck1hbmFnZXIuY2hlY2tQZXJtaXNzaW9uKGN0eC51c2VyLCAnbGlzdCcpO1xuICAgICAgaWYgKCFoYXNMaXN0KSB7XG4gICAgICAgIHJldHVybiB7IGxpbmVzOiBbJ1Blcm1pc3Npb24gZGVuaWVkOiBjZCddIH07XG4gICAgICB9XG4gICAgICBjb25zdCB0YXJnZXREaXIgPSBhcmdzLnRyaW0oKSA/IHBhdGgucmVzb2x2ZShhcmdzLnRyaW0oKSkgOiBjdHguaG9tZURpciB8fCAnLic7XG4gICAgICBjdHguY3VycmVudERpciA9IHRhcmdldERpcjtcbiAgICAgIHJldHVybiB7IGxpbmVzOiBbYENkIG9rOiAke3RhcmdldERpcn1gXSB9O1xuICAgIH1cblxuICAgIGRlZmF1bHQ6XG4gICAgICByZXR1cm4geyBsaW5lczogW2BDb21tYW5kIG5vdCBpbXBsZW1lbnRlZDogJHtjb21tYW5kfWBdIH07XG4gIH1cbn1cbiJdfQ==
|
package/src/cli/repl.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.startFtpRepl = startFtpRepl;
|
|
37
|
+
const readline = __importStar(require("readline"));
|
|
38
|
+
const registry_1 = require("./registry");
|
|
39
|
+
const banner_1 = require("./banner");
|
|
40
|
+
async function startFtpRepl(server, port) {
|
|
41
|
+
const ctx = {
|
|
42
|
+
server,
|
|
43
|
+
user: null,
|
|
44
|
+
homeDir: '',
|
|
45
|
+
currentDir: '',
|
|
46
|
+
connection: null,
|
|
47
|
+
};
|
|
48
|
+
const rl = readline.createInterface({
|
|
49
|
+
input: process.stdin,
|
|
50
|
+
output: process.stdout,
|
|
51
|
+
terminal: true,
|
|
52
|
+
});
|
|
53
|
+
let prompt = (0, banner_1.renderFtpPrompt)(ctx);
|
|
54
|
+
const writeLines = (lines) => {
|
|
55
|
+
for (const line of lines) {
|
|
56
|
+
process.stdout.write(`${line}\r\n`);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const showPrompt = () => {
|
|
60
|
+
prompt = (0, banner_1.renderFtpPrompt)(ctx);
|
|
61
|
+
rl.setPrompt(prompt);
|
|
62
|
+
rl.prompt();
|
|
63
|
+
};
|
|
64
|
+
rl.setPrompt(prompt);
|
|
65
|
+
rl.prompt();
|
|
66
|
+
rl.on('line', (line) => {
|
|
67
|
+
const result = (0, registry_1.handleFtpCommand)(line, ctx);
|
|
68
|
+
if (result.clear) {
|
|
69
|
+
process.stdout.write('\u001b[2J\u001b[H');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (result.exit) {
|
|
73
|
+
process.stdout.write('\r\n');
|
|
74
|
+
rl.close();
|
|
75
|
+
process.exit(0);
|
|
76
|
+
}
|
|
77
|
+
if (result.lines.length > 0) {
|
|
78
|
+
writeLines(result.lines);
|
|
79
|
+
}
|
|
80
|
+
showPrompt();
|
|
81
|
+
});
|
|
82
|
+
rl.on('close', () => {
|
|
83
|
+
process.stdout.write('\r\n');
|
|
84
|
+
process.exit(0);
|
|
85
|
+
});
|
|
86
|
+
process.on('SIGINT', () => {
|
|
87
|
+
process.stdout.write('\r\n');
|
|
88
|
+
rl.close();
|
|
89
|
+
process.exit(0);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVwbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jbGkvcmVwbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxNQUFZLFFBQVEscUNBQWlCO0FBR3JDLHlDQUE4QztBQUM5QyxxQ0FBMkM7QUFLcEMsS0FBSyx1QkFBdUIsTUFBaUIsRUFBRSxJQUFZO0lBQ2hFLE1BQU0sR0FBRyxHQUFrQjtRQUN6QixNQUFNO1FBQ04sSUFBSSxFQUFFLElBQUk7UUFDVixPQUFPLEVBQUUsRUFBRTtRQUNYLFVBQVUsRUFBRSxFQUFFO1FBQ2QsVUFBVSxFQUFFLElBQUk7S0FDakIsQ0FBQztJQUVGLE1BQU0sRUFBRSxHQUFHLFFBQVEsQ0FBQyxlQUFlLENBQUM7UUFDbEMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxLQUFLO1FBQ3BCLE1BQU0sRUFBRSxPQUFPLENBQUMsTUFBTTtRQUN0QixRQUFRLEVBQUUsSUFBSTtLQUNmLENBQUMsQ0FBQztJQUVILElBQUksTUFBTSxHQUFHLElBQUEsd0JBQWUsRUFBQyxHQUFHLENBQUMsQ0FBQztJQUVsQyxNQUFNLFVBQVUsR0FBRyxDQUFDLEtBQWUsRUFBUSxFQUFFO1FBQzNDLEtBQUssTUFBTSxJQUFJLElBQUksS0FBSyxFQUFFLENBQUM7WUFDekIsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsR0FBRyxJQUFJLE1BQU0sQ0FBQyxDQUFDO1FBQ3RDLENBQUM7SUFDSCxDQUFDLENBQUM7SUFFRixNQUFNLFVBQVUsR0FBRyxHQUFTLEVBQUU7UUFDNUIsTUFBTSxHQUFHLElBQUEsd0JBQWUsRUFBQyxHQUFHLENBQUMsQ0FBQztRQUM5QixFQUFFLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ3JCLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQztJQUNkLENBQUMsQ0FBQztJQUVGLEVBQUUsQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDckIsRUFBRSxDQUFDLE1BQU0sRUFBRSxDQUFDO0lBRVosRUFBRSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxJQUFZLEVBQUUsRUFBRTtRQUM3QixNQUFNLE1BQU0sR0FBRyxJQUFBLDJCQUFnQixFQUFDLElBQUksRUFBRSxHQUFHLENBQUMsQ0FBQztRQUMzQyxJQUFJLE1BQU0sQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUNqQixPQUFPLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDO1lBQzFDLE9BQU87UUFDVCxDQUFDO1FBQ0QsSUFBSSxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDaEIsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDN0IsRUFBRSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ1gsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNsQixDQUFDO1FBQ0QsSUFBSSxNQUFNLENBQUMsS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUM1QixVQUFVLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzNCLENBQUM7UUFDRCxVQUFVLEVBQUUsQ0FBQztJQUNmLENBQUMsQ0FBQyxDQUFDO0lBRUgsRUFBRSxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsR0FBRyxFQUFFO1FBQ2xCLE9BQU8sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQzdCLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDbEIsQ0FBQyxDQUFDLENBQUM7SUFHSCxPQUFPLENBQUMsRUFBRSxDQUFDLFFBQVEsRUFBRSxHQUFHLEVBQUU7UUFDeEIsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDN0IsRUFBRSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ1gsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNsQixDQUFDLENBQUMsQ0FBQztBQUNMLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyByZWFkbGluZSBmcm9tICdyZWFkbGluZSc7XG5pbXBvcnQgeyBGdHBTZXJ2ZXIgfSBmcm9tICcuLi9jb3JlL0Z0cFNlcnZlcic7XG5pbXBvcnQgeyBGdHBDbGlDb250ZXh0IH0gZnJvbSAnLi9jb250ZXh0JztcbmltcG9ydCB7IGhhbmRsZUZ0cENvbW1hbmQgfSBmcm9tICcuL3JlZ2lzdHJ5JztcbmltcG9ydCB7IHJlbmRlckZ0cFByb21wdCB9IGZyb20gJy4vYmFubmVyJztcblxuLyoqXG4gKiBGVFAgaW50ZXJha3RpZiB0ZXJtaW5hbCAoQ0xJIFJFUEwpIGJhxZ9sYXRcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN0YXJ0RnRwUmVwbChzZXJ2ZXI6IEZ0cFNlcnZlciwgcG9ydDogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IGN0eDogRnRwQ2xpQ29udGV4dCA9IHtcbiAgICBzZXJ2ZXIsXG4gICAgdXNlcjogbnVsbCxcbiAgICBob21lRGlyOiAnJyxcbiAgICBjdXJyZW50RGlyOiAnJyxcbiAgICBjb25uZWN0aW9uOiBudWxsLFxuICB9O1xuXG4gIGNvbnN0IHJsID0gcmVhZGxpbmUuY3JlYXRlSW50ZXJmYWNlKHtcbiAgICBpbnB1dDogcHJvY2Vzcy5zdGRpbixcbiAgICBvdXRwdXQ6IHByb2Nlc3Muc3Rkb3V0LFxuICAgIHRlcm1pbmFsOiB0cnVlLFxuICB9KTtcblxuICBsZXQgcHJvbXB0ID0gcmVuZGVyRnRwUHJvbXB0KGN0eCk7XG5cbiAgY29uc3Qgd3JpdGVMaW5lcyA9IChsaW5lczogc3RyaW5nW10pOiB2b2lkID0+IHtcbiAgICBmb3IgKGNvbnN0IGxpbmUgb2YgbGluZXMpIHtcbiAgICAgIHByb2Nlc3Muc3Rkb3V0LndyaXRlKGAke2xpbmV9XFxyXFxuYCk7XG4gICAgfVxuICB9O1xuXG4gIGNvbnN0IHNob3dQcm9tcHQgPSAoKTogdm9pZCA9PiB7XG4gICAgcHJvbXB0ID0gcmVuZGVyRnRwUHJvbXB0KGN0eCk7XG4gICAgcmwuc2V0UHJvbXB0KHByb21wdCk7XG4gICAgcmwucHJvbXB0KCk7XG4gIH07XG5cbiAgcmwuc2V0UHJvbXB0KHByb21wdCk7XG4gIHJsLnByb21wdCgpO1xuXG4gIHJsLm9uKCdsaW5lJywgKGxpbmU6IHN0cmluZykgPT4ge1xuICAgIGNvbnN0IHJlc3VsdCA9IGhhbmRsZUZ0cENvbW1hbmQobGluZSwgY3R4KTtcbiAgICBpZiAocmVzdWx0LmNsZWFyKSB7XG4gICAgICBwcm9jZXNzLnN0ZG91dC53cml0ZSgnXFx1MDAxYlsySlxcdTAwMWJbSCcpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAocmVzdWx0LmV4aXQpIHtcbiAgICAgIHByb2Nlc3Muc3Rkb3V0LndyaXRlKCdcXHJcXG4nKTtcbiAgICAgIHJsLmNsb3NlKCk7XG4gICAgICBwcm9jZXNzLmV4aXQoMCk7XG4gICAgfVxuICAgIGlmIChyZXN1bHQubGluZXMubGVuZ3RoID4gMCkge1xuICAgICAgd3JpdGVMaW5lcyhyZXN1bHQubGluZXMpO1xuICAgIH1cbiAgICBzaG93UHJvbXB0KCk7XG4gIH0pO1xuXG4gIHJsLm9uKCdjbG9zZScsICgpID0+IHtcbiAgICBwcm9jZXNzLnN0ZG91dC53cml0ZSgnXFxyXFxuJyk7XG4gICAgcHJvY2Vzcy5leGl0KDApO1xuICB9KTtcblxuICAvLyBHcmFjZWZ1bCBzaHV0ZG93blxuICBwcm9jZXNzLm9uKCdTSUdJTlQnLCAoKSA9PiB7XG4gICAgcHJvY2Vzcy5zdGRvdXQud3JpdGUoJ1xcclxcbicpO1xuICAgIHJsLmNsb3NlKCk7XG4gICAgcHJvY2Vzcy5leGl0KDApO1xuICB9KTtcbn1cbiJdfQ==
|
package/src/core/FtpServer.d.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { ServerConfig } from '../types';
|
|
2
|
-
|
|
2
|
+
import { UserManager } from '../userManager';
|
|
3
|
+
import { DatabaseManager } from '../db/database';
|
|
3
4
|
export declare class FtpServer {
|
|
4
5
|
private server;
|
|
5
6
|
private dataServer;
|
|
6
7
|
private connections;
|
|
7
8
|
private dataConnections;
|
|
8
|
-
private
|
|
9
|
+
private runtime;
|
|
10
|
+
private db;
|
|
9
11
|
private userManager;
|
|
10
12
|
private connectionCount;
|
|
11
13
|
private maxConnections;
|
|
@@ -28,7 +30,12 @@ export declare class FtpServer {
|
|
|
28
30
|
private passiveConnId;
|
|
29
31
|
private dataWaitTimer;
|
|
30
32
|
private static readonly DATA_WAIT_TIMEOUT_MS;
|
|
31
|
-
constructor(config?: Partial<
|
|
33
|
+
constructor(config?: Partial<ServerConfig>);
|
|
34
|
+
private loadConfigFromSettings;
|
|
35
|
+
private saveSettingToDb;
|
|
36
|
+
updateSetting(key: keyof ServerConfig, value: any): void;
|
|
37
|
+
getDb(): DatabaseManager;
|
|
38
|
+
getUserManager(): UserManager | null;
|
|
32
39
|
private normalizePortRange;
|
|
33
40
|
private normalizeIp;
|
|
34
41
|
private resolvePassiveIp;
|
|
@@ -50,4 +57,3 @@ export declare class FtpServer {
|
|
|
50
57
|
private writeControl;
|
|
51
58
|
closeConnection(connId: string, reason?: string): void;
|
|
52
59
|
}
|
|
53
|
-
export {};
|