@devbro/neko-storage 0.1.4 → 0.1.6
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 +104 -7
- package/dist/Storage.d.mts +1 -0
- package/dist/Storage.mjs.map +1 -1
- package/dist/StorageProviderFactory.d.mts +2 -1
- package/dist/StorageProviderFactory.mjs.map +1 -1
- package/dist/StorageProviderInterface.d.mts +1 -0
- package/dist/index.d.mts +6 -1
- package/dist/index.js +440 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4 -0
- package/dist/index.mjs.map +1 -1
- package/dist/providers/AWSS3StorageProvider.d.mts +4 -3
- package/dist/providers/AWSS3StorageProvider.mjs +2 -2
- package/dist/providers/AWSS3StorageProvider.mjs.map +1 -1
- package/dist/providers/AzureBlobStorageProvider.d.mts +23 -0
- package/dist/providers/AzureBlobStorageProvider.mjs +116 -0
- package/dist/providers/AzureBlobStorageProvider.mjs.map +1 -0
- package/dist/providers/FTPStorageProvider.d.mts +22 -0
- package/dist/providers/FTPStorageProvider.mjs +124 -0
- package/dist/providers/FTPStorageProvider.mjs.map +1 -0
- package/dist/providers/GCPStorageProvider.d.mts +22 -0
- package/dist/providers/GCPStorageProvider.mjs +82 -0
- package/dist/providers/GCPStorageProvider.mjs.map +1 -0
- package/dist/providers/LocalStorageProvider.d.mts +3 -2
- package/dist/providers/LocalStorageProvider.mjs.map +1 -1
- package/dist/providers/SFTPStorageProvider.d.mts +22 -0
- package/dist/providers/SFTPStorageProvider.mjs +124 -0
- package/dist/providers/SFTPStorageProvider.mjs.map +1 -0
- package/dist/types.d.mts +32 -6
- package/package.json +9 -7
package/README.md
CHANGED
|
@@ -39,22 +39,119 @@ let is_file_deleted = await storage.delete('path/to/file/filename.ext');
|
|
|
39
39
|
|
|
40
40
|
## available drivers
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
### Local Storage
|
|
43
|
+
|
|
44
|
+
Store files on the local file system.
|
|
43
45
|
|
|
44
46
|
```ts
|
|
45
|
-
import {
|
|
47
|
+
import { LocalStorageProvider, Storage } from '@devbro/neko-storage';
|
|
46
48
|
|
|
47
49
|
const basePath = path.resolve(os.tmpdir(), `test-storage-${randomUUID()}`);
|
|
48
|
-
const
|
|
50
|
+
const provider = new LocalStorageProvider({ engine: 'local', basePath });
|
|
51
|
+
const storage = new Storage(provider);
|
|
49
52
|
```
|
|
50
53
|
|
|
51
|
-
AWS S3
|
|
54
|
+
### AWS S3
|
|
55
|
+
|
|
56
|
+
Store files in Amazon S3 buckets.
|
|
52
57
|
|
|
53
58
|
```ts
|
|
54
|
-
import {
|
|
59
|
+
import { AWSS3StorageProvider, Storage } from '@devbro/neko-storage';
|
|
60
|
+
|
|
61
|
+
const provider = new AWSS3StorageProvider({
|
|
62
|
+
engine: 's3',
|
|
63
|
+
bucket: 'your-bucket-name',
|
|
64
|
+
s3Config: {
|
|
65
|
+
region: 'us-east-1',
|
|
66
|
+
credentials: {
|
|
67
|
+
accessKeyId: 'YOUR_ACCESS_KEY',
|
|
68
|
+
secretAccessKey: 'YOUR_SECRET_KEY',
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
const storage = new Storage(provider);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Google Cloud Storage (GCP)
|
|
76
|
+
|
|
77
|
+
Store files in Google Cloud Storage buckets.
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { GCPStorageProvider, Storage } from '@devbro/neko-storage';
|
|
81
|
+
|
|
82
|
+
const provider = new GCPStorageProvider({
|
|
83
|
+
engine: 'gcp',
|
|
84
|
+
bucket: 'your-bucket-name',
|
|
85
|
+
gcpConfig: {
|
|
86
|
+
projectId: 'your-project-id',
|
|
87
|
+
keyFilename: '/path/to/service-account-key.json',
|
|
88
|
+
// Or use credentials object directly
|
|
89
|
+
// credentials: {...}
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
const storage = new Storage(provider);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Azure Blob Storage
|
|
96
|
+
|
|
97
|
+
Store files in Azure Blob Storage containers.
|
|
55
98
|
|
|
56
|
-
|
|
57
|
-
|
|
99
|
+
```ts
|
|
100
|
+
import { AzureBlobStorageProvider, Storage } from '@devbro/neko-storage';
|
|
101
|
+
|
|
102
|
+
const provider = new AzureBlobStorageProvider({
|
|
103
|
+
engine: 'azure',
|
|
104
|
+
azureConfig: {
|
|
105
|
+
accountName: 'your-storage-account',
|
|
106
|
+
accountKey: 'YOUR_ACCOUNT_KEY', // Or use sasToken instead
|
|
107
|
+
containerName: 'your-container-name',
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
const storage = new Storage(provider);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### FTP
|
|
114
|
+
|
|
115
|
+
Store files on FTP servers.
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import { FTPStorageProvider, Storage } from '@devbro/neko-storage';
|
|
119
|
+
|
|
120
|
+
const provider = new FTPStorageProvider({
|
|
121
|
+
engine: 'ftp',
|
|
122
|
+
basePath: '/remote/path',
|
|
123
|
+
ftpConfig: {
|
|
124
|
+
host: 'ftp.example.com',
|
|
125
|
+
port: 21,
|
|
126
|
+
user: 'username',
|
|
127
|
+
password: 'password',
|
|
128
|
+
secure: false, // Set to true for FTPS
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
const storage = new Storage(provider);
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### SFTP
|
|
135
|
+
|
|
136
|
+
Store files on SFTP servers via SSH.
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { SFTPStorageProvider, Storage } from '@devbro/neko-storage';
|
|
140
|
+
|
|
141
|
+
const provider = new SFTPStorageProvider({
|
|
142
|
+
engine: 'sftp',
|
|
143
|
+
basePath: '/remote/path',
|
|
144
|
+
sftpConfig: {
|
|
145
|
+
host: 'sftp.example.com',
|
|
146
|
+
port: 22,
|
|
147
|
+
username: 'username',
|
|
148
|
+
password: 'password',
|
|
149
|
+
// Or use private key authentication
|
|
150
|
+
// privateKey: fs.readFileSync('/path/to/private-key'),
|
|
151
|
+
// passphrase: 'key-passphrase',
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
const storage = new Storage(provider);
|
|
58
155
|
```
|
|
59
156
|
|
|
60
157
|
More driver available upon request or through PR.
|
package/dist/Storage.d.mts
CHANGED
|
@@ -3,6 +3,7 @@ import { Stream } from 'stream';
|
|
|
3
3
|
import { Metadata } from './types.mjs';
|
|
4
4
|
import { StorageProviderInterface } from './StorageProviderInterface.mjs';
|
|
5
5
|
import '@aws-sdk/client-s3';
|
|
6
|
+
import '@google-cloud/storage';
|
|
6
7
|
|
|
7
8
|
declare class Storage {
|
|
8
9
|
protected provider: StorageProviderInterface;
|
package/dist/Storage.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/Storage.mts"],"sourcesContent":["import { ReadStream } from 'fs';\nimport { Stream } from 'stream';\nimport { Metadata } from './types.mjs';\nimport {
|
|
1
|
+
{"version":3,"sources":["../src/Storage.mts"],"sourcesContent":["import { ReadStream } from 'fs';\nimport { Stream } from 'stream';\nimport { Metadata } from './types.mjs';\nimport { StorageProviderInterface } from './StorageProviderInterface.mjs';\n\nexport class Storage {\n constructor(protected provider: StorageProviderInterface) {}\n\n exists(path: string): Promise<boolean> {\n return this.provider.exists(path);\n }\n\n put(path: string, content: string | object | Stream | Buffer): Promise<boolean> {\n return this.provider.put(path, content);\n }\n\n getJson(path: string): Promise<object> {\n return this.provider.getJson(path);\n }\n\n getString(path: string): Promise<string> {\n return this.provider.getString(path);\n }\n\n getBuffer(path: string): Promise<Buffer> {\n return this.provider.getBuffer(path);\n }\n\n getStream(path: string): Promise<ReadStream> {\n return this.provider.getStream(path);\n }\n\n delete(path: string): Promise<boolean> {\n return this.provider.delete(path);\n }\n\n metadata(path: string): Promise<Metadata> {\n return this.provider.metadata(path);\n }\n}\n"],"mappings":";;AAKO,MAAM,QAAQ;AAAA,EACnB,YAAsB,UAAoC;AAApC;AAAA,EAAqC;AAAA,EAN7D,OAKqB;AAAA;AAAA;AAAA,EAGnB,OAAO,MAAgC;AACrC,WAAO,KAAK,SAAS,OAAO,IAAI;AAAA,EAClC;AAAA,EAEA,IAAI,MAAc,SAA8D;AAC9E,WAAO,KAAK,SAAS,IAAI,MAAM,OAAO;AAAA,EACxC;AAAA,EAEA,QAAQ,MAA+B;AACrC,WAAO,KAAK,SAAS,QAAQ,IAAI;AAAA,EACnC;AAAA,EAEA,UAAU,MAA+B;AACvC,WAAO,KAAK,SAAS,UAAU,IAAI;AAAA,EACrC;AAAA,EAEA,UAAU,MAA+B;AACvC,WAAO,KAAK,SAAS,UAAU,IAAI;AAAA,EACrC;AAAA,EAEA,UAAU,MAAmC;AAC3C,WAAO,KAAK,SAAS,UAAU,IAAI;AAAA,EACrC;AAAA,EAEA,OAAO,MAAgC;AACrC,WAAO,KAAK,SAAS,OAAO,IAAI;AAAA,EAClC;AAAA,EAEA,SAAS,MAAiC;AACxC,WAAO,KAAK,SAAS,SAAS,IAAI;AAAA,EACpC;AACF;","names":[]}
|
|
@@ -4,11 +4,12 @@ import 'fs';
|
|
|
4
4
|
import 'stream';
|
|
5
5
|
import './types.mjs';
|
|
6
6
|
import '@aws-sdk/client-s3';
|
|
7
|
+
import '@google-cloud/storage';
|
|
7
8
|
|
|
8
9
|
declare class StorageProviderFactory {
|
|
9
10
|
static instance: FlexibleFactory<StorageProviderInterface>;
|
|
10
11
|
static register(key: string, factory: (...args: any[]) => StorageProviderInterface): void;
|
|
11
|
-
static create
|
|
12
|
+
static create(key: string, ...args: any[]): StorageProviderInterface;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export { StorageProviderFactory };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/StorageProviderFactory.mts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../src/StorageProviderFactory.mts"],"sourcesContent":["import { FlexibleFactory } from '@devbro/neko-helper';\nimport { StorageProviderInterface } from './StorageProviderInterface.mjs';\n\nexport class StorageProviderFactory {\n static instance: FlexibleFactory<StorageProviderInterface> =\n new FlexibleFactory<StorageProviderInterface>();\n\n static register(key: string, factory: (...args: any[]) => StorageProviderInterface): void {\n StorageProviderFactory.instance.register(key, factory);\n }\n\n static create(key: string, ...args: any[]): StorageProviderInterface {\n return StorageProviderFactory.instance.create(key, ...args);\n }\n}\n"],"mappings":";;AAAA,SAAS,uBAAuB;AAGzB,MAAM,uBAAuB;AAAA,EAHpC,OAGoC;AAAA;AAAA;AAAA,EAClC,OAAO,WACL,IAAI,gBAA0C;AAAA,EAEhD,OAAO,SAAS,KAAa,SAA6D;AACxF,2BAAuB,SAAS,SAAS,KAAK,OAAO;AAAA,EACvD;AAAA,EAEA,OAAO,OAAO,QAAgB,MAAuC;AACnE,WAAO,uBAAuB,SAAS,OAAO,KAAK,GAAG,IAAI;AAAA,EAC5D;AACF;","names":[]}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
export { Metadata, StorageConfig } from './types.mjs';
|
|
1
|
+
export { AzureStorageConfig, FTPConfig, GCPStorageConfig, LocalStorageConfig, Metadata, S3ClientConfig, SFTPConfig, StorageConfig } from './types.mjs';
|
|
2
2
|
export { Storage } from './Storage.mjs';
|
|
3
3
|
export { AWSS3StorageProvider } from './providers/AWSS3StorageProvider.mjs';
|
|
4
4
|
export { LocalStorageProvider } from './providers/LocalStorageProvider.mjs';
|
|
5
|
+
export { GCPStorageProvider } from './providers/GCPStorageProvider.mjs';
|
|
6
|
+
export { AzureBlobStorageProvider } from './providers/AzureBlobStorageProvider.mjs';
|
|
7
|
+
export { FTPStorageProvider } from './providers/FTPStorageProvider.mjs';
|
|
8
|
+
export { SFTPStorageProvider } from './providers/SFTPStorageProvider.mjs';
|
|
5
9
|
export { StorageProviderFactory } from './StorageProviderFactory.mjs';
|
|
6
10
|
export { StorageProviderInterface } from './StorageProviderInterface.mjs';
|
|
7
11
|
import '@aws-sdk/client-s3';
|
|
12
|
+
import '@google-cloud/storage';
|
|
8
13
|
import 'fs';
|
|
9
14
|
import 'stream';
|
|
10
15
|
import '@devbro/neko-helper';
|
package/dist/index.js
CHANGED
|
@@ -32,7 +32,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
32
32
|
var index_exports = {};
|
|
33
33
|
__export(index_exports, {
|
|
34
34
|
AWSS3StorageProvider: () => AWSS3StorageProvider,
|
|
35
|
+
AzureBlobStorageProvider: () => AzureBlobStorageProvider,
|
|
36
|
+
FTPStorageProvider: () => FTPStorageProvider,
|
|
37
|
+
GCPStorageProvider: () => GCPStorageProvider,
|
|
35
38
|
LocalStorageProvider: () => LocalStorageProvider,
|
|
39
|
+
SFTPStorageProvider: () => SFTPStorageProvider,
|
|
36
40
|
Storage: () => Storage,
|
|
37
41
|
StorageProviderFactory: () => StorageProviderFactory
|
|
38
42
|
});
|
|
@@ -78,7 +82,7 @@ var import_stream = __toESM(require("stream"), 1);
|
|
|
78
82
|
var AWSS3StorageProvider = class {
|
|
79
83
|
constructor(config) {
|
|
80
84
|
this.config = config;
|
|
81
|
-
this.s3 = new import_client_s3.S3Client(this.config
|
|
85
|
+
this.s3 = new import_client_s3.S3Client(this.config);
|
|
82
86
|
}
|
|
83
87
|
static {
|
|
84
88
|
__name(this, "AWSS3StorageProvider");
|
|
@@ -86,7 +90,7 @@ var AWSS3StorageProvider = class {
|
|
|
86
90
|
s3;
|
|
87
91
|
async exists(path2) {
|
|
88
92
|
try {
|
|
89
|
-
await this.s3.send(new import_client_s3.HeadObjectCommand({ Bucket: this.config
|
|
93
|
+
await this.s3.send(new import_client_s3.HeadObjectCommand({ Bucket: this.config.bucket, Key: path2 }));
|
|
90
94
|
return true;
|
|
91
95
|
} catch (error) {
|
|
92
96
|
if (error.name === "NotFound") {
|
|
@@ -250,6 +254,436 @@ var LocalStorageProvider = class {
|
|
|
250
254
|
}
|
|
251
255
|
};
|
|
252
256
|
|
|
257
|
+
// src/providers/GCPStorageProvider.mts
|
|
258
|
+
var import_storage = require("@google-cloud/storage");
|
|
259
|
+
var import_stream3 = __toESM(require("stream"), 1);
|
|
260
|
+
var mime2 = __toESM(require("mime-types"), 1);
|
|
261
|
+
var GCPStorageProvider = class {
|
|
262
|
+
constructor(config) {
|
|
263
|
+
this.config = config;
|
|
264
|
+
const { bucket, ...gcpOptions } = config;
|
|
265
|
+
this.storage = new import_storage.Storage(gcpOptions);
|
|
266
|
+
}
|
|
267
|
+
static {
|
|
268
|
+
__name(this, "GCPStorageProvider");
|
|
269
|
+
}
|
|
270
|
+
storage;
|
|
271
|
+
async exists(path2) {
|
|
272
|
+
try {
|
|
273
|
+
const file = this.storage.bucket(this.config.bucket).file(path2);
|
|
274
|
+
const [exists] = await file.exists();
|
|
275
|
+
return exists;
|
|
276
|
+
} catch (error) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async put(path2, content) {
|
|
281
|
+
const file = this.storage.bucket(this.config.bucket).file(path2);
|
|
282
|
+
let data;
|
|
283
|
+
if (typeof content === "string" || content instanceof Buffer) {
|
|
284
|
+
data = content;
|
|
285
|
+
} else if (typeof content === "object" && !(content instanceof import_stream3.default)) {
|
|
286
|
+
data = JSON.stringify(content);
|
|
287
|
+
} else if (content instanceof import_stream3.default) {
|
|
288
|
+
data = content;
|
|
289
|
+
} else {
|
|
290
|
+
throw new Error("Unsupported content type");
|
|
291
|
+
}
|
|
292
|
+
if (data instanceof import_stream3.default) {
|
|
293
|
+
await new Promise((resolve, reject) => {
|
|
294
|
+
data.pipe(file.createWriteStream()).on("finish", () => resolve()).on("error", reject);
|
|
295
|
+
});
|
|
296
|
+
} else {
|
|
297
|
+
await file.save(data);
|
|
298
|
+
}
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
async getJson(path2) {
|
|
302
|
+
const data = await this.getString(path2);
|
|
303
|
+
return JSON.parse(data);
|
|
304
|
+
}
|
|
305
|
+
async getString(path2) {
|
|
306
|
+
const file = this.storage.bucket(this.config.bucket).file(path2);
|
|
307
|
+
const [content] = await file.download();
|
|
308
|
+
return content.toString("utf-8");
|
|
309
|
+
}
|
|
310
|
+
async getBuffer(path2) {
|
|
311
|
+
const file = this.storage.bucket(this.config.bucket).file(path2);
|
|
312
|
+
const [content] = await file.download();
|
|
313
|
+
return content;
|
|
314
|
+
}
|
|
315
|
+
async getStream(path2) {
|
|
316
|
+
const file = this.storage.bucket(this.config.bucket).file(path2);
|
|
317
|
+
return file.createReadStream();
|
|
318
|
+
}
|
|
319
|
+
async delete(path2) {
|
|
320
|
+
const file = this.storage.bucket(this.config.bucket).file(path2);
|
|
321
|
+
await file.delete();
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
async metadata(path2) {
|
|
325
|
+
const file = this.storage.bucket(this.config.bucket).file(path2);
|
|
326
|
+
const [metadata] = await file.getMetadata();
|
|
327
|
+
return {
|
|
328
|
+
size: typeof metadata.size === "number" ? metadata.size : parseInt(metadata.size || "0", 10),
|
|
329
|
+
mimeType: metadata.contentType || mime2.lookup(path2) || "unknown",
|
|
330
|
+
lastModifiedDate: metadata.updated || (/* @__PURE__ */ new Date(0)).toISOString()
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// src/providers/AzureBlobStorageProvider.mts
|
|
336
|
+
var import_storage_blob = require("@azure/storage-blob");
|
|
337
|
+
var import_stream4 = __toESM(require("stream"), 1);
|
|
338
|
+
var mime3 = __toESM(require("mime-types"), 1);
|
|
339
|
+
var AzureBlobStorageProvider = class {
|
|
340
|
+
constructor(config) {
|
|
341
|
+
this.config = config;
|
|
342
|
+
const { accountName, accountKey, sasToken } = config;
|
|
343
|
+
if (accountKey) {
|
|
344
|
+
const sharedKeyCredential = new import_storage_blob.StorageSharedKeyCredential(accountName, accountKey);
|
|
345
|
+
this.blobServiceClient = new import_storage_blob.BlobServiceClient(
|
|
346
|
+
`https://${accountName}.blob.core.windows.net`,
|
|
347
|
+
sharedKeyCredential
|
|
348
|
+
);
|
|
349
|
+
} else if (sasToken) {
|
|
350
|
+
this.blobServiceClient = new import_storage_blob.BlobServiceClient(
|
|
351
|
+
`https://${accountName}.blob.core.windows.net?${sasToken}`
|
|
352
|
+
);
|
|
353
|
+
} else {
|
|
354
|
+
throw new Error("Either accountKey or sasToken is required for Azure Blob Storage");
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
static {
|
|
358
|
+
__name(this, "AzureBlobStorageProvider");
|
|
359
|
+
}
|
|
360
|
+
blobServiceClient;
|
|
361
|
+
async exists(path2) {
|
|
362
|
+
try {
|
|
363
|
+
const containerClient = this.blobServiceClient.getContainerClient(this.config.containerName);
|
|
364
|
+
const blobClient = containerClient.getBlobClient(path2);
|
|
365
|
+
return await blobClient.exists();
|
|
366
|
+
} catch (error) {
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
async put(path2, content) {
|
|
371
|
+
const containerClient = this.blobServiceClient.getContainerClient(this.config.containerName);
|
|
372
|
+
const blockBlobClient = containerClient.getBlockBlobClient(path2);
|
|
373
|
+
let data;
|
|
374
|
+
if (typeof content === "string" || content instanceof Buffer) {
|
|
375
|
+
data = content;
|
|
376
|
+
} else if (typeof content === "object" && !(content instanceof import_stream4.default)) {
|
|
377
|
+
data = JSON.stringify(content);
|
|
378
|
+
} else if (content instanceof import_stream4.default) {
|
|
379
|
+
data = content;
|
|
380
|
+
} else {
|
|
381
|
+
throw new Error("Unsupported content type");
|
|
382
|
+
}
|
|
383
|
+
if (data instanceof import_stream4.default) {
|
|
384
|
+
await blockBlobClient.uploadStream(data);
|
|
385
|
+
} else {
|
|
386
|
+
const buffer = typeof data === "string" ? Buffer.from(data) : data;
|
|
387
|
+
await blockBlobClient.upload(buffer, buffer.length);
|
|
388
|
+
}
|
|
389
|
+
return true;
|
|
390
|
+
}
|
|
391
|
+
async getJson(path2) {
|
|
392
|
+
const data = await this.getString(path2);
|
|
393
|
+
return JSON.parse(data);
|
|
394
|
+
}
|
|
395
|
+
async getString(path2) {
|
|
396
|
+
const buffer = await this.getBuffer(path2);
|
|
397
|
+
return buffer.toString("utf-8");
|
|
398
|
+
}
|
|
399
|
+
async getBuffer(path2) {
|
|
400
|
+
const containerClient = this.blobServiceClient.getContainerClient(this.config.containerName);
|
|
401
|
+
const blobClient = containerClient.getBlobClient(path2);
|
|
402
|
+
const downloadResponse = await blobClient.download();
|
|
403
|
+
if (!downloadResponse.readableStreamBody) {
|
|
404
|
+
throw new Error("Failed to download blob");
|
|
405
|
+
}
|
|
406
|
+
return await this.streamToBuffer(downloadResponse.readableStreamBody);
|
|
407
|
+
}
|
|
408
|
+
async getStream(path2) {
|
|
409
|
+
const containerClient = this.blobServiceClient.getContainerClient(this.config.containerName);
|
|
410
|
+
const blobClient = containerClient.getBlobClient(path2);
|
|
411
|
+
const downloadResponse = await blobClient.download();
|
|
412
|
+
if (!downloadResponse.readableStreamBody) {
|
|
413
|
+
throw new Error("Failed to download blob");
|
|
414
|
+
}
|
|
415
|
+
return downloadResponse.readableStreamBody;
|
|
416
|
+
}
|
|
417
|
+
async delete(path2) {
|
|
418
|
+
const containerClient = this.blobServiceClient.getContainerClient(this.config.containerName);
|
|
419
|
+
const blobClient = containerClient.getBlobClient(path2);
|
|
420
|
+
await blobClient.delete();
|
|
421
|
+
return true;
|
|
422
|
+
}
|
|
423
|
+
async metadata(path2) {
|
|
424
|
+
const containerClient = this.blobServiceClient.getContainerClient(this.config.containerName);
|
|
425
|
+
const blobClient = containerClient.getBlobClient(path2);
|
|
426
|
+
const properties = await blobClient.getProperties();
|
|
427
|
+
return {
|
|
428
|
+
size: properties.contentLength || 0,
|
|
429
|
+
mimeType: properties.contentType || mime3.lookup(path2) || "unknown",
|
|
430
|
+
lastModifiedDate: properties.lastModified?.toISOString() || (/* @__PURE__ */ new Date(0)).toISOString()
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
async streamToBuffer(readableStream) {
|
|
434
|
+
return new Promise((resolve, reject) => {
|
|
435
|
+
const chunks = [];
|
|
436
|
+
readableStream.on("data", (chunk) => {
|
|
437
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
438
|
+
});
|
|
439
|
+
readableStream.on("end", () => {
|
|
440
|
+
resolve(Buffer.concat(chunks));
|
|
441
|
+
});
|
|
442
|
+
readableStream.on("error", reject);
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
// src/providers/FTPStorageProvider.mts
|
|
448
|
+
var import_basic_ftp = require("basic-ftp");
|
|
449
|
+
var import_stream5 = __toESM(require("stream"), 1);
|
|
450
|
+
var mime4 = __toESM(require("mime-types"), 1);
|
|
451
|
+
var FTPStorageProvider = class {
|
|
452
|
+
constructor(config) {
|
|
453
|
+
this.config = config;
|
|
454
|
+
}
|
|
455
|
+
static {
|
|
456
|
+
__name(this, "FTPStorageProvider");
|
|
457
|
+
}
|
|
458
|
+
async getClient() {
|
|
459
|
+
const client = new import_basic_ftp.Client();
|
|
460
|
+
await client.access({
|
|
461
|
+
host: this.config.host,
|
|
462
|
+
port: this.config.port || 21,
|
|
463
|
+
user: this.config.user || "anonymous",
|
|
464
|
+
password: this.config.password || "",
|
|
465
|
+
secure: this.config.secure || false
|
|
466
|
+
});
|
|
467
|
+
return client;
|
|
468
|
+
}
|
|
469
|
+
async exists(path2) {
|
|
470
|
+
const client = await this.getClient();
|
|
471
|
+
try {
|
|
472
|
+
await client.size(path2);
|
|
473
|
+
return true;
|
|
474
|
+
} catch (error) {
|
|
475
|
+
return false;
|
|
476
|
+
} finally {
|
|
477
|
+
client.close();
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
async put(path2, content) {
|
|
481
|
+
const client = await this.getClient();
|
|
482
|
+
try {
|
|
483
|
+
let stream;
|
|
484
|
+
if (typeof content === "string" || content instanceof Buffer) {
|
|
485
|
+
const readable = new import_stream5.Readable();
|
|
486
|
+
readable.push(typeof content === "string" ? Buffer.from(content) : content);
|
|
487
|
+
readable.push(null);
|
|
488
|
+
stream = readable;
|
|
489
|
+
} else if (typeof content === "object" && !(content instanceof import_stream5.default)) {
|
|
490
|
+
const readable = new import_stream5.Readable();
|
|
491
|
+
readable.push(Buffer.from(JSON.stringify(content)));
|
|
492
|
+
readable.push(null);
|
|
493
|
+
stream = readable;
|
|
494
|
+
} else if (content instanceof import_stream5.default) {
|
|
495
|
+
stream = content;
|
|
496
|
+
} else {
|
|
497
|
+
throw new Error("Unsupported content type");
|
|
498
|
+
}
|
|
499
|
+
await client.uploadFrom(stream, path2);
|
|
500
|
+
return true;
|
|
501
|
+
} finally {
|
|
502
|
+
client.close();
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
async getJson(path2) {
|
|
506
|
+
const data = await this.getString(path2);
|
|
507
|
+
return JSON.parse(data);
|
|
508
|
+
}
|
|
509
|
+
async getString(path2) {
|
|
510
|
+
const buffer = await this.getBuffer(path2);
|
|
511
|
+
return buffer.toString("utf-8");
|
|
512
|
+
}
|
|
513
|
+
async getBuffer(path2) {
|
|
514
|
+
const client = await this.getClient();
|
|
515
|
+
try {
|
|
516
|
+
const chunks = [];
|
|
517
|
+
const writable = new import_stream5.PassThrough();
|
|
518
|
+
writable.on("data", (chunk) => {
|
|
519
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
520
|
+
});
|
|
521
|
+
await client.downloadTo(writable, path2);
|
|
522
|
+
return Buffer.concat(chunks);
|
|
523
|
+
} finally {
|
|
524
|
+
client.close();
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
async getStream(path2) {
|
|
528
|
+
const client = await this.getClient();
|
|
529
|
+
const passThrough = new import_stream5.PassThrough();
|
|
530
|
+
client.downloadTo(passThrough, path2).then(() => client.close()).catch((error) => {
|
|
531
|
+
client.close();
|
|
532
|
+
passThrough.destroy(error);
|
|
533
|
+
});
|
|
534
|
+
passThrough.on("close", () => {
|
|
535
|
+
try {
|
|
536
|
+
client.close();
|
|
537
|
+
} catch {
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
return passThrough;
|
|
541
|
+
}
|
|
542
|
+
async delete(path2) {
|
|
543
|
+
const client = await this.getClient();
|
|
544
|
+
try {
|
|
545
|
+
await client.remove(path2);
|
|
546
|
+
return true;
|
|
547
|
+
} finally {
|
|
548
|
+
client.close();
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
async metadata(path2) {
|
|
552
|
+
const client = await this.getClient();
|
|
553
|
+
try {
|
|
554
|
+
const size = await client.size(path2);
|
|
555
|
+
const lastMod = await client.lastMod(path2);
|
|
556
|
+
return {
|
|
557
|
+
size,
|
|
558
|
+
mimeType: mime4.lookup(path2) || "unknown",
|
|
559
|
+
lastModifiedDate: lastMod?.toISOString() || (/* @__PURE__ */ new Date(0)).toISOString()
|
|
560
|
+
};
|
|
561
|
+
} finally {
|
|
562
|
+
client.close();
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
// src/providers/SFTPStorageProvider.mts
|
|
568
|
+
var import_ssh2_sftp_client = __toESM(require("ssh2-sftp-client"), 1);
|
|
569
|
+
var import_stream6 = __toESM(require("stream"), 1);
|
|
570
|
+
var mime5 = __toESM(require("mime-types"), 1);
|
|
571
|
+
var SFTPStorageProvider = class {
|
|
572
|
+
constructor(config) {
|
|
573
|
+
this.config = config;
|
|
574
|
+
}
|
|
575
|
+
static {
|
|
576
|
+
__name(this, "SFTPStorageProvider");
|
|
577
|
+
}
|
|
578
|
+
async getClient() {
|
|
579
|
+
const client = new import_ssh2_sftp_client.default();
|
|
580
|
+
await client.connect({
|
|
581
|
+
host: this.config.host,
|
|
582
|
+
port: this.config.port || 22,
|
|
583
|
+
username: this.config.username,
|
|
584
|
+
password: this.config.password,
|
|
585
|
+
privateKey: this.config.privateKey,
|
|
586
|
+
passphrase: this.config.passphrase
|
|
587
|
+
});
|
|
588
|
+
return client;
|
|
589
|
+
}
|
|
590
|
+
async exists(path2) {
|
|
591
|
+
const client = await this.getClient();
|
|
592
|
+
try {
|
|
593
|
+
const result = await client.exists(path2);
|
|
594
|
+
return result !== false;
|
|
595
|
+
} catch (error) {
|
|
596
|
+
return false;
|
|
597
|
+
} finally {
|
|
598
|
+
await client.end();
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
async put(path2, content) {
|
|
602
|
+
const client = await this.getClient();
|
|
603
|
+
try {
|
|
604
|
+
let data;
|
|
605
|
+
if (typeof content === "string") {
|
|
606
|
+
data = content;
|
|
607
|
+
} else if (content instanceof Buffer) {
|
|
608
|
+
data = content;
|
|
609
|
+
} else if (typeof content === "object" && !(content instanceof import_stream6.default)) {
|
|
610
|
+
data = Buffer.from(JSON.stringify(content));
|
|
611
|
+
} else if (content instanceof import_stream6.default) {
|
|
612
|
+
data = content;
|
|
613
|
+
} else {
|
|
614
|
+
throw new Error("Unsupported content type");
|
|
615
|
+
}
|
|
616
|
+
await client.put(data, path2);
|
|
617
|
+
return true;
|
|
618
|
+
} finally {
|
|
619
|
+
await client.end();
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
async getJson(path2) {
|
|
623
|
+
const data = await this.getString(path2);
|
|
624
|
+
return JSON.parse(data);
|
|
625
|
+
}
|
|
626
|
+
async getString(path2) {
|
|
627
|
+
const buffer = await this.getBuffer(path2);
|
|
628
|
+
return buffer.toString("utf-8");
|
|
629
|
+
}
|
|
630
|
+
async getBuffer(path2) {
|
|
631
|
+
const client = await this.getClient();
|
|
632
|
+
try {
|
|
633
|
+
const buffer = await client.get(path2);
|
|
634
|
+
return buffer;
|
|
635
|
+
} finally {
|
|
636
|
+
await client.end();
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
async getStream(path2) {
|
|
640
|
+
const client = await this.getClient();
|
|
641
|
+
const passThrough = new import_stream6.PassThrough();
|
|
642
|
+
client.get(path2).then((data) => {
|
|
643
|
+
if (data instanceof Buffer) {
|
|
644
|
+
const readable = new import_stream6.Readable();
|
|
645
|
+
readable.push(data);
|
|
646
|
+
readable.push(null);
|
|
647
|
+
readable.pipe(passThrough);
|
|
648
|
+
} else if (data instanceof import_stream6.default) {
|
|
649
|
+
data.pipe(passThrough);
|
|
650
|
+
}
|
|
651
|
+
return client.end();
|
|
652
|
+
}).catch((error) => {
|
|
653
|
+
client.end().catch(() => {
|
|
654
|
+
});
|
|
655
|
+
passThrough.destroy(error);
|
|
656
|
+
});
|
|
657
|
+
passThrough.on("close", () => {
|
|
658
|
+
client.end().catch(() => {
|
|
659
|
+
});
|
|
660
|
+
});
|
|
661
|
+
return passThrough;
|
|
662
|
+
}
|
|
663
|
+
async delete(path2) {
|
|
664
|
+
const client = await this.getClient();
|
|
665
|
+
try {
|
|
666
|
+
await client.delete(path2);
|
|
667
|
+
return true;
|
|
668
|
+
} finally {
|
|
669
|
+
await client.end();
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
async metadata(path2) {
|
|
673
|
+
const client = await this.getClient();
|
|
674
|
+
try {
|
|
675
|
+
const stats = await client.stat(path2);
|
|
676
|
+
return {
|
|
677
|
+
size: stats.size || 0,
|
|
678
|
+
mimeType: mime5.lookup(path2) || "unknown",
|
|
679
|
+
lastModifiedDate: new Date((stats.modifyTime || 0) * 1e3).toISOString()
|
|
680
|
+
};
|
|
681
|
+
} finally {
|
|
682
|
+
await client.end();
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
|
|
253
687
|
// src/StorageProviderFactory.mts
|
|
254
688
|
var import_neko_helper = require("@devbro/neko-helper");
|
|
255
689
|
var StorageProviderFactory = class _StorageProviderFactory {
|
|
@@ -267,7 +701,11 @@ var StorageProviderFactory = class _StorageProviderFactory {
|
|
|
267
701
|
// Annotate the CommonJS export names for ESM import in node:
|
|
268
702
|
0 && (module.exports = {
|
|
269
703
|
AWSS3StorageProvider,
|
|
704
|
+
AzureBlobStorageProvider,
|
|
705
|
+
FTPStorageProvider,
|
|
706
|
+
GCPStorageProvider,
|
|
270
707
|
LocalStorageProvider,
|
|
708
|
+
SFTPStorageProvider,
|
|
271
709
|
Storage,
|
|
272
710
|
StorageProviderFactory
|
|
273
711
|
});
|