@arkstack/filesystem 0.5.1 → 0.5.3
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 +68 -0
- package/dist/commands/StorageLinkCommand.d.ts +1 -2
- package/dist/commands/StorageLinkCommand.js +1 -3
- package/dist/index.d.ts +394 -7
- package/dist/index.js +2 -2
- package/dist/{src-wGEPrkk5.js → src-DNh_3BpA.js} +201 -48
- package/package.json +6 -5
- package/dist/commands/StorageLinkCommand.js.map +0 -1
- package/dist/src-wGEPrkk5.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,3 +1,71 @@
|
|
|
1
1
|
# @arkstack/filesystem
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@arkstack/filesystem)
|
|
4
|
+
|
|
3
5
|
Filesystem module for Arkstack, providing shared file storage and filesystem utitlities for the framework.
|
|
6
|
+
|
|
7
|
+
## Custom Drivers
|
|
8
|
+
|
|
9
|
+
@arkstack/filesystem allows you to configure and use custom storage drivers.
|
|
10
|
+
|
|
11
|
+
**CloudinaryFileDriver.ts**
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import type { DriverContract, ObjectVisibility } from 'flydrive/types';
|
|
15
|
+
import type { CustomDiskConfig } from '@arkstack/filesystem';
|
|
16
|
+
|
|
17
|
+
export class CloudinaryFileDriver implements DriverContract {
|
|
18
|
+
constructor(private config?: CustomDiskConfig) {}
|
|
19
|
+
async exists(key: string) {}
|
|
20
|
+
async get(key: string) {}
|
|
21
|
+
async getStream(key: string) {}
|
|
22
|
+
async getBytes(key: string) {}
|
|
23
|
+
async getMetaData(key: string) {}
|
|
24
|
+
async getVisibility(): Promise<ObjectVisibility> {}
|
|
25
|
+
async getUrl(key: string) {}
|
|
26
|
+
async getSignedUrl(key: string) {}
|
|
27
|
+
async getSignedUploadUrl(key: string) {}
|
|
28
|
+
async setVisibility() {}
|
|
29
|
+
async put() {}
|
|
30
|
+
async putStream() {}
|
|
31
|
+
async copy() {}
|
|
32
|
+
async move() {}
|
|
33
|
+
async delete() {}
|
|
34
|
+
async deleteAll() {}
|
|
35
|
+
async listAll() {}
|
|
36
|
+
async bucket() {}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**src/config/filesystem.ts**
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { CloudinaryFileDriver } from '../CloudinaryFileDriver';
|
|
44
|
+
export default () => {
|
|
45
|
+
return {
|
|
46
|
+
default: 'images',
|
|
47
|
+
disks: {
|
|
48
|
+
images: {
|
|
49
|
+
//...Other Disks Here
|
|
50
|
+
driver: 'cloudinary',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
links: {},
|
|
54
|
+
custom_drivers: {
|
|
55
|
+
cloudinary: CloudinaryFileDriver,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
To improve type safety and auto complete, you may augment the `CustomDiskDriverRegistry`
|
|
62
|
+
|
|
63
|
+
**env.d.ts**
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
declare module '@arkstack/filesystem' {
|
|
67
|
+
interface CustomDiskDriverRegistry {
|
|
68
|
+
cloudinary: { cloud_name: string; api_key: string; api_secret: string };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as Storage } from "../src-
|
|
1
|
+
import { t as Storage } from "../src-DNh_3BpA.js";
|
|
2
2
|
import { Command } from "@h3ravel/musket";
|
|
3
3
|
//#region src/commands/StorageLinkCommand.ts
|
|
4
4
|
var StorageLinkCommand = class extends Command {
|
|
@@ -12,5 +12,3 @@ var StorageLinkCommand = class extends Command {
|
|
|
12
12
|
};
|
|
13
13
|
//#endregion
|
|
14
14
|
export { StorageLinkCommand };
|
|
15
|
-
|
|
16
|
-
//# sourceMappingURL=StorageLinkCommand.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,41 +1,291 @@
|
|
|
1
1
|
import { DriveDirectory, DriveFile, DriveManager } from "flydrive";
|
|
2
2
|
import { Readable } from "node:stream";
|
|
3
3
|
import { DriverContract, ObjectMetaData, ObjectVisibility, SignedURLOptions, WriteOptions } from "flydrive/types";
|
|
4
|
+
import { GCSDriverOptions } from "flydrive/drivers/gcs/types";
|
|
4
5
|
|
|
5
|
-
//#region src/
|
|
6
|
+
//#region src/types.d.ts
|
|
6
7
|
interface FileLike {
|
|
7
8
|
originalname: string;
|
|
8
9
|
buffer: Buffer;
|
|
9
10
|
mimetype: string;
|
|
10
11
|
}
|
|
11
|
-
|
|
12
|
+
interface CustomDiskDriverRegistry {}
|
|
13
|
+
type GcsDiskDriverConfig = GCSDriverOptions;
|
|
14
|
+
interface FtpDriverConfig {
|
|
15
|
+
host: string;
|
|
16
|
+
username: string;
|
|
17
|
+
password: string;
|
|
18
|
+
port?: number;
|
|
19
|
+
verbose?: boolean | undefined;
|
|
20
|
+
privateKey?: string | undefined;
|
|
21
|
+
}
|
|
22
|
+
interface S3DriverConfig {
|
|
23
|
+
credentials?: {
|
|
24
|
+
accessKeyId: string;
|
|
25
|
+
secretAccessKey: string;
|
|
26
|
+
sessionToken?: string | undefined;
|
|
27
|
+
credentialScope?: string | undefined;
|
|
28
|
+
accountId?: string | undefined;
|
|
29
|
+
};
|
|
30
|
+
url?: string;
|
|
31
|
+
key?: string;
|
|
32
|
+
secret?: string;
|
|
33
|
+
endpoint?: string;
|
|
34
|
+
region?: string;
|
|
35
|
+
bucket: string;
|
|
36
|
+
visibility: ObjectVisibility;
|
|
37
|
+
cdnUrl?: string;
|
|
38
|
+
}
|
|
39
|
+
interface LocalDriverConfig {
|
|
40
|
+
root?: string;
|
|
41
|
+
location?: string | URL;
|
|
42
|
+
visibility: ObjectVisibility;
|
|
43
|
+
url?: string;
|
|
44
|
+
}
|
|
45
|
+
type CustomDiskConfig = keyof CustomDiskDriverRegistry extends never ? {
|
|
46
|
+
driver: string;
|
|
47
|
+
[key: string]: any;
|
|
48
|
+
} : { [K in keyof CustomDiskDriverRegistry]: CustomDiskDriverRegistry[K] & {
|
|
49
|
+
driver: K;
|
|
50
|
+
} }[keyof CustomDiskDriverRegistry];
|
|
51
|
+
type DiskConfig = LocalDriverConfig & {
|
|
52
|
+
driver: 'local' | 'public';
|
|
53
|
+
} | FtpDriverConfig & {
|
|
54
|
+
driver: 'ftp';
|
|
55
|
+
} | S3DriverConfig & {
|
|
56
|
+
driver: 's3';
|
|
57
|
+
} | CustomDiskConfig;
|
|
58
|
+
type DriverConfig<K extends 'ftp' | 'local' | 'gcs' | 's3' | (string & {}) = string & {}> = K extends 'ftp' ? FtpDriverConfig : K extends 's3' ? S3DriverConfig : K extends 'gcs' ? GcsDiskDriverConfig : K extends 'local' ? LocalDriverConfig : K extends keyof CustomDiskDriverRegistry ? CustomDiskDriverRegistry[K] : DiskConfig;
|
|
59
|
+
type KnownDisks = {
|
|
60
|
+
local: LocalDriverConfig & {
|
|
61
|
+
driver: 'local';
|
|
62
|
+
};
|
|
63
|
+
public: LocalDriverConfig & {
|
|
64
|
+
driver: 'local';
|
|
65
|
+
};
|
|
66
|
+
ftp: FtpDriverConfig & {
|
|
67
|
+
driver: 'ftp';
|
|
68
|
+
};
|
|
69
|
+
gcs: GcsDiskDriverConfig & {
|
|
70
|
+
driver: 'gcs';
|
|
71
|
+
};
|
|
72
|
+
s3: S3DriverConfig & {
|
|
73
|
+
driver: 's3';
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
interface FilesystemConfig {
|
|
77
|
+
default: 'local' | 'ftp' | 'gcs' | 's3' | keyof CustomDiskDriverRegistry | (string & {});
|
|
78
|
+
disks: KnownDisks & CustomDiskDriverRegistry;
|
|
79
|
+
links: Record<string, string>;
|
|
80
|
+
custom_drivers?: Record<keyof CustomDiskDriverRegistry | (string & {}), DriverContract | (new (config?: CustomDiskConfig) => DriverContract)>;
|
|
81
|
+
fileNameGenerator?: (originalName: string) => string;
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/Storage.d.ts
|
|
85
|
+
declare class Storage<D extends keyof KnownDisks | keyof CustomDiskDriverRegistry = keyof KnownDisks | keyof CustomDiskDriverRegistry> implements DriverContract {
|
|
12
86
|
driver: DriveManager<any>;
|
|
13
87
|
services: Record<string, () => DriverContract>;
|
|
14
|
-
diskName:
|
|
88
|
+
diskName: D;
|
|
89
|
+
driverName: FilesystemConfig['disks'][D]['driver'];
|
|
15
90
|
constructor();
|
|
16
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Static method to get a disk instance directly from the Storage class without needing to instantiate it first.
|
|
93
|
+
*
|
|
94
|
+
* @param diskName The name of the disk to use. If not provided, the default disk will be used.
|
|
95
|
+
* @returns A Storage instance
|
|
96
|
+
*/
|
|
97
|
+
static disk<K extends keyof KnownDisks | keyof CustomDiskDriverRegistry>(diskName?: K): Storage<K>;
|
|
98
|
+
/**
|
|
99
|
+
* Generate a unique name for the file based on random numbers and original extension
|
|
100
|
+
*
|
|
101
|
+
* @param file The file object containing the original name
|
|
102
|
+
* @returns A unique file name
|
|
103
|
+
*/
|
|
17
104
|
static generateName: (file: {
|
|
18
105
|
name?: string;
|
|
19
106
|
originalname?: string;
|
|
20
107
|
}) => string;
|
|
108
|
+
/**
|
|
109
|
+
* Save the file to the storage and return the public URL and the file path
|
|
110
|
+
*
|
|
111
|
+
* @param file The file object containing the file data
|
|
112
|
+
* @param filePath The path where the file should be saved
|
|
113
|
+
* @param fileName The name to save the file as (optional)
|
|
114
|
+
* @returns A tuple containing the public URL and the file path
|
|
115
|
+
*/
|
|
21
116
|
static saveFile: (file: FileLike, filePath?: string, fileName?: string) => Promise<[string, string]>;
|
|
117
|
+
/**
|
|
118
|
+
* Save the file to the storage and return the public URL and the file path
|
|
119
|
+
*
|
|
120
|
+
* @param file The file object containing the file data
|
|
121
|
+
* @param filePath The path where the file should be saved
|
|
122
|
+
* @param fileName The name to save the file as (optional)
|
|
123
|
+
* @returns A tuple containing the public URL and the file path
|
|
124
|
+
*/
|
|
22
125
|
saveFile: (file: FileLike, filePath?: string, fileName?: string) => Promise<[string, string]>;
|
|
126
|
+
/**
|
|
127
|
+
* Return a boolean indicating if the file exists
|
|
128
|
+
*
|
|
129
|
+
* @param key
|
|
130
|
+
* @returns
|
|
131
|
+
*/
|
|
23
132
|
exists(key: string): Promise<boolean>;
|
|
133
|
+
/**
|
|
134
|
+
* Return contents of a object for the given key as a UTF-8 string.
|
|
135
|
+
* Should throw "E_CANNOT_READ_FILE" error when the file
|
|
136
|
+
* does not exists.
|
|
137
|
+
*
|
|
138
|
+
* @param key
|
|
139
|
+
* @returns
|
|
140
|
+
*/
|
|
24
141
|
get(key: string): Promise<string>;
|
|
142
|
+
/**
|
|
143
|
+
* Get the name of the disk currently in use.
|
|
144
|
+
*
|
|
145
|
+
* @returns
|
|
146
|
+
*/
|
|
147
|
+
getDiskName(): D;
|
|
148
|
+
/**
|
|
149
|
+
* Get the name of the driver currently in use.
|
|
150
|
+
*
|
|
151
|
+
* @returns
|
|
152
|
+
*/
|
|
153
|
+
getDriverName(): (KnownDisks & CustomDiskDriverRegistry)[D]["driver"];
|
|
154
|
+
/**
|
|
155
|
+
* Get the driver currently in use.
|
|
156
|
+
*
|
|
157
|
+
* @returns
|
|
158
|
+
*/
|
|
159
|
+
getDriver(): DriveManager<any>;
|
|
160
|
+
/**
|
|
161
|
+
* Return contents of a object for the given key as a Readable stream.
|
|
162
|
+
* Should throw "E_CANNOT_READ_FILE" error when the file
|
|
163
|
+
* does not exists.
|
|
164
|
+
*
|
|
165
|
+
* @param key
|
|
166
|
+
* @returns
|
|
167
|
+
*/
|
|
25
168
|
getStream(key: string): Promise<Readable>;
|
|
169
|
+
/**
|
|
170
|
+
* Return contents of an object for the given key as an Uint8Array.
|
|
171
|
+
* Should throw "E_CANNOT_READ_FILE" error when the file
|
|
172
|
+
* does not exists.
|
|
173
|
+
*
|
|
174
|
+
* @param key
|
|
175
|
+
* @returns
|
|
176
|
+
*/
|
|
26
177
|
getBytes(key: string): Promise<Uint8Array>;
|
|
178
|
+
/**
|
|
179
|
+
* Return metadata of an object for the given key.
|
|
180
|
+
*
|
|
181
|
+
* @param key
|
|
182
|
+
* @returns
|
|
183
|
+
*/
|
|
27
184
|
getMetaData(key: string): Promise<ObjectMetaData>;
|
|
185
|
+
/**
|
|
186
|
+
* Return the visibility of the file
|
|
187
|
+
*
|
|
188
|
+
* @param key
|
|
189
|
+
* @returns
|
|
190
|
+
*/
|
|
28
191
|
getVisibility(key: string): Promise<ObjectVisibility>;
|
|
192
|
+
/**
|
|
193
|
+
* Return the public URL to access the file
|
|
194
|
+
*
|
|
195
|
+
* @param key
|
|
196
|
+
* @returns
|
|
197
|
+
*/
|
|
29
198
|
getUrl(key: string): Promise<string>;
|
|
199
|
+
/**
|
|
200
|
+
* Return the signed/temporary URL to access the file
|
|
201
|
+
*
|
|
202
|
+
* @param key
|
|
203
|
+
* @param options
|
|
204
|
+
* @returns
|
|
205
|
+
*/
|
|
30
206
|
getSignedUrl(key: string, options?: SignedURLOptions): Promise<string>;
|
|
207
|
+
/**
|
|
208
|
+
* Return the signed/temporary URL that can be used to directly upload
|
|
209
|
+
* the file contents to the storage.
|
|
210
|
+
*
|
|
211
|
+
* @param key
|
|
212
|
+
* @param options
|
|
213
|
+
* @returns
|
|
214
|
+
*/
|
|
31
215
|
getSignedUploadUrl(key: string, options?: SignedURLOptions): Promise<string>;
|
|
216
|
+
/**
|
|
217
|
+
* Update the visibility of the file
|
|
218
|
+
*
|
|
219
|
+
* @param key
|
|
220
|
+
* @param visibility
|
|
221
|
+
* @returns
|
|
222
|
+
*/
|
|
32
223
|
setVisibility(key: string, visibility: ObjectVisibility): Promise<void>;
|
|
224
|
+
/**
|
|
225
|
+
* Write object to the destination with the provided
|
|
226
|
+
* contents.
|
|
227
|
+
*
|
|
228
|
+
* @param key
|
|
229
|
+
* @param contents
|
|
230
|
+
* @param options
|
|
231
|
+
* @returns
|
|
232
|
+
*/
|
|
33
233
|
put(key: string, contents: string | Uint8Array | FileLike, options?: WriteOptions): Promise<void>;
|
|
234
|
+
/**
|
|
235
|
+
* Write object to the destination with the provided
|
|
236
|
+
* contents as a readable stream
|
|
237
|
+
*
|
|
238
|
+
* @param key
|
|
239
|
+
* @param contents
|
|
240
|
+
* @param options
|
|
241
|
+
* @returns
|
|
242
|
+
*/
|
|
34
243
|
putStream(key: string, contents: Readable, options?: WriteOptions): Promise<void>;
|
|
244
|
+
/**
|
|
245
|
+
* Copy the file from within the disk root location. Both
|
|
246
|
+
* the "source" and "destination" will be the key names
|
|
247
|
+
* and not absolute paths.
|
|
248
|
+
*
|
|
249
|
+
* @param source
|
|
250
|
+
* @param destination
|
|
251
|
+
* @param options
|
|
252
|
+
* @returns
|
|
253
|
+
*/
|
|
35
254
|
copy(source: string, destination: string, options?: WriteOptions): Promise<void>;
|
|
255
|
+
/**
|
|
256
|
+
* Move the file from within the disk root location. Both
|
|
257
|
+
* the "source" and "destination" will be the key names
|
|
258
|
+
* and not absolute paths.
|
|
259
|
+
*
|
|
260
|
+
* @param source
|
|
261
|
+
* @param destination
|
|
262
|
+
* @param options
|
|
263
|
+
* @returns
|
|
264
|
+
*/
|
|
36
265
|
move(source: string, destination: string, options?: WriteOptions): Promise<void>;
|
|
266
|
+
/**
|
|
267
|
+
* Delete the file for the given key. Should not throw
|
|
268
|
+
* error when file does not exist in first place
|
|
269
|
+
*
|
|
270
|
+
* @param key
|
|
271
|
+
* @returns
|
|
272
|
+
*/
|
|
37
273
|
delete(key: string): Promise<void>;
|
|
274
|
+
/**
|
|
275
|
+
* Delete the files and directories matching the provided prefix.
|
|
276
|
+
*
|
|
277
|
+
* @param prefix
|
|
278
|
+
* @returns
|
|
279
|
+
*/
|
|
38
280
|
deleteAll(prefix: string): Promise<void>;
|
|
281
|
+
/**
|
|
282
|
+
* The list all method must return an array of objects with
|
|
283
|
+
* the ability to paginate results (if supported).
|
|
284
|
+
*
|
|
285
|
+
* @param prefix
|
|
286
|
+
* @param options
|
|
287
|
+
* @returns
|
|
288
|
+
*/
|
|
39
289
|
listAll(prefix: string, options?: {
|
|
40
290
|
recursive?: boolean;
|
|
41
291
|
paginationToken?: string;
|
|
@@ -43,14 +293,151 @@ declare class Storage implements DriverContract {
|
|
|
43
293
|
paginationToken?: string;
|
|
44
294
|
objects: Iterable<DriveFile | DriveDirectory>;
|
|
45
295
|
}>;
|
|
296
|
+
/**
|
|
297
|
+
* Switch bucket at runtime if supported.
|
|
298
|
+
*
|
|
299
|
+
* @param bucket
|
|
300
|
+
* @returns
|
|
301
|
+
*/
|
|
46
302
|
bucket(bucket: string): DriverContract;
|
|
303
|
+
/**
|
|
304
|
+
* Create symbolic links for all configured links in the application configuration.
|
|
305
|
+
*
|
|
306
|
+
* @param param0
|
|
307
|
+
*/
|
|
47
308
|
static link({
|
|
48
309
|
force
|
|
49
310
|
}?: {
|
|
50
311
|
force?: boolean;
|
|
51
312
|
}): void;
|
|
52
|
-
private driversMap;
|
|
53
313
|
}
|
|
54
314
|
//#endregion
|
|
55
|
-
|
|
56
|
-
|
|
315
|
+
//#region src/FtpDriver.d.ts
|
|
316
|
+
declare class FtpDriver implements DriverContract {
|
|
317
|
+
private config;
|
|
318
|
+
constructor(config: string | {
|
|
319
|
+
host: string;
|
|
320
|
+
username: string;
|
|
321
|
+
password: string;
|
|
322
|
+
port?: number;
|
|
323
|
+
verbose?: boolean;
|
|
324
|
+
privateKey?: string;
|
|
325
|
+
});
|
|
326
|
+
getConfig(): {
|
|
327
|
+
host: string;
|
|
328
|
+
username: string;
|
|
329
|
+
password: string;
|
|
330
|
+
port?: number;
|
|
331
|
+
verbose?: boolean;
|
|
332
|
+
privateKey?: string;
|
|
333
|
+
};
|
|
334
|
+
private init;
|
|
335
|
+
private load;
|
|
336
|
+
/**
|
|
337
|
+
* Return a boolean value indicating if the file exists
|
|
338
|
+
* or not.
|
|
339
|
+
*/
|
|
340
|
+
exists(key: string): Promise<boolean>;
|
|
341
|
+
/**
|
|
342
|
+
* Return the file contents as a UTF-8 string. Throw an exception
|
|
343
|
+
* if the file is missing.
|
|
344
|
+
*/
|
|
345
|
+
get(key: string): Promise<string>;
|
|
346
|
+
/**
|
|
347
|
+
* Return the file contents as a Readable stream. Throw an exception
|
|
348
|
+
* if the file is missing.
|
|
349
|
+
*/
|
|
350
|
+
getStream(key: string): Promise<Readable>;
|
|
351
|
+
/**
|
|
352
|
+
* Return the file contents as a Uint8Array. Throw an exception
|
|
353
|
+
* if the file is missing.
|
|
354
|
+
*/
|
|
355
|
+
getBytes(key: string): Promise<Uint8Array>;
|
|
356
|
+
/**
|
|
357
|
+
* Return metadata of the file. Throw an exception
|
|
358
|
+
* if the file is missing.
|
|
359
|
+
*/
|
|
360
|
+
getMetaData(key: string): Promise<ObjectMetaData>;
|
|
361
|
+
/**
|
|
362
|
+
* Return visibility of the file. Infer visibility from the initial
|
|
363
|
+
* config, when the driver does not support the concept of visibility.
|
|
364
|
+
*/
|
|
365
|
+
getVisibility(key: string): Promise<ObjectVisibility>;
|
|
366
|
+
/**
|
|
367
|
+
* Return the public URL of the file. Throw an exception when the driver
|
|
368
|
+
* does not support generating URLs.
|
|
369
|
+
*/
|
|
370
|
+
getUrl(key: string): Promise<string>;
|
|
371
|
+
/**
|
|
372
|
+
* Return the signed URL to serve a private file. Throw exception
|
|
373
|
+
* when the driver does not support generating URLs.
|
|
374
|
+
*/
|
|
375
|
+
getSignedUrl(key: string, options?: SignedURLOptions): Promise<string>;
|
|
376
|
+
/**
|
|
377
|
+
* Return the signed/temporary URL that can be used to directly upload
|
|
378
|
+
* the file contents to the storage.
|
|
379
|
+
*/
|
|
380
|
+
getSignedUploadUrl(key: string, options?: SignedURLOptions): Promise<string>;
|
|
381
|
+
/**
|
|
382
|
+
* Update the visibility of the file. Result in a NOOP
|
|
383
|
+
* when the driver does not support the concept of
|
|
384
|
+
* visibility.
|
|
385
|
+
*/
|
|
386
|
+
setVisibility(key: string, visibility: ObjectVisibility): Promise<void>;
|
|
387
|
+
/**
|
|
388
|
+
* Create a new file or update an existing file. The contents
|
|
389
|
+
* will be a UTF-8 string or "Uint8Array".
|
|
390
|
+
*/
|
|
391
|
+
put(key: string, contents: string | Uint8Array, options?: WriteOptions): Promise<void>;
|
|
392
|
+
/**
|
|
393
|
+
* Create a new file or update an existing file. The contents
|
|
394
|
+
* will be a Readable stream.
|
|
395
|
+
*/
|
|
396
|
+
putStream(key: string, contents: Readable, options?: WriteOptions): Promise<void>;
|
|
397
|
+
/**
|
|
398
|
+
* Copy the existing file to the destination. Make sure the new file
|
|
399
|
+
* has the same visibility as the existing file. It might require
|
|
400
|
+
* manually fetching the visibility of the "source" file.
|
|
401
|
+
*/
|
|
402
|
+
copy(source: string, destination: string, options?: WriteOptions): Promise<void>;
|
|
403
|
+
/**
|
|
404
|
+
* Move the existing file to the destination. Make sure the new file
|
|
405
|
+
* has the same visibility as the existing file. It might require
|
|
406
|
+
* manually fetching the visibility of the "source" file.
|
|
407
|
+
*/
|
|
408
|
+
move(source: string, destination: string, options?: WriteOptions): Promise<void>;
|
|
409
|
+
/**
|
|
410
|
+
* Delete an existing file. Do not throw an error if the
|
|
411
|
+
* file is already missing
|
|
412
|
+
*/
|
|
413
|
+
delete(key: string): Promise<void>;
|
|
414
|
+
/**
|
|
415
|
+
* Delete all files inside a folder. Do not throw an error
|
|
416
|
+
* if the folder does not exist or is empty.
|
|
417
|
+
*/
|
|
418
|
+
deleteAll(prefix: string): Promise<void>;
|
|
419
|
+
/**
|
|
420
|
+
* Switch bucket at runtime if supported.
|
|
421
|
+
*/
|
|
422
|
+
bucket(config: string | {
|
|
423
|
+
host: string;
|
|
424
|
+
username: string;
|
|
425
|
+
password: string;
|
|
426
|
+
port?: number;
|
|
427
|
+
verbose?: boolean;
|
|
428
|
+
privateKey?: string;
|
|
429
|
+
}): DriverContract;
|
|
430
|
+
/**
|
|
431
|
+
* List all files from a given folder or the root of the storage.
|
|
432
|
+
* Do not throw an error if the request folder does not exist.
|
|
433
|
+
*/
|
|
434
|
+
listAll(prefix: string, options?: {
|
|
435
|
+
recursive?: boolean;
|
|
436
|
+
paginationToken?: string;
|
|
437
|
+
}): Promise<{
|
|
438
|
+
paginationToken?: string;
|
|
439
|
+
objects: Iterable<DriveFile | DriveDirectory>;
|
|
440
|
+
}>;
|
|
441
|
+
}
|
|
442
|
+
//#endregion
|
|
443
|
+
export { CustomDiskConfig, CustomDiskDriverRegistry, DiskConfig, DriverConfig, FileLike, FilesystemConfig, FtpDriver, FtpDriverConfig, GcsDiskDriverConfig, KnownDisks, LocalDriverConfig, S3DriverConfig, Storage };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as Storage } from "./src-
|
|
2
|
-
export { Storage };
|
|
1
|
+
import { n as FtpDriver, t as Storage } from "./src-DNh_3BpA.js";
|
|
2
|
+
export { FtpDriver, Storage };
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { DriveDirectory, DriveFile, DriveManager } from "flydrive";
|
|
2
|
-
import { appUrl, config } from "@arkstack/common";
|
|
3
2
|
import { createReadStream, createWriteStream, rmSync, symlinkSync } from "node:fs";
|
|
3
|
+
import { Arkstack } from "@arkstack/contract";
|
|
4
4
|
import { FSDriver } from "flydrive/drivers/fs";
|
|
5
5
|
import Client from "ssh2-sftp-client";
|
|
6
6
|
import { Readable } from "node:stream";
|
|
7
7
|
import path from "node:path";
|
|
8
|
-
import {
|
|
8
|
+
import { GCSDriver } from "flydrive/drivers/gcs";
|
|
9
9
|
import { S3Driver } from "flydrive/drivers/s3";
|
|
10
|
+
import { appUrl, config } from "@arkstack/common";
|
|
11
|
+
import { Logger } from "@h3ravel/shared";
|
|
10
12
|
//#region src/FtpDriver.ts
|
|
11
13
|
var FtpDriver = class FtpDriver {
|
|
12
14
|
config;
|
|
@@ -235,19 +237,112 @@ var FtpDriver = class FtpDriver {
|
|
|
235
237
|
}
|
|
236
238
|
};
|
|
237
239
|
//#endregion
|
|
238
|
-
//#region src/
|
|
240
|
+
//#region src/Driver.ts
|
|
241
|
+
var Driver = class Driver {
|
|
242
|
+
config;
|
|
243
|
+
static customDrivers = /* @__PURE__ */ new Map();
|
|
244
|
+
constructor(config) {
|
|
245
|
+
this.config = config;
|
|
246
|
+
}
|
|
247
|
+
static make(config) {
|
|
248
|
+
const name = config.driver;
|
|
249
|
+
if (![
|
|
250
|
+
"local",
|
|
251
|
+
"ftp",
|
|
252
|
+
"s3",
|
|
253
|
+
"gcs"
|
|
254
|
+
].includes(name) && !this.customDrivers.has(name)) throw new Error(`Unsupported driver: ${name}`);
|
|
255
|
+
const driver = new Driver(config);
|
|
256
|
+
if (this.customDrivers.has(name)) return driver.custom(name);
|
|
257
|
+
return driver[name].call(driver);
|
|
258
|
+
}
|
|
259
|
+
local() {
|
|
260
|
+
const config = this.config;
|
|
261
|
+
return new FSDriver({
|
|
262
|
+
location: config.location ?? new URL(config.root, import.meta.url),
|
|
263
|
+
visibility: config.visibility ?? "public",
|
|
264
|
+
urlBuilder: {
|
|
265
|
+
async generateURL(key, _path) {
|
|
266
|
+
if (config.url) return `${config.url}/key`.replace(/^(https?:\/)\/+/, "$1/").replace(/([^:]\/)\/+/g, "$1");
|
|
267
|
+
return appUrl(key);
|
|
268
|
+
},
|
|
269
|
+
async generateSignedURL(key, _path, _opts) {
|
|
270
|
+
if (config.url) return `${config.url}/key`.replace(/^(https?:\/)\/+/, "$1/").replace(/([^:]\/)\/+/g, "$1");
|
|
271
|
+
return appUrl(key);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
s3() {
|
|
277
|
+
const config = this.config;
|
|
278
|
+
return new S3Driver({
|
|
279
|
+
credentials: config.credentials ?? {
|
|
280
|
+
accessKeyId: config.key,
|
|
281
|
+
secretAccessKey: config.secret
|
|
282
|
+
},
|
|
283
|
+
endpoint: config.endpoint,
|
|
284
|
+
region: config.region,
|
|
285
|
+
bucket: config.bucket,
|
|
286
|
+
visibility: "private",
|
|
287
|
+
cdnUrl: config.cdnUrl ?? config.url
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
gcs() {
|
|
291
|
+
const { driver: _driver, ...options } = this.config;
|
|
292
|
+
return new GCSDriver(options);
|
|
293
|
+
}
|
|
294
|
+
ftp() {
|
|
295
|
+
const config = this.config;
|
|
296
|
+
return new FtpDriver({
|
|
297
|
+
host: config.host,
|
|
298
|
+
username: config.username,
|
|
299
|
+
password: config.password,
|
|
300
|
+
port: config.port,
|
|
301
|
+
verbose: config.verbose,
|
|
302
|
+
privateKey: config.privateKey
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
custom(name) {
|
|
306
|
+
if (!Driver.customDrivers.has(name)) throw new Error(`Unsupported driver: ${name} has not been registered`);
|
|
307
|
+
const DriverInstance = Driver.customDrivers.get(name);
|
|
308
|
+
if (typeof DriverInstance === "function") {
|
|
309
|
+
const config = this.config;
|
|
310
|
+
return new DriverInstance(config);
|
|
311
|
+
}
|
|
312
|
+
return DriverInstance;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Register a new custom driver
|
|
316
|
+
*
|
|
317
|
+
* @param name
|
|
318
|
+
* @param driver
|
|
319
|
+
*/
|
|
320
|
+
static registerDriver(name, driver) {
|
|
321
|
+
Driver.customDrivers.set(name, driver);
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Unregister a new custom driver
|
|
325
|
+
*
|
|
326
|
+
* @param name
|
|
327
|
+
*/
|
|
328
|
+
static removeDriver(name) {
|
|
329
|
+
Driver.customDrivers.delete(name);
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
//#endregion
|
|
333
|
+
//#region src/Storage.ts
|
|
239
334
|
var Storage = class Storage {
|
|
240
335
|
driver;
|
|
241
336
|
services = {};
|
|
242
337
|
diskName;
|
|
338
|
+
driverName;
|
|
243
339
|
constructor() {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
this.services[diskName] = () => driverFactory(diskConfig);
|
|
249
|
-
}
|
|
340
|
+
const disks = Object.entries(config("filesystem.disks", {}));
|
|
341
|
+
const customDrivers = config("filesystem.custom_drivers", {});
|
|
342
|
+
for (const [name, driver] of Object.entries(customDrivers)) Driver.registerDriver(name, driver);
|
|
343
|
+
for (const [disk, config] of disks) this.services[disk] = () => Driver.make(config);
|
|
250
344
|
this.diskName = config("filesystem.default");
|
|
345
|
+
this.driverName = config(`filesystem.disks.${this.diskName}.driver`);
|
|
251
346
|
this.driver = new DriveManager({
|
|
252
347
|
default: config("filesystem.default"),
|
|
253
348
|
services: this.services
|
|
@@ -263,6 +358,7 @@ var Storage = class Storage {
|
|
|
263
358
|
const storage = new Storage();
|
|
264
359
|
if (diskName) {
|
|
265
360
|
storage.diskName = diskName;
|
|
361
|
+
storage.driverName = config(`filesystem.disks.${diskName}.driver`);
|
|
266
362
|
storage.driver = new DriveManager({
|
|
267
363
|
default: diskName,
|
|
268
364
|
services: storage.services
|
|
@@ -306,10 +402,13 @@ var Storage = class Storage {
|
|
|
306
402
|
if (file instanceof File && !file.buffer) file.buffer = Buffer.from(await file.arrayBuffer());
|
|
307
403
|
await drive.put(path.join(filePath, name), file.buffer);
|
|
308
404
|
const url = await drive.getUrl(path.join(filePath, name));
|
|
309
|
-
return [url, this.
|
|
405
|
+
return [url, this.driverName === "local" ? path.join(filePath, name) : url];
|
|
310
406
|
};
|
|
311
407
|
/**
|
|
312
408
|
* Return a boolean indicating if the file exists
|
|
409
|
+
*
|
|
410
|
+
* @param key
|
|
411
|
+
* @returns
|
|
313
412
|
*/
|
|
314
413
|
exists(key) {
|
|
315
414
|
return this.driver.use().exists(key);
|
|
@@ -318,14 +417,44 @@ var Storage = class Storage {
|
|
|
318
417
|
* Return contents of a object for the given key as a UTF-8 string.
|
|
319
418
|
* Should throw "E_CANNOT_READ_FILE" error when the file
|
|
320
419
|
* does not exists.
|
|
420
|
+
*
|
|
421
|
+
* @param key
|
|
422
|
+
* @returns
|
|
321
423
|
*/
|
|
322
424
|
get(key) {
|
|
323
425
|
return this.driver.use().get(key);
|
|
324
426
|
}
|
|
325
427
|
/**
|
|
428
|
+
* Get the name of the disk currently in use.
|
|
429
|
+
*
|
|
430
|
+
* @returns
|
|
431
|
+
*/
|
|
432
|
+
getDiskName() {
|
|
433
|
+
return this.diskName;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Get the name of the driver currently in use.
|
|
437
|
+
*
|
|
438
|
+
* @returns
|
|
439
|
+
*/
|
|
440
|
+
getDriverName() {
|
|
441
|
+
return this.driverName;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Get the driver currently in use.
|
|
445
|
+
*
|
|
446
|
+
* @returns
|
|
447
|
+
*/
|
|
448
|
+
getDriver() {
|
|
449
|
+
return this.driver;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
326
452
|
* Return contents of a object for the given key as a Readable stream.
|
|
327
453
|
* Should throw "E_CANNOT_READ_FILE" error when the file
|
|
328
454
|
* does not exists.
|
|
455
|
+
*
|
|
456
|
+
* @param key
|
|
457
|
+
* @returns
|
|
329
458
|
*/
|
|
330
459
|
getStream(key) {
|
|
331
460
|
return this.driver.use().getStream(key);
|
|
@@ -334,30 +463,46 @@ var Storage = class Storage {
|
|
|
334
463
|
* Return contents of an object for the given key as an Uint8Array.
|
|
335
464
|
* Should throw "E_CANNOT_READ_FILE" error when the file
|
|
336
465
|
* does not exists.
|
|
466
|
+
*
|
|
467
|
+
* @param key
|
|
468
|
+
* @returns
|
|
337
469
|
*/
|
|
338
470
|
getBytes(key) {
|
|
339
471
|
return this.driver.use().getBytes(key);
|
|
340
472
|
}
|
|
341
473
|
/**
|
|
342
474
|
* Return metadata of an object for the given key.
|
|
475
|
+
*
|
|
476
|
+
* @param key
|
|
477
|
+
* @returns
|
|
343
478
|
*/
|
|
344
479
|
getMetaData(key) {
|
|
345
480
|
return this.driver.use().getMetaData(key);
|
|
346
481
|
}
|
|
347
482
|
/**
|
|
348
483
|
* Return the visibility of the file
|
|
484
|
+
*
|
|
485
|
+
* @param key
|
|
486
|
+
* @returns
|
|
349
487
|
*/
|
|
350
488
|
getVisibility(key) {
|
|
351
489
|
return this.driver.use().getVisibility(key);
|
|
352
490
|
}
|
|
353
491
|
/**
|
|
354
492
|
* Return the public URL to access the file
|
|
493
|
+
*
|
|
494
|
+
* @param key
|
|
495
|
+
* @returns
|
|
355
496
|
*/
|
|
356
497
|
getUrl(key) {
|
|
357
498
|
return this.driver.use().getUrl(key);
|
|
358
499
|
}
|
|
359
500
|
/**
|
|
360
501
|
* Return the signed/temporary URL to access the file
|
|
502
|
+
*
|
|
503
|
+
* @param key
|
|
504
|
+
* @param options
|
|
505
|
+
* @returns
|
|
361
506
|
*/
|
|
362
507
|
getSignedUrl(key, options) {
|
|
363
508
|
return this.driver.use().getSignedUrl(key, options);
|
|
@@ -365,12 +510,20 @@ var Storage = class Storage {
|
|
|
365
510
|
/**
|
|
366
511
|
* Return the signed/temporary URL that can be used to directly upload
|
|
367
512
|
* the file contents to the storage.
|
|
513
|
+
*
|
|
514
|
+
* @param key
|
|
515
|
+
* @param options
|
|
516
|
+
* @returns
|
|
368
517
|
*/
|
|
369
518
|
getSignedUploadUrl(key, options) {
|
|
370
519
|
return this.driver.use().getSignedUploadUrl(key, options);
|
|
371
520
|
}
|
|
372
521
|
/**
|
|
373
522
|
* Update the visibility of the file
|
|
523
|
+
*
|
|
524
|
+
* @param key
|
|
525
|
+
* @param visibility
|
|
526
|
+
* @returns
|
|
374
527
|
*/
|
|
375
528
|
setVisibility(key, visibility) {
|
|
376
529
|
return this.driver.use().setVisibility(key, visibility);
|
|
@@ -378,6 +531,11 @@ var Storage = class Storage {
|
|
|
378
531
|
/**
|
|
379
532
|
* Write object to the destination with the provided
|
|
380
533
|
* contents.
|
|
534
|
+
*
|
|
535
|
+
* @param key
|
|
536
|
+
* @param contents
|
|
537
|
+
* @param options
|
|
538
|
+
* @returns
|
|
381
539
|
*/
|
|
382
540
|
put(key, contents, options) {
|
|
383
541
|
if (!(contents instanceof Uint8Array) && typeof contents !== "string") contents = contents.buffer;
|
|
@@ -386,6 +544,11 @@ var Storage = class Storage {
|
|
|
386
544
|
/**
|
|
387
545
|
* Write object to the destination with the provided
|
|
388
546
|
* contents as a readable stream
|
|
547
|
+
*
|
|
548
|
+
* @param key
|
|
549
|
+
* @param contents
|
|
550
|
+
* @param options
|
|
551
|
+
* @returns
|
|
389
552
|
*/
|
|
390
553
|
putStream(key, contents, options) {
|
|
391
554
|
return this.driver.use().putStream(key, contents, options);
|
|
@@ -394,6 +557,11 @@ var Storage = class Storage {
|
|
|
394
557
|
* Copy the file from within the disk root location. Both
|
|
395
558
|
* the "source" and "destination" will be the key names
|
|
396
559
|
* and not absolute paths.
|
|
560
|
+
*
|
|
561
|
+
* @param source
|
|
562
|
+
* @param destination
|
|
563
|
+
* @param options
|
|
564
|
+
* @returns
|
|
397
565
|
*/
|
|
398
566
|
copy(source, destination, options) {
|
|
399
567
|
return this.driver.use().copy(source, destination, options);
|
|
@@ -402,6 +570,11 @@ var Storage = class Storage {
|
|
|
402
570
|
* Move the file from within the disk root location. Both
|
|
403
571
|
* the "source" and "destination" will be the key names
|
|
404
572
|
* and not absolute paths.
|
|
573
|
+
*
|
|
574
|
+
* @param source
|
|
575
|
+
* @param destination
|
|
576
|
+
* @param options
|
|
577
|
+
* @returns
|
|
405
578
|
*/
|
|
406
579
|
move(source, destination, options) {
|
|
407
580
|
return this.driver.use().move(source, destination, options);
|
|
@@ -409,12 +582,18 @@ var Storage = class Storage {
|
|
|
409
582
|
/**
|
|
410
583
|
* Delete the file for the given key. Should not throw
|
|
411
584
|
* error when file does not exist in first place
|
|
585
|
+
*
|
|
586
|
+
* @param key
|
|
587
|
+
* @returns
|
|
412
588
|
*/
|
|
413
589
|
delete(key) {
|
|
414
590
|
return this.driver.use().delete(key);
|
|
415
591
|
}
|
|
416
592
|
/**
|
|
417
593
|
* Delete the files and directories matching the provided prefix.
|
|
594
|
+
*
|
|
595
|
+
* @param prefix
|
|
596
|
+
* @returns
|
|
418
597
|
*/
|
|
419
598
|
deleteAll(prefix) {
|
|
420
599
|
return this.driver.use().deleteAll(prefix);
|
|
@@ -422,24 +601,33 @@ var Storage = class Storage {
|
|
|
422
601
|
/**
|
|
423
602
|
* The list all method must return an array of objects with
|
|
424
603
|
* the ability to paginate results (if supported).
|
|
604
|
+
*
|
|
605
|
+
* @param prefix
|
|
606
|
+
* @param options
|
|
607
|
+
* @returns
|
|
425
608
|
*/
|
|
426
609
|
listAll(prefix, options) {
|
|
427
610
|
return this.driver.use().listAll(prefix, options);
|
|
428
611
|
}
|
|
429
612
|
/**
|
|
430
613
|
* Switch bucket at runtime if supported.
|
|
614
|
+
*
|
|
615
|
+
* @param bucket
|
|
616
|
+
* @returns
|
|
431
617
|
*/
|
|
432
618
|
bucket(bucket) {
|
|
433
619
|
return this.driver.use().bucket(bucket);
|
|
434
620
|
}
|
|
435
621
|
/**
|
|
436
622
|
* Create symbolic links for all configured links in the application configuration.
|
|
623
|
+
*
|
|
624
|
+
* @param param0
|
|
437
625
|
*/
|
|
438
626
|
static link({ force = false } = {}) {
|
|
439
627
|
for (const link in config("filesystem.links")) {
|
|
440
628
|
const target = config("filesystem.links")[link];
|
|
441
|
-
const unlink = link.replace(
|
|
442
|
-
const untarget = target.replace(
|
|
629
|
+
const unlink = link.replace(Arkstack.rootDir(), "");
|
|
630
|
+
const untarget = target.replace(Arkstack.rootDir(), "");
|
|
443
631
|
try {
|
|
444
632
|
if (force) rmSync(link, {
|
|
445
633
|
recursive: true,
|
|
@@ -470,41 +658,6 @@ var Storage = class Storage {
|
|
|
470
658
|
}
|
|
471
659
|
}
|
|
472
660
|
}
|
|
473
|
-
driversMap = {
|
|
474
|
-
local: (conf) => new FSDriver({
|
|
475
|
-
location: new URL(conf.root, import.meta.url),
|
|
476
|
-
visibility: "public",
|
|
477
|
-
urlBuilder: {
|
|
478
|
-
async generateURL(key, _path) {
|
|
479
|
-
return appUrl(key);
|
|
480
|
-
},
|
|
481
|
-
async generateSignedURL(key, _path, _opts) {
|
|
482
|
-
return appUrl(key);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
}),
|
|
486
|
-
s3: (conf) => new S3Driver({
|
|
487
|
-
credentials: {
|
|
488
|
-
accessKeyId: conf.key,
|
|
489
|
-
secretAccessKey: conf.secret
|
|
490
|
-
},
|
|
491
|
-
endpoint: conf.endpoint,
|
|
492
|
-
region: conf.region,
|
|
493
|
-
bucket: conf.bucket,
|
|
494
|
-
visibility: "private",
|
|
495
|
-
cdnUrl: conf.url
|
|
496
|
-
}),
|
|
497
|
-
ftp: (conf) => new FtpDriver({
|
|
498
|
-
host: conf.host,
|
|
499
|
-
username: conf.username,
|
|
500
|
-
password: conf.password,
|
|
501
|
-
port: conf.port,
|
|
502
|
-
verbose: conf.verbose,
|
|
503
|
-
privateKey: conf.privateKey
|
|
504
|
-
})
|
|
505
|
-
};
|
|
506
661
|
};
|
|
507
662
|
//#endregion
|
|
508
|
-
export { Storage as t };
|
|
509
|
-
|
|
510
|
-
//# sourceMappingURL=src-wGEPrkk5.js.map
|
|
663
|
+
export { FtpDriver as n, Storage as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arkstack/filesystem",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Filesystem module for Arkstack, providing shared file storage and filesystem utitlities for the framework.",
|
|
6
6
|
"homepage": "https://arkstack.toneflix.net",
|
|
@@ -31,16 +31,17 @@
|
|
|
31
31
|
"./package.json": "./package.json"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@h3ravel/musket": "^
|
|
34
|
+
"@h3ravel/musket": "^2.2.1"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@h3ravel/shared": "^
|
|
37
|
+
"@h3ravel/shared": "^2.1.3",
|
|
38
|
+
"@google-cloud/storage": "^7.21.0",
|
|
38
39
|
"@aws-sdk/client-s3": "^3.1011.0",
|
|
39
40
|
"@aws-sdk/s3-request-presigner": "^3.1011.0",
|
|
40
41
|
"flydrive": "^2.0.0",
|
|
41
42
|
"ssh2-sftp-client": "^12.1.0",
|
|
42
|
-
"@arkstack/
|
|
43
|
-
"@arkstack/
|
|
43
|
+
"@arkstack/common": "^0.5.3",
|
|
44
|
+
"@arkstack/contract": "^0.5.3"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"@types/ssh2-sftp-client": "^9.0.6"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"StorageLinkCommand.js","names":[],"sources":["../../src/commands/StorageLinkCommand.ts"],"sourcesContent":["import { Command } from '@h3ravel/musket'\nimport { Storage } from '../'\n\nexport class StorageLinkCommand extends Command {\n protected signature = `storage:link\n {--force : Remove existing links before creating new ones.}\n `\n protected description = 'Create symbolic links for filesystem.links configuration.'\n\n async handle () {\n Storage.link(this.options())\n }\n}"],"mappings":";;;AAGA,IAAa,qBAAb,cAAwC,QAAQ;CAC5C,YAAsB;;;CAGtB,cAAwB;CAExB,MAAM,SAAU;EACZ,QAAQ,KAAK,KAAK,SAAS,CAAC"}
|
package/dist/src-wGEPrkk5.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"src-wGEPrkk5.js","names":[],"sources":["../src/FtpDriver.ts","../src/index.ts"],"sourcesContent":["import { DriveDirectory, DriveFile } from 'flydrive'\nimport type {\n DriverContract,\n ObjectMetaData,\n ObjectVisibility,\n SignedURLOptions,\n WriteOptions,\n} from 'flydrive/types'\nimport { createReadStream, createWriteStream } from 'node:fs'\n\nimport Client from 'ssh2-sftp-client'\nimport { Readable } from 'node:stream'\nimport path from 'node:path'\n\nexport class FtpDriver implements DriverContract {\n private config: {\n host: string\n username: string\n password: string\n port?: number\n verbose?: boolean\n privateKey?: string\n }\n\n constructor(config: string | {\n host: string\n username: string\n password: string\n port?: number\n verbose?: boolean\n privateKey?: string\n }) {\n if (typeof config === 'string') {\n const url = new URL(config)\n\n this.config = {\n host: url.hostname,\n username: url.username,\n password: url.password,\n port: url.port ? parseInt(url.port, 10) : 22,\n verbose: url.searchParams.get('verbose') === 'true',\n }\n } else {\n this.config = config\n }\n }\n\n getConfig () {\n return this.config\n }\n\n private async init () {\n const client = new Client()\n\n await client.connect({\n host: this.config.host,\n username: this.config.username,\n password: this.config.password,\n port: this.config.port || 22,\n })\n\n return client\n }\n\n private async load<T> (handle: (client: Client) => Promise<T>): Promise<T> {\n const client = await this.init()\n\n try {\n return await handle(client)\n } catch (e) {\n if (this.config.verbose) {\n throw e\n }\n } finally {\n await client.end()\n }\n\n return null as any\n }\n\n /**\n * Return a boolean value indicating if the file exists\n * or not.\n */\n async exists (key: string): Promise<boolean> {\n const client = await this.load((client) => {\n return client.stat(key)\n }).catch(() => {\n return null\n })\n\n return !!client\n }\n\n /**\n * Return the file contents as a UTF-8 string. Throw an exception\n * if the file is missing.\n */\n async get (key: string): Promise<string> {\n const stream = await this.getStream(key)\n\n const chunks: Uint8Array[] = []\n for await (const chunk of stream) {\n chunks.push(chunk)\n }\n\n return Buffer.concat(chunks).toString('utf-8')\n }\n\n /**\n * Return the file contents as a Readable stream. Throw an exception\n * if the file is missing.\n */\n async getStream (key: string): Promise<Readable> {\n const dst = createWriteStream('/tmp/' + path.basename(key))\n await this.load((client) => {\n return client.get(key, dst)\n }).catch(() => {\n return null\n })\n\n return createReadStream('/tmp/' + path.basename(key))\n }\n\n /**\n * Return the file contents as a Uint8Array. Throw an exception\n * if the file is missing.\n */\n async getBytes (key: string): Promise<Uint8Array> {\n const content = await this.get(key)\n\n return new Uint8Array(Buffer.from(content, 'utf-8'))\n }\n\n /**\n * Return metadata of the file. Throw an exception\n * if the file is missing.\n */\n async getMetaData (key: string): Promise<ObjectMetaData> {\n const stat = await this.load((client) => {\n return client.stat(key)\n }).catch(() => {\n return null\n })\n\n if (!stat) {\n throw new Error('E_CANNOT_READ_FILE')\n }\n\n return {\n contentType: undefined,\n contentLength: stat.size,\n etag: key,\n lastModified: new Date(stat.modifyTime),\n }\n }\n\n /**\n * Return visibility of the file. Infer visibility from the initial\n * config, when the driver does not support the concept of visibility.\n */\n async getVisibility (key: string): Promise<ObjectVisibility> {\n void key\n\n return 'private'\n }\n\n /**\n * Return the public URL of the file. Throw an exception when the driver\n * does not support generating URLs.\n */\n async getUrl (key: string): Promise<string> {\n void key\n throw new Error('E_URL_GENERATION_UNSUPPORTED')\n }\n\n /**\n * Return the signed URL to serve a private file. Throw exception\n * when the driver does not support generating URLs.\n */\n async getSignedUrl (key: string, options?: SignedURLOptions): Promise<string> {\n void key\n void options\n throw new Error('E_URL_GENERATION_UNSUPPORTED')\n }\n /**\n * Return the signed/temporary URL that can be used to directly upload\n * the file contents to the storage.\n */\n async getSignedUploadUrl (key: string, options?: SignedURLOptions): Promise<string> {\n void key\n void options\n throw new Error('E_URL_GENERATION_UNSUPPORTED')\n }\n\n /**\n * Update the visibility of the file. Result in a NOOP\n * when the driver does not support the concept of\n * visibility.\n */\n async setVisibility (key: string, visibility: ObjectVisibility): Promise<void> {\n void key\n void visibility\n }\n\n /**\n * Create a new file or update an existing file. The contents\n * will be a UTF-8 string or \"Uint8Array\".\n */\n async put (key: string, contents: string | Uint8Array, options?: WriteOptions): Promise<void> {\n if (contents instanceof Uint8Array) {\n contents = Buffer.from(contents)\n }\n\n const stream = Readable.from(contents)\n\n await this.putStream(key, stream, options)\n }\n\n /**\n * Create a new file or update an existing file. The contents\n * will be a Readable stream.\n */\n async putStream (key: string, contents: Readable, options?: WriteOptions): Promise<void> {\n const dst = createWriteStream('/tmp/' + path.basename(key), {\n encoding: options?.contentEncoding as never || 'utf-8',\n })\n\n await new Promise((resolve, reject) => {\n contents.pipe(dst)\n contents.on('error', reject)\n dst.on('finish', resolve)\n dst.on('error', reject)\n })\n\n await this.load((client) => {\n return client.put('/tmp/' + path.basename(key), key)\n })\n }\n\n /**\n * Copy the existing file to the destination. Make sure the new file\n * has the same visibility as the existing file. It might require\n * manually fetching the visibility of the \"source\" file.\n */\n async copy (source: string, destination: string, options?: WriteOptions): Promise<void> {\n void options\n await this.load((client) => {\n return client.rcopy(source, destination)\n })\n }\n\n /**\n * Move the existing file to the destination. Make sure the new file\n * has the same visibility as the existing file. It might require\n * manually fetching the visibility of the \"source\" file.\n */\n async move (source: string, destination: string, options?: WriteOptions): Promise<void> {\n void options\n await this.load((client) => {\n return client.rename(source, destination)\n })\n }\n\n /**\n * Delete an existing file. Do not throw an error if the\n * file is already missing\n */\n async delete (key: string): Promise<void> {\n await this.load((client) => {\n return client.delete(key)\n }).catch(() => {\n return null\n })\n }\n\n /**\n * Delete all files inside a folder. Do not throw an error\n * if the folder does not exist or is empty.\n */\n async deleteAll (prefix: string): Promise<void> {\n await this.load((client) => {\n return client.rmdir(prefix, true)\n }).catch(() => {\n return null\n })\n }\n\n\n /**\n * Switch bucket at runtime if supported.\n */\n bucket (config: string | {\n host: string\n username: string\n password: string\n port?: number\n verbose?: boolean\n privateKey?: string\n }): DriverContract {\n return new FtpDriver(config)\n }\n\n /**\n * List all files from a given folder or the root of the storage.\n * Do not throw an error if the request folder does not exist.\n */\n async listAll (\n prefix: string,\n options?: {\n recursive?: boolean\n paginationToken?: string\n }\n ): Promise<{\n paginationToken?: string\n objects: Iterable<DriveFile | DriveDirectory>\n }> {\n void options\n\n const data = await this.load((client) => {\n return client.list(prefix)\n }).catch(() => {\n return null\n })\n\n return data ? {\n objects: data.map((file) => {\n if (file.type === 'd') {\n return new DriveDirectory(file.name)\n } else {\n return new DriveFile(file.name, this, {\n contentType: undefined,\n contentLength: file.size,\n etag: file.name,\n lastModified: new Date(file.modifyTime),\n })\n }\n })\n } : { objects: [] }\n }\n\n}\n","import { DriveDirectory, DriveFile, DriveManager } from 'flydrive'\nimport { DriverContract, ObjectMetaData, ObjectVisibility, SignedURLOptions, WriteOptions } from 'flydrive/types'\nimport { appUrl, config } from '@arkstack/common'\nimport { rmSync, symlinkSync } from 'node:fs'\n\nimport { FSDriver } from 'flydrive/drivers/fs'\nimport { FtpDriver } from './FtpDriver'\nimport { Logger } from '@h3ravel/shared'\nimport { Readable } from 'node:stream'\nimport { S3Driver } from 'flydrive/drivers/s3'\nimport path from 'node:path'\n\ninterface FileLike {\n originalname: string\n buffer: Buffer\n mimetype: string\n}\n\nexport class Storage implements DriverContract {\n driver: DriveManager<any>\n services: Record<string, () => DriverContract> = {}\n diskName: string\n\n constructor() {\n for (const diskName in config('filesystem.disks')) {\n const diskConfig = config('filesystem.disks')[diskName]\n const driverFactory = this.driversMap[diskConfig.driver]\n\n if (!driverFactory) {\n throw new Error(`Unsupported driver: ${diskConfig.driver}`)\n }\n\n this.services[diskName] = () => driverFactory(diskConfig)\n }\n\n this.diskName = config('filesystem.default')\n this.driver = new DriveManager({\n default: config('filesystem.default'),\n services: this.services\n })\n }\n\n /**\n * Static method to get a disk instance directly from the Storage class without needing to instantiate it first.\n * \n * @param diskName The name of the disk to use. If not provided, the default disk will be used.\n * @returns A Storage instance\n */\n static disk<K extends string> (diskName?: K): Storage {\n const storage = new Storage()\n\n if (diskName) {\n storage.diskName = diskName\n storage.driver = new DriveManager({\n default: diskName,\n services: storage.services\n })\n }\n\n return storage\n }\n\n /**\n * Generate a unique name for the file based on random numbers and original extension\n * \n * @param file The file object containing the original name\n * @returns A unique file name\n */\n static generateName = (file: { name?: string; originalname?: string }): string => {\n const name = file.originalname || file.name || 'file'\n\n if (typeof config('filesystem.fileNameGenerator') === 'function') {\n return config('filesystem.fileNameGenerator')(name)\n }\n\n return Math.floor(Math.random() * 999999999999).toString() +\n '_' + Math.floor(Math.random() * 999999999999) +\n '.' + (name).split('.').pop()\n }\n\n /**\n * Save the file to the storage and return the public URL and the file path\n * \n * @param file The file object containing the file data\n * @param filePath The path where the file should be saved\n * @param fileName The name to save the file as (optional)\n * @returns A tuple containing the public URL and the file path\n */\n static saveFile = async (\n file: FileLike,\n filePath: string = '',\n fileName?: string\n ): Promise<[string, string]> => {\n return new Storage().saveFile(file, filePath, fileName)\n }\n\n /**\n * Save the file to the storage and return the public URL and the file path\n * \n * @param file The file object containing the file data\n * @param filePath The path where the file should be saved\n * @param fileName The name to save the file as (optional)\n * @returns A tuple containing the public URL and the file path\n */\n saveFile = async (\n file: FileLike,\n filePath: string = '',\n fileName?: string\n ): Promise<[string, string]> => {\n const name = fileName || Storage.generateName(file)\n const drive = this.driver.use()\n\n if (file instanceof File && !file.buffer) {\n file.buffer = Buffer.from(await file.arrayBuffer())\n }\n\n await drive.put(path.join(filePath, name), file.buffer)\n\n const url = await drive.getUrl(path.join(filePath, name))\n const pth = this.diskName === 'local' ? path.join(filePath, name) : url\n\n return [url, pth]\n }\n\n /**\n * Return a boolean indicating if the file exists\n */\n exists (key: string): Promise<boolean> {\n return this.driver.use().exists(key)\n }\n /**\n * Return contents of a object for the given key as a UTF-8 string.\n * Should throw \"E_CANNOT_READ_FILE\" error when the file\n * does not exists.\n */\n get (key: string): Promise<string> {\n return this.driver.use().get(key)\n }\n /**\n * Return contents of a object for the given key as a Readable stream.\n * Should throw \"E_CANNOT_READ_FILE\" error when the file\n * does not exists.\n */\n getStream (key: string): Promise<Readable> {\n return this.driver.use().getStream(key)\n }\n /**\n * Return contents of an object for the given key as an Uint8Array.\n * Should throw \"E_CANNOT_READ_FILE\" error when the file\n * does not exists.\n */\n getBytes (key: string): Promise<Uint8Array> {\n return this.driver.use().getBytes(key)\n }\n /**\n * Return metadata of an object for the given key.\n */\n getMetaData (key: string): Promise<ObjectMetaData> {\n return this.driver.use().getMetaData(key)\n }\n /**\n * Return the visibility of the file\n */\n getVisibility (key: string): Promise<ObjectVisibility> {\n return this.driver.use().getVisibility(key)\n }\n /**\n * Return the public URL to access the file\n */\n getUrl (key: string): Promise<string> {\n return this.driver.use().getUrl(key)\n }\n /**\n * Return the signed/temporary URL to access the file\n */\n getSignedUrl (key: string, options?: SignedURLOptions): Promise<string> {\n return this.driver.use().getSignedUrl(key, options)\n }\n /**\n * Return the signed/temporary URL that can be used to directly upload\n * the file contents to the storage.\n */\n getSignedUploadUrl (key: string, options?: SignedURLOptions): Promise<string> {\n return this.driver.use().getSignedUploadUrl(key, options)\n }\n /**\n * Update the visibility of the file\n */\n setVisibility (key: string, visibility: ObjectVisibility): Promise<void> {\n return this.driver.use().setVisibility(key, visibility)\n }\n /**\n * Write object to the destination with the provided\n * contents.\n */\n put (key: string, contents: string | Uint8Array | FileLike, options?: WriteOptions): Promise<void> {\n if (!(contents instanceof Uint8Array) && typeof contents !== 'string') {\n contents = contents.buffer\n }\n\n return this.driver.use().put(key, contents, options)\n }\n /**\n * Write object to the destination with the provided\n * contents as a readable stream\n */\n putStream (key: string, contents: Readable, options?: WriteOptions): Promise<void> {\n return this.driver.use().putStream(key, contents, options)\n }\n /**\n * Copy the file from within the disk root location. Both\n * the \"source\" and \"destination\" will be the key names\n * and not absolute paths.\n */\n copy (source: string, destination: string, options?: WriteOptions): Promise<void> {\n return this.driver.use().copy(source, destination, options)\n }\n /**\n * Move the file from within the disk root location. Both\n * the \"source\" and \"destination\" will be the key names\n * and not absolute paths.\n */\n move (source: string, destination: string, options?: WriteOptions): Promise<void> {\n return this.driver.use().move(source, destination, options)\n }\n /**\n * Delete the file for the given key. Should not throw\n * error when file does not exist in first place\n */\n delete (key: string): Promise<void> {\n return this.driver.use().delete(key)\n }\n /**\n * Delete the files and directories matching the provided prefix.\n */\n deleteAll (prefix: string): Promise<void> {\n return this.driver.use().deleteAll(prefix)\n }\n /**\n * The list all method must return an array of objects with\n * the ability to paginate results (if supported).\n */\n listAll (prefix: string, options?: {\n recursive?: boolean;\n paginationToken?: string;\n }): Promise<{\n paginationToken?: string;\n objects: Iterable<DriveFile | DriveDirectory>;\n }> {\n return this.driver.use().listAll(prefix, options)\n }\n /**\n * Switch bucket at runtime if supported.\n */\n bucket (bucket: string): DriverContract {\n return (this.driver.use() as any).bucket(bucket)\n }\n\n /**\n * Create symbolic links for all configured links in the application configuration.\n */\n static link ({ force = false }: { force?: boolean } = {}): void {\n for (const link in config('filesystem.links')) {\n const target = config('filesystem.links')[link]\n\n const unlink = link.replace(process.cwd(), '')\n const untarget = target.replace(process.cwd(), '')\n\n try {\n if (force) rmSync(link, { recursive: true, force: true })\n symlinkSync(target, link)\n\n Logger.log([\n [' SUCCESS ', 'bgGreen'],\n [`[${unlink}]`, 'green'],\n ['is now linked to', 'white'],\n [`[${untarget}].`, 'green']\n ], ' ')\n } catch (error: any) {\n if (error.code === 'EEXIST') {\n Logger.log([\n [' INFO ', 'bgBlue'],\n [`[${unlink}]`, 'green'],\n ['is already linked to', 'white'],\n [`[${untarget}].`, 'green']\n ], ' ')\n } else {\n Logger.log([\n [' ERROR ', 'bgRed'],\n ['Failed to create symbolic link from', 'white'],\n [`[${unlink}]`, 'green'],\n ['to', 'white'],\n [`[${untarget}]`, 'green'],\n [error.message, 'red']\n ], ' ')\n }\n }\n }\n }\n\n private driversMap: Record<string, (conf: Record<string, any>) => DriverContract> = {\n local: (conf: Record<string, any>) => new FSDriver({\n location: new URL(conf.root, import.meta.url),\n visibility: 'public',\n urlBuilder: {\n async generateURL (key: string, _path: string) {\n return appUrl(key)\n },\n\n async generateSignedURL (key: string, _path: string, _opts: SignedURLOptions) {\n return appUrl(key)\n },\n },\n }),\n s3: (conf: Record<string, any>) => new S3Driver({\n credentials: {\n accessKeyId: conf.key,\n secretAccessKey: conf.secret,\n },\n endpoint: conf.endpoint,\n region: conf.region,\n bucket: conf.bucket,\n visibility: 'private',\n cdnUrl: conf.url,\n }),\n ftp: (conf: Record<string, any>) => new FtpDriver({\n host: conf.host,\n username: conf.username,\n password: conf.password,\n port: conf.port,\n verbose: conf.verbose,\n privateKey: conf.privateKey,\n }),\n }\n}"],"mappings":"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"}
|