@simplysm/storage 13.0.69 → 13.0.71
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 +17 -276
- package/dist/clients/ftp-storage-client.d.ts +18 -18
- package/dist/clients/ftp-storage-client.d.ts.map +1 -1
- package/dist/clients/ftp-storage-client.js +17 -17
- package/dist/clients/ftp-storage-client.js.map +1 -1
- package/dist/clients/sftp-storage-client.d.ts +14 -14
- package/dist/clients/sftp-storage-client.d.ts.map +1 -1
- package/dist/clients/sftp-storage-client.js +15 -15
- package/dist/clients/sftp-storage-client.js.map +1 -1
- package/dist/storage-factory.d.ts +5 -5
- package/dist/storage-factory.js +3 -3
- package/package.json +6 -5
- package/src/clients/ftp-storage-client.ts +22 -22
- package/src/clients/sftp-storage-client.ts +26 -26
- package/src/storage-factory.ts +6 -6
- package/tests/ftp-storage-client.spec.ts +261 -0
- package/tests/sftp-storage-client.spec.ts +253 -0
- package/tests/storage-factory.spec.ts +172 -0
package/README.md
CHANGED
|
@@ -1,292 +1,33 @@
|
|
|
1
1
|
# @simplysm/storage
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Using `StorageFactory`, you can automatically manage connection/disconnection, and you can also directly instantiate `FtpStorageClient` or `SftpStorageClient` if needed.
|
|
3
|
+
Simplysm Package - Storage Module (node)
|
|
6
4
|
|
|
7
5
|
## Installation
|
|
8
6
|
|
|
9
|
-
```bash
|
|
10
|
-
npm install @simplysm/storage
|
|
11
|
-
# or
|
|
12
7
|
pnpm add @simplysm/storage
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Main Modules
|
|
16
|
-
|
|
17
|
-
### Export List
|
|
18
|
-
|
|
19
|
-
| Module | Type | Description |
|
|
20
|
-
|------|------|------|
|
|
21
|
-
| `StorageFactory` | Class | Creates clients based on storage type and automatically manages connection/disconnection |
|
|
22
|
-
| `FtpStorageClient` | Class | FTP/FTPS protocol client (based on `basic-ftp`) |
|
|
23
|
-
| `SftpStorageClient` | Class | SFTP protocol client (based on `ssh2-sftp-client`) |
|
|
24
|
-
| `Storage` | Interface | Common interface implemented by all storage clients |
|
|
25
|
-
| `StorageConnConfig` | Interface | Connection configuration |
|
|
26
|
-
| `FileInfo` | Interface | Directory entry information |
|
|
27
|
-
| `StorageType` | Type | Storage protocol types (`"ftp" \| "ftps" \| "sftp"`) |
|
|
28
|
-
|
|
29
|
-
## Type Definitions
|
|
30
|
-
|
|
31
|
-
### StorageConnConfig
|
|
32
|
-
|
|
33
|
-
Configuration required for server connection.
|
|
34
|
-
|
|
35
|
-
```typescript
|
|
36
|
-
interface StorageConnConfig {
|
|
37
|
-
host: string; // Server host
|
|
38
|
-
port?: number; // Port (FTP default: 21, SFTP default: 22)
|
|
39
|
-
user?: string; // Username
|
|
40
|
-
pass?: string; // Password (required for FTP/FTPS; optional for SFTP)
|
|
41
|
-
}
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
**SFTP SSH Key Authentication:**
|
|
45
|
-
- If `pass` is omitted for SFTP connections, the client automatically attempts authentication using:
|
|
46
|
-
1. SSH key file at `~/.ssh/id_ed25519`
|
|
47
|
-
2. SSH agent (if `SSH_AUTH_SOCK` environment variable is set)
|
|
48
|
-
- Both methods are tried in order; if the key file authentication fails (e.g., encrypted keys), the client falls back to agent-only authentication.
|
|
49
|
-
|
|
50
|
-
### FileInfo
|
|
51
|
-
|
|
52
|
-
File/directory information returned by `readdir()`.
|
|
53
|
-
|
|
54
|
-
```typescript
|
|
55
|
-
interface FileInfo {
|
|
56
|
-
name: string; // File or directory name
|
|
57
|
-
isFile: boolean; // true if file, false if directory
|
|
58
|
-
}
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### StorageType
|
|
62
|
-
|
|
63
|
-
Supported storage protocol types.
|
|
64
|
-
|
|
65
|
-
```typescript
|
|
66
|
-
type StorageType = "ftp" | "ftps" | "sftp";
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
| Value | Protocol | Default Port | Description |
|
|
70
|
-
|-----|---------|----------|------|
|
|
71
|
-
| `"ftp"` | FTP | 21 | Unencrypted FTP |
|
|
72
|
-
| `"ftps"` | FTPS | 21 | TLS-encrypted FTP |
|
|
73
|
-
| `"sftp"` | SFTP | 22 | SSH-based file transfer |
|
|
74
|
-
|
|
75
|
-
### Storage Interface
|
|
76
|
-
|
|
77
|
-
Common interface implemented by all storage clients (`FtpStorageClient`, `SftpStorageClient`). `Bytes` is a `Uint8Array` type alias defined in `@simplysm/core-common`.
|
|
78
|
-
|
|
79
|
-
| Method | Signature | Description |
|
|
80
|
-
|--------|---------|------|
|
|
81
|
-
| `connect` | `(config: StorageConnConfig) => Promise<void>` | Connect to server |
|
|
82
|
-
| `close` | `() => Promise<void>` | Close connection |
|
|
83
|
-
| `put` | `(localPathOrBuffer: string \| Bytes, storageFilePath: string) => Promise<void>` | Upload file (local path or byte data) |
|
|
84
|
-
| `readFile` | `(filePath: string) => Promise<Bytes>` | Download file (returns `Bytes`) |
|
|
85
|
-
| `readdir` | `(dirPath: string) => Promise<FileInfo[]>` | List directory contents |
|
|
86
|
-
| `remove` | `(filePath: string) => Promise<void>` | Delete file |
|
|
87
|
-
| `exists` | `(filePath: string) => Promise<boolean>` | Check if file/directory exists |
|
|
88
|
-
| `mkdir` | `(dirPath: string) => Promise<void>` | Create directory (recursive) |
|
|
89
|
-
| `rename` | `(fromPath: string, toPath: string) => Promise<void>` | Rename file/directory |
|
|
90
|
-
| `uploadDir` | `(fromPath: string, toPath: string) => Promise<void>` | Upload entire local directory to remote |
|
|
91
|
-
|
|
92
|
-
## Usage
|
|
93
|
-
|
|
94
|
-
### StorageFactory (Recommended)
|
|
95
|
-
|
|
96
|
-
`StorageFactory.connect()` automatically manages connection and disconnection with a callback pattern. The connection is always closed even if an exception occurs in the callback, so it's recommended over using clients directly.
|
|
97
|
-
|
|
98
|
-
```typescript
|
|
99
|
-
import { StorageFactory } from "@simplysm/storage";
|
|
100
|
-
|
|
101
|
-
// FTP connection
|
|
102
|
-
const result = await StorageFactory.connect("ftp", {
|
|
103
|
-
host: "ftp.example.com",
|
|
104
|
-
port: 21,
|
|
105
|
-
user: "username",
|
|
106
|
-
pass: "password",
|
|
107
|
-
}, async (client) => {
|
|
108
|
-
// Upload local file to remote server
|
|
109
|
-
await client.put("/local/path/file.txt", "/remote/path/file.txt");
|
|
110
|
-
|
|
111
|
-
// Upload byte data directly
|
|
112
|
-
const data = new TextEncoder().encode("hello world");
|
|
113
|
-
await client.put(data, "/remote/path/hello.txt");
|
|
114
|
-
|
|
115
|
-
// Download remote file
|
|
116
|
-
const content = await client.readFile("/remote/path/file.txt");
|
|
117
|
-
|
|
118
|
-
// The callback's return value becomes the return value of StorageFactory.connect()
|
|
119
|
-
return content;
|
|
120
|
-
});
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
```typescript
|
|
124
|
-
// FTPS connection (TLS encryption)
|
|
125
|
-
await StorageFactory.connect("ftps", {
|
|
126
|
-
host: "ftps.example.com",
|
|
127
|
-
user: "username",
|
|
128
|
-
pass: "password",
|
|
129
|
-
}, async (client) => {
|
|
130
|
-
await client.put("/local/file.txt", "/remote/file.txt");
|
|
131
|
-
});
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
```typescript
|
|
135
|
-
// SFTP connection with password
|
|
136
|
-
await StorageFactory.connect("sftp", {
|
|
137
|
-
host: "sftp.example.com",
|
|
138
|
-
port: 22,
|
|
139
|
-
user: "username",
|
|
140
|
-
pass: "password",
|
|
141
|
-
}, async (client) => {
|
|
142
|
-
// List directory contents
|
|
143
|
-
const files = await client.readdir("/remote/path");
|
|
144
|
-
for (const file of files) {
|
|
145
|
-
console.log(`${file.name} - ${file.isFile ? "File" : "Directory"}`);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Upload entire directory
|
|
149
|
-
await client.uploadDir("/local/dir", "/remote/dir");
|
|
150
|
-
});
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
// SFTP connection with SSH key (if pass is omitted, uses ~/.ssh/id_ed25519 or SSH agent)
|
|
155
|
-
await StorageFactory.connect("sftp", {
|
|
156
|
-
host: "sftp.example.com",
|
|
157
|
-
port: 22,
|
|
158
|
-
user: "username",
|
|
159
|
-
// No password provided - uses SSH key authentication
|
|
160
|
-
}, async (client) => {
|
|
161
|
-
const files = await client.readdir("/remote/path");
|
|
162
|
-
await client.uploadDir("/local/dir", "/remote/dir");
|
|
163
|
-
});
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
### FtpStorageClient (Direct Usage)
|
|
167
|
-
|
|
168
|
-
Client that uses FTP or FTPS protocol. The `secure` parameter in the constructor determines whether to use FTPS.
|
|
169
|
-
|
|
170
|
-
```typescript
|
|
171
|
-
import { FtpStorageClient } from "@simplysm/storage";
|
|
172
|
-
|
|
173
|
-
// FTP client (secure: false is default)
|
|
174
|
-
const client = new FtpStorageClient();
|
|
175
|
-
|
|
176
|
-
// FTPS client
|
|
177
|
-
const secureClient = new FtpStorageClient(true);
|
|
178
|
-
|
|
179
|
-
await client.connect({
|
|
180
|
-
host: "ftp.example.com",
|
|
181
|
-
port: 21,
|
|
182
|
-
user: "username",
|
|
183
|
-
pass: "password",
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
try {
|
|
187
|
-
// Upload file - from local file path
|
|
188
|
-
await client.put("/local/path/file.txt", "/remote/path/file.txt");
|
|
189
|
-
|
|
190
|
-
// Upload file - from Uint8Array byte data
|
|
191
|
-
const bytes = new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f]);
|
|
192
|
-
await client.put(bytes, "/remote/path/hello.bin");
|
|
193
|
-
|
|
194
|
-
// Download file (returns Bytes, i.e. Uint8Array)
|
|
195
|
-
const data = await client.readFile("/remote/path/file.txt");
|
|
196
|
-
const text = new TextDecoder().decode(data);
|
|
197
|
-
|
|
198
|
-
// List directory contents
|
|
199
|
-
const files = await client.readdir("/remote/path");
|
|
200
|
-
|
|
201
|
-
// Check if file/directory exists
|
|
202
|
-
const exists = await client.exists("/remote/path/file.txt");
|
|
203
|
-
|
|
204
|
-
// Create directory (creates parent directories too)
|
|
205
|
-
await client.mkdir("/remote/new/nested/path");
|
|
206
|
-
|
|
207
|
-
// Rename file
|
|
208
|
-
await client.rename("/remote/old-name.txt", "/remote/new-name.txt");
|
|
209
|
-
|
|
210
|
-
// Delete file
|
|
211
|
-
await client.remove("/remote/path/file.txt");
|
|
212
|
-
|
|
213
|
-
// Upload entire local directory to remote
|
|
214
|
-
await client.uploadDir("/local/dir", "/remote/dir");
|
|
215
|
-
} finally {
|
|
216
|
-
// Connection must be closed
|
|
217
|
-
await client.close();
|
|
218
|
-
}
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
### SftpStorageClient (Direct Usage)
|
|
222
|
-
|
|
223
|
-
Client that uses SFTP protocol. It implements the same `Storage` interface as `FtpStorageClient`, so the API is identical.
|
|
224
|
-
|
|
225
|
-
```typescript
|
|
226
|
-
import { SftpStorageClient } from "@simplysm/storage";
|
|
227
|
-
|
|
228
|
-
const client = new SftpStorageClient();
|
|
229
|
-
|
|
230
|
-
// Connection with password
|
|
231
|
-
await client.connect({
|
|
232
|
-
host: "sftp.example.com",
|
|
233
|
-
port: 22,
|
|
234
|
-
user: "username",
|
|
235
|
-
pass: "password",
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
try {
|
|
239
|
-
// All methods of the Storage interface can be used identically
|
|
240
|
-
await client.put("/local/path/file.txt", "/remote/path/file.txt");
|
|
241
|
-
const data = await client.readFile("/remote/path/file.txt");
|
|
242
|
-
const files = await client.readdir("/remote/path");
|
|
243
|
-
const exists = await client.exists("/remote/path/file.txt");
|
|
244
|
-
await client.mkdir("/remote/new/path");
|
|
245
|
-
await client.rename("/remote/old.txt", "/remote/new.txt");
|
|
246
|
-
await client.remove("/remote/path/file.txt");
|
|
247
|
-
await client.uploadDir("/local/dir", "/remote/dir");
|
|
248
|
-
} finally {
|
|
249
|
-
await client.close();
|
|
250
|
-
}
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
```typescript
|
|
254
|
-
// Connection with SSH key (password omitted)
|
|
255
|
-
const client = new SftpStorageClient();
|
|
256
|
-
|
|
257
|
-
await client.connect({
|
|
258
|
-
host: "sftp.example.com",
|
|
259
|
-
port: 22,
|
|
260
|
-
user: "username",
|
|
261
|
-
// Uses ~/.ssh/id_ed25519 or SSH agent for authentication
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
try {
|
|
265
|
-
await client.put("/local/path/file.txt", "/remote/path/file.txt");
|
|
266
|
-
// ... other operations
|
|
267
|
-
} finally {
|
|
268
|
-
await client.close();
|
|
269
|
-
}
|
|
270
|
-
```
|
|
271
8
|
|
|
272
|
-
##
|
|
9
|
+
## Source Index
|
|
273
10
|
|
|
274
|
-
###
|
|
11
|
+
### Types
|
|
275
12
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
-
|
|
279
|
-
|
|
13
|
+
| Source | Exports | Description | Test |
|
|
14
|
+
|--------|---------|-------------|------|
|
|
15
|
+
| `src/types/storage-conn-config.ts` | `StorageConnConfig` | Connection config interface (host, port, user, pass) | - |
|
|
16
|
+
| `src/types/storage.ts` | `FileInfo`, `Storage` | Storage interface and file entry type | - |
|
|
17
|
+
| `src/types/storage-type.ts` | `StorageType` | Union type for supported protocols: ftp, ftps, sftp | - |
|
|
280
18
|
|
|
281
|
-
###
|
|
19
|
+
### Clients
|
|
282
20
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
-
|
|
21
|
+
| Source | Exports | Description | Test |
|
|
22
|
+
|--------|---------|-------------|------|
|
|
23
|
+
| `src/clients/ftp-storage-client.ts` | `FtpStorageClient` | Storage client for FTP/FTPS protocol | `ftp-storage-client.spec.ts` |
|
|
24
|
+
| `src/clients/sftp-storage-client.ts` | `SftpStorageClient` | Storage client for SFTP protocol with SSH key support | `sftp-storage-client.spec.ts` |
|
|
286
25
|
|
|
287
|
-
###
|
|
26
|
+
### Factory
|
|
288
27
|
|
|
289
|
-
|
|
28
|
+
| Source | Exports | Description | Test |
|
|
29
|
+
|--------|---------|-------------|------|
|
|
30
|
+
| `src/storage-factory.ts` | `StorageFactory` | Factory that creates and auto-closes storage connections | `storage-factory.spec.ts` |
|
|
290
31
|
|
|
291
32
|
## License
|
|
292
33
|
|
|
@@ -2,54 +2,54 @@ import type { Bytes } from "@simplysm/core-common";
|
|
|
2
2
|
import type { Storage, FileInfo } from "../types/storage";
|
|
3
3
|
import type { StorageConnConfig } from "../types/storage-conn-config";
|
|
4
4
|
/**
|
|
5
|
-
* FTP/FTPS
|
|
5
|
+
* Storage client using FTP/FTPS protocol.
|
|
6
6
|
*
|
|
7
7
|
* @remarks
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* The `secure` constructor parameter configures whether to use FTPS.
|
|
9
|
+
* Using {@link StorageFactory.connect} is recommended over direct usage.
|
|
10
10
|
*/
|
|
11
11
|
export declare class FtpStorageClient implements Storage {
|
|
12
12
|
private readonly _secure;
|
|
13
13
|
private _client;
|
|
14
14
|
constructor(_secure?: boolean);
|
|
15
15
|
/**
|
|
16
|
-
* FTP
|
|
16
|
+
* Connect to the FTP server.
|
|
17
17
|
*
|
|
18
18
|
* @remarks
|
|
19
|
-
* -
|
|
20
|
-
* -
|
|
21
|
-
* -
|
|
19
|
+
* - Must close the connection with {@link close} after use.
|
|
20
|
+
* - Do not call multiple times on the same instance (connection leak).
|
|
21
|
+
* - Use {@link StorageFactory.connect} for automatic connection/close management (recommended).
|
|
22
22
|
*/
|
|
23
23
|
connect(config: StorageConnConfig): Promise<void>;
|
|
24
24
|
private _requireClient;
|
|
25
|
-
/**
|
|
25
|
+
/** Create a directory. Creates parent directories if they do not exist. */
|
|
26
26
|
mkdir(dirPath: string): Promise<void>;
|
|
27
27
|
rename(fromPath: string, toPath: string): Promise<void>;
|
|
28
28
|
readdir(dirPath: string): Promise<FileInfo[]>;
|
|
29
29
|
readFile(filePath: string): Promise<Bytes>;
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
31
|
+
* Check whether a file or directory exists.
|
|
32
32
|
*
|
|
33
33
|
* @remarks
|
|
34
|
-
*
|
|
35
|
-
*
|
|
34
|
+
* For files, uses the size() command for O(1) performance.
|
|
35
|
+
* For directories, queries the parent directory listing, so performance may degrade with many entries.
|
|
36
36
|
*
|
|
37
|
-
*
|
|
37
|
+
* Paths without slashes (e.g. `file.txt`) are searched in the root directory (`/`).
|
|
38
38
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
39
|
+
* Returns false even if the parent directory does not exist.
|
|
40
|
+
* Returns false for all exceptions including network errors and permission errors.
|
|
41
41
|
*/
|
|
42
42
|
exists(filePath: string): Promise<boolean>;
|
|
43
43
|
remove(filePath: string): Promise<void>;
|
|
44
|
-
/**
|
|
44
|
+
/** Upload a local file path or byte data to the remote path. */
|
|
45
45
|
put(localPathOrBuffer: string | Bytes, storageFilePath: string): Promise<void>;
|
|
46
46
|
uploadDir(fromPath: string, toPath: string): Promise<void>;
|
|
47
47
|
/**
|
|
48
|
-
*
|
|
48
|
+
* Close the connection.
|
|
49
49
|
*
|
|
50
50
|
* @remarks
|
|
51
|
-
*
|
|
52
|
-
*
|
|
51
|
+
* Safe to call when already closed (no error thrown).
|
|
52
|
+
* After closing, you can reconnect by calling {@link connect} again on the same instance.
|
|
53
53
|
*/
|
|
54
54
|
close(): Promise<void>;
|
|
55
55
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ftp-storage-client.d.ts","sourceRoot":"","sources":["..\\..\\src\\clients\\ftp-storage-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAInD,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEtE;;;;;;GAMG;AACH,qBAAa,gBAAiB,YAAW,OAAO;IAGlC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,OAAO,CAAC,OAAO,CAAyB;gBAEX,OAAO,GAAE,OAAe;IAErD;;;;;;;OAOG;IACG,OAAO,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBvD,OAAO,CAAC,cAAc;IAOtB,
|
|
1
|
+
{"version":3,"file":"ftp-storage-client.d.ts","sourceRoot":"","sources":["..\\..\\src\\clients\\ftp-storage-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAInD,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEtE;;;;;;GAMG;AACH,qBAAa,gBAAiB,YAAW,OAAO;IAGlC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,OAAO,CAAC,OAAO,CAAyB;gBAEX,OAAO,GAAE,OAAe;IAErD;;;;;;;OAOG;IACG,OAAO,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBvD,OAAO,CAAC,cAAc;IAOtB,2EAA2E;IACrE,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrC,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvD,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAK7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAWhD;;;;;;;;;;;OAWG;IACG,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAmB1C,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7C,gEAAgE;IAC1D,GAAG,CAAC,iBAAiB,EAAE,MAAM,GAAG,KAAK,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU9E,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhE;;;;;;OAMG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CASvB"}
|
|
@@ -7,16 +7,16 @@ class FtpStorageClient {
|
|
|
7
7
|
}
|
|
8
8
|
_client;
|
|
9
9
|
/**
|
|
10
|
-
* FTP
|
|
10
|
+
* Connect to the FTP server.
|
|
11
11
|
*
|
|
12
12
|
* @remarks
|
|
13
|
-
* -
|
|
14
|
-
* -
|
|
15
|
-
* -
|
|
13
|
+
* - Must close the connection with {@link close} after use.
|
|
14
|
+
* - Do not call multiple times on the same instance (connection leak).
|
|
15
|
+
* - Use {@link StorageFactory.connect} for automatic connection/close management (recommended).
|
|
16
16
|
*/
|
|
17
17
|
async connect(config) {
|
|
18
18
|
if (this._client !== void 0) {
|
|
19
|
-
throw new SdError("
|
|
19
|
+
throw new SdError("FTP server is already connected. Please call close() first.");
|
|
20
20
|
}
|
|
21
21
|
const client = new ftp.Client();
|
|
22
22
|
try {
|
|
@@ -35,11 +35,11 @@ class FtpStorageClient {
|
|
|
35
35
|
}
|
|
36
36
|
_requireClient() {
|
|
37
37
|
if (this._client === void 0) {
|
|
38
|
-
throw new SdError("
|
|
38
|
+
throw new SdError("Not connected to FTP server.");
|
|
39
39
|
}
|
|
40
40
|
return this._client;
|
|
41
41
|
}
|
|
42
|
-
/**
|
|
42
|
+
/** Create a directory. Creates parent directories if they do not exist. */
|
|
43
43
|
async mkdir(dirPath) {
|
|
44
44
|
await this._requireClient().ensureDir(dirPath);
|
|
45
45
|
}
|
|
@@ -61,16 +61,16 @@ class FtpStorageClient {
|
|
|
61
61
|
return bytesConcat(chunks);
|
|
62
62
|
}
|
|
63
63
|
/**
|
|
64
|
-
*
|
|
64
|
+
* Check whether a file or directory exists.
|
|
65
65
|
*
|
|
66
66
|
* @remarks
|
|
67
|
-
*
|
|
68
|
-
*
|
|
67
|
+
* For files, uses the size() command for O(1) performance.
|
|
68
|
+
* For directories, queries the parent directory listing, so performance may degrade with many entries.
|
|
69
69
|
*
|
|
70
|
-
*
|
|
70
|
+
* Paths without slashes (e.g. `file.txt`) are searched in the root directory (`/`).
|
|
71
71
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
72
|
+
* Returns false even if the parent directory does not exist.
|
|
73
|
+
* Returns false for all exceptions including network errors and permission errors.
|
|
74
74
|
*/
|
|
75
75
|
async exists(filePath) {
|
|
76
76
|
try {
|
|
@@ -91,7 +91,7 @@ class FtpStorageClient {
|
|
|
91
91
|
async remove(filePath) {
|
|
92
92
|
await this._requireClient().remove(filePath);
|
|
93
93
|
}
|
|
94
|
-
/**
|
|
94
|
+
/** Upload a local file path or byte data to the remote path. */
|
|
95
95
|
async put(localPathOrBuffer, storageFilePath) {
|
|
96
96
|
let param;
|
|
97
97
|
if (typeof localPathOrBuffer === "string") {
|
|
@@ -105,11 +105,11 @@ class FtpStorageClient {
|
|
|
105
105
|
await this._requireClient().uploadFromDir(fromPath, toPath);
|
|
106
106
|
}
|
|
107
107
|
/**
|
|
108
|
-
*
|
|
108
|
+
* Close the connection.
|
|
109
109
|
*
|
|
110
110
|
* @remarks
|
|
111
|
-
*
|
|
112
|
-
*
|
|
111
|
+
* Safe to call when already closed (no error thrown).
|
|
112
|
+
* After closing, you can reconnect by calling {@link connect} again on the same instance.
|
|
113
113
|
*/
|
|
114
114
|
close() {
|
|
115
115
|
if (this._client === void 0) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/clients/ftp-storage-client.ts"],
|
|
4
|
-
"mappings": "AACA,SAAS,aAAa,eAAe;AACrC,OAAO,SAAS;AAChB,SAAS,aAAa,gBAAgB;AAW/B,MAAM,iBAAoC;AAAA,EAG/C,YAA6B,UAAmB,OAAO;AAA1B;AAAA,EAA2B;AAAA,EAFhD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYR,MAAM,QAAQ,QAA0C;AACtD,QAAI,KAAK,YAAY,QAAW;AAC9B,YAAM,IAAI,QAAQ,
|
|
4
|
+
"mappings": "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",
|
|
5
5
|
"names": []
|
|
6
6
|
}
|
|
@@ -2,46 +2,46 @@ import type { Bytes } from "@simplysm/core-common";
|
|
|
2
2
|
import type { Storage, FileInfo } from "../types/storage";
|
|
3
3
|
import type { StorageConnConfig } from "../types/storage-conn-config";
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Storage client using SFTP protocol.
|
|
6
6
|
*
|
|
7
7
|
* @remarks
|
|
8
|
-
*
|
|
8
|
+
* Using {@link StorageFactory.connect} is recommended over direct usage.
|
|
9
9
|
*/
|
|
10
10
|
export declare class SftpStorageClient implements Storage {
|
|
11
11
|
private _client;
|
|
12
12
|
/**
|
|
13
|
-
* SFTP
|
|
13
|
+
* Connect to the SFTP server.
|
|
14
14
|
*
|
|
15
15
|
* @remarks
|
|
16
|
-
* -
|
|
17
|
-
* -
|
|
18
|
-
* -
|
|
16
|
+
* - Must close the connection with {@link close} after use.
|
|
17
|
+
* - Do not call multiple times on the same instance (connection leak).
|
|
18
|
+
* - Use {@link StorageFactory.connect} for automatic connection/close management (recommended).
|
|
19
19
|
*/
|
|
20
20
|
connect(config: StorageConnConfig): Promise<void>;
|
|
21
21
|
private _requireClient;
|
|
22
|
-
/**
|
|
22
|
+
/** Create a directory. Creates parent directories if they do not exist. */
|
|
23
23
|
mkdir(dirPath: string): Promise<void>;
|
|
24
24
|
rename(fromPath: string, toPath: string): Promise<void>;
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
26
|
+
* Check whether a file or directory exists.
|
|
27
27
|
*
|
|
28
28
|
* @remarks
|
|
29
|
-
*
|
|
30
|
-
*
|
|
29
|
+
* Returns false even if the parent directory does not exist.
|
|
30
|
+
* Returns false for all exceptions including network errors and permission errors.
|
|
31
31
|
*/
|
|
32
32
|
exists(filePath: string): Promise<boolean>;
|
|
33
33
|
readdir(dirPath: string): Promise<FileInfo[]>;
|
|
34
34
|
readFile(filePath: string): Promise<Bytes>;
|
|
35
35
|
remove(filePath: string): Promise<void>;
|
|
36
|
-
/**
|
|
36
|
+
/** Upload a local file path or byte data to the remote path. */
|
|
37
37
|
put(localPathOrBuffer: string | Bytes, storageFilePath: string): Promise<void>;
|
|
38
38
|
uploadDir(fromPath: string, toPath: string): Promise<void>;
|
|
39
39
|
/**
|
|
40
|
-
*
|
|
40
|
+
* Close the connection.
|
|
41
41
|
*
|
|
42
42
|
* @remarks
|
|
43
|
-
*
|
|
44
|
-
*
|
|
43
|
+
* Safe to call when already closed (no error thrown).
|
|
44
|
+
* After closing, you can reconnect by calling {@link connect} again on the same instance.
|
|
45
45
|
*/
|
|
46
46
|
close(): Promise<void>;
|
|
47
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sftp-storage-client.d.ts","sourceRoot":"","sources":["..\\..\\src\\clients\\sftp-storage-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAGnD,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAKtE;;;;;GAKG;AACH,qBAAa,iBAAkB,YAAW,OAAO;IAC/C,OAAO,CAAC,OAAO,CAAyB;IAExC;;;;;;;OAOG;IACG,OAAO,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IA6CvD,OAAO,CAAC,cAAc;IAOtB,
|
|
1
|
+
{"version":3,"file":"sftp-storage-client.d.ts","sourceRoot":"","sources":["..\\..\\src\\clients\\sftp-storage-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAGnD,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAKtE;;;;;GAKG;AACH,qBAAa,iBAAkB,YAAW,OAAO;IAC/C,OAAO,CAAC,OAAO,CAAyB;IAExC;;;;;;;OAOG;IACG,OAAO,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IA6CvD,OAAO,CAAC,cAAc;IAOtB,2EAA2E;IACrE,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrC,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7D;;;;;;OAMG;IACG,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAW1C,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAQ7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAc1C,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7C,gEAAgE;IAC1D,GAAG,CAAC,iBAAiB,EAAE,MAAM,GAAG,KAAK,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS9E,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhE;;;;;;OAMG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAO7B"}
|
|
@@ -3,16 +3,16 @@ import SftpClient from "ssh2-sftp-client";
|
|
|
3
3
|
class SftpStorageClient {
|
|
4
4
|
_client;
|
|
5
5
|
/**
|
|
6
|
-
* SFTP
|
|
6
|
+
* Connect to the SFTP server.
|
|
7
7
|
*
|
|
8
8
|
* @remarks
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
11
|
-
* -
|
|
9
|
+
* - Must close the connection with {@link close} after use.
|
|
10
|
+
* - Do not call multiple times on the same instance (connection leak).
|
|
11
|
+
* - Use {@link StorageFactory.connect} for automatic connection/close management (recommended).
|
|
12
12
|
*/
|
|
13
13
|
async connect(config) {
|
|
14
14
|
if (this._client !== void 0) {
|
|
15
|
-
throw new SdError("
|
|
15
|
+
throw new SdError("SFTP server is already connected. Please call close() first.");
|
|
16
16
|
}
|
|
17
17
|
const client = new SftpClient();
|
|
18
18
|
try {
|
|
@@ -51,11 +51,11 @@ class SftpStorageClient {
|
|
|
51
51
|
}
|
|
52
52
|
_requireClient() {
|
|
53
53
|
if (this._client === void 0) {
|
|
54
|
-
throw new SdError("
|
|
54
|
+
throw new SdError("Not connected to SFTP server.");
|
|
55
55
|
}
|
|
56
56
|
return this._client;
|
|
57
57
|
}
|
|
58
|
-
/**
|
|
58
|
+
/** Create a directory. Creates parent directories if they do not exist. */
|
|
59
59
|
async mkdir(dirPath) {
|
|
60
60
|
await this._requireClient().mkdir(dirPath, true);
|
|
61
61
|
}
|
|
@@ -63,11 +63,11 @@ class SftpStorageClient {
|
|
|
63
63
|
await this._requireClient().rename(fromPath, toPath);
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
66
|
+
* Check whether a file or directory exists.
|
|
67
67
|
*
|
|
68
68
|
* @remarks
|
|
69
|
-
*
|
|
70
|
-
*
|
|
69
|
+
* Returns false even if the parent directory does not exist.
|
|
70
|
+
* Returns false for all exceptions including network errors and permission errors.
|
|
71
71
|
*/
|
|
72
72
|
async exists(filePath) {
|
|
73
73
|
try {
|
|
@@ -92,12 +92,12 @@ class SftpStorageClient {
|
|
|
92
92
|
if (typeof result === "string") {
|
|
93
93
|
return new TextEncoder().encode(result);
|
|
94
94
|
}
|
|
95
|
-
throw new SdError("
|
|
95
|
+
throw new SdError("Unexpected response type.");
|
|
96
96
|
}
|
|
97
97
|
async remove(filePath) {
|
|
98
98
|
await this._requireClient().delete(filePath);
|
|
99
99
|
}
|
|
100
|
-
/**
|
|
100
|
+
/** Upload a local file path or byte data to the remote path. */
|
|
101
101
|
async put(localPathOrBuffer, storageFilePath) {
|
|
102
102
|
if (typeof localPathOrBuffer === "string") {
|
|
103
103
|
await this._requireClient().fastPut(localPathOrBuffer, storageFilePath);
|
|
@@ -109,11 +109,11 @@ class SftpStorageClient {
|
|
|
109
109
|
await this._requireClient().uploadDir(fromPath, toPath);
|
|
110
110
|
}
|
|
111
111
|
/**
|
|
112
|
-
*
|
|
112
|
+
* Close the connection.
|
|
113
113
|
*
|
|
114
114
|
* @remarks
|
|
115
|
-
*
|
|
116
|
-
*
|
|
115
|
+
* Safe to call when already closed (no error thrown).
|
|
116
|
+
* After closing, you can reconnect by calling {@link connect} again on the same instance.
|
|
117
117
|
*/
|
|
118
118
|
async close() {
|
|
119
119
|
if (this._client === void 0) {
|