@quatrain/storage-s3 1.2.5 → 1.2.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.
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AbstractStorageAdapter, FileType, FileResponseLinkType, StorageParameters, DownloadFileMetaType } from '@quatrain/storage';
|
|
1
|
+
import { AbstractStorageAdapter, FileType, FileResponseLinkType, StorageParameters, DownloadFileMetaType, BucketStatsType } from '@quatrain/storage';
|
|
2
2
|
import { Readable, Stream } from 'node:stream';
|
|
3
3
|
import { S3Client } from '@aws-sdk/client-s3';
|
|
4
4
|
/**
|
|
@@ -111,4 +111,12 @@ export declare class S3StorageAdapter extends AbstractStorageAdapter {
|
|
|
111
111
|
* @returns A promise resolving to the upload URL instructions.
|
|
112
112
|
*/
|
|
113
113
|
getUploadUrl(file: FileType, expiresIn?: number): Promise<FileResponseLinkType>;
|
|
114
|
+
/**
|
|
115
|
+
* Computes statistics for a given bucket in S3.
|
|
116
|
+
* Iterates over all objects in the bucket to compute total count and size.
|
|
117
|
+
*
|
|
118
|
+
* @param bucket - Target bucket name.
|
|
119
|
+
* @returns A promise resolving to bucket statistics.
|
|
120
|
+
*/
|
|
121
|
+
getBucketStats(bucket?: string): Promise<BucketStatsType>;
|
|
114
122
|
}
|
package/dist/S3StorageAdapter.js
CHANGED
|
@@ -294,5 +294,69 @@ class S3StorageAdapter extends storage_1.AbstractStorageAdapter {
|
|
|
294
294
|
};
|
|
295
295
|
});
|
|
296
296
|
}
|
|
297
|
+
/**
|
|
298
|
+
* Computes statistics for a given bucket in S3.
|
|
299
|
+
* Iterates over all objects in the bucket to compute total count and size.
|
|
300
|
+
*
|
|
301
|
+
* @param bucket - Target bucket name.
|
|
302
|
+
* @returns A promise resolving to bucket statistics.
|
|
303
|
+
*/
|
|
304
|
+
getBucketStats(bucket) {
|
|
305
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
306
|
+
var _a;
|
|
307
|
+
const targetBucket = bucket || this._params.config.bucket;
|
|
308
|
+
let totalObjects = 0;
|
|
309
|
+
let totalSize = 0;
|
|
310
|
+
let isTruncated = true;
|
|
311
|
+
let continuationToken = undefined;
|
|
312
|
+
const folders = {};
|
|
313
|
+
storage_1.Storage.info(`Fetching bucket stats for ${targetBucket}`);
|
|
314
|
+
while (isTruncated) {
|
|
315
|
+
const input = {
|
|
316
|
+
Bucket: targetBucket,
|
|
317
|
+
ContinuationToken: continuationToken,
|
|
318
|
+
};
|
|
319
|
+
const command = new client_s3_1.ListObjectsV2Command(input);
|
|
320
|
+
const res = yield this._client.send(command);
|
|
321
|
+
if (res.Contents) {
|
|
322
|
+
for (const item of res.Contents) {
|
|
323
|
+
const size = item.Size || 0;
|
|
324
|
+
totalObjects++;
|
|
325
|
+
totalSize += size;
|
|
326
|
+
if (item.Key) {
|
|
327
|
+
const parts = item.Key.split('/');
|
|
328
|
+
parts.pop(); // remove file name to get exact folder
|
|
329
|
+
const folderPath = parts.length > 0 ? parts.join('/') : '/';
|
|
330
|
+
const folderName = parts.length > 0 ? parts[parts.length - 1] : '/';
|
|
331
|
+
if (!folders[folderPath]) {
|
|
332
|
+
folders[folderPath] = {
|
|
333
|
+
name: folderName,
|
|
334
|
+
path: folderPath,
|
|
335
|
+
totalObjects: 0,
|
|
336
|
+
totalSize: 0,
|
|
337
|
+
lastModified: item.LastModified
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
else if (item.LastModified) {
|
|
341
|
+
if (!folders[folderPath].lastModified || item.LastModified > folders[folderPath].lastModified) {
|
|
342
|
+
folders[folderPath].lastModified = item.LastModified;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
folders[folderPath].totalObjects++;
|
|
346
|
+
folders[folderPath].totalSize += size;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
isTruncated = (_a = res.IsTruncated) !== null && _a !== void 0 ? _a : false;
|
|
351
|
+
continuationToken = res.NextContinuationToken;
|
|
352
|
+
}
|
|
353
|
+
return {
|
|
354
|
+
bucket: targetBucket,
|
|
355
|
+
totalObjects,
|
|
356
|
+
totalSize,
|
|
357
|
+
folders,
|
|
358
|
+
};
|
|
359
|
+
});
|
|
360
|
+
}
|
|
297
361
|
}
|
|
298
362
|
exports.S3StorageAdapter = S3StorageAdapter;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quatrain/storage-s3",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
4
4
|
"description": "Storage adapter for S3 compatible services",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@aws-sdk/client-s3": "^3.337.0",
|
|
35
35
|
"@aws-sdk/s3-request-presigner": "^3.342.0",
|
|
36
|
-
"@quatrain/storage": "^1.2.
|
|
36
|
+
"@quatrain/storage": "^1.2.7",
|
|
37
37
|
"fs-extra": "^11.2.0"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
package/src/S3StorageAdapter.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
FileResponseLinkType,
|
|
6
6
|
StorageParameters,
|
|
7
7
|
DownloadFileMetaType,
|
|
8
|
+
BucketStatsType,
|
|
8
9
|
} from '@quatrain/storage'
|
|
9
10
|
import { Readable, Stream } from 'node:stream'
|
|
10
11
|
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
|
|
@@ -17,6 +18,9 @@ import {
|
|
|
17
18
|
CopyObjectCommand,
|
|
18
19
|
HeadObjectCommand,
|
|
19
20
|
ListBucketsCommand,
|
|
21
|
+
ListObjectsV2Command,
|
|
22
|
+
ListObjectsV2CommandInput,
|
|
23
|
+
ListObjectsV2CommandOutput,
|
|
20
24
|
} from '@aws-sdk/client-s3'
|
|
21
25
|
|
|
22
26
|
/**
|
|
@@ -331,4 +335,70 @@ export class S3StorageAdapter extends AbstractStorageAdapter {
|
|
|
331
335
|
expiresIn,
|
|
332
336
|
}
|
|
333
337
|
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Computes statistics for a given bucket in S3.
|
|
341
|
+
* Iterates over all objects in the bucket to compute total count and size.
|
|
342
|
+
*
|
|
343
|
+
* @param bucket - Target bucket name.
|
|
344
|
+
* @returns A promise resolving to bucket statistics.
|
|
345
|
+
*/
|
|
346
|
+
async getBucketStats(bucket?: string): Promise<BucketStatsType> {
|
|
347
|
+
const targetBucket = bucket || this._params.config.bucket
|
|
348
|
+
let totalObjects = 0
|
|
349
|
+
let totalSize = 0
|
|
350
|
+
let isTruncated = true
|
|
351
|
+
let continuationToken: string | undefined = undefined
|
|
352
|
+
const folders: Record<string, any> = {}
|
|
353
|
+
|
|
354
|
+
Storage.info(`Fetching bucket stats for ${targetBucket}`)
|
|
355
|
+
|
|
356
|
+
while (isTruncated) {
|
|
357
|
+
const input: ListObjectsV2CommandInput = {
|
|
358
|
+
Bucket: targetBucket,
|
|
359
|
+
ContinuationToken: continuationToken,
|
|
360
|
+
}
|
|
361
|
+
const command = new ListObjectsV2Command(input)
|
|
362
|
+
const res: ListObjectsV2CommandOutput = await this._client.send(command)
|
|
363
|
+
if (res.Contents) {
|
|
364
|
+
for (const item of res.Contents) {
|
|
365
|
+
const size = item.Size || 0
|
|
366
|
+
totalObjects++
|
|
367
|
+
totalSize += size
|
|
368
|
+
|
|
369
|
+
if (item.Key) {
|
|
370
|
+
const parts = item.Key.split('/')
|
|
371
|
+
parts.pop() // remove file name to get exact folder
|
|
372
|
+
const folderPath = parts.length > 0 ? parts.join('/') : '/'
|
|
373
|
+
const folderName = parts.length > 0 ? parts[parts.length - 1] : '/'
|
|
374
|
+
|
|
375
|
+
if (!folders[folderPath]) {
|
|
376
|
+
folders[folderPath] = {
|
|
377
|
+
name: folderName,
|
|
378
|
+
path: folderPath,
|
|
379
|
+
totalObjects: 0,
|
|
380
|
+
totalSize: 0,
|
|
381
|
+
lastModified: item.LastModified
|
|
382
|
+
}
|
|
383
|
+
} else if (item.LastModified) {
|
|
384
|
+
if (!folders[folderPath].lastModified || item.LastModified > folders[folderPath].lastModified) {
|
|
385
|
+
folders[folderPath].lastModified = item.LastModified
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
folders[folderPath].totalObjects++
|
|
389
|
+
folders[folderPath].totalSize += size
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
isTruncated = res.IsTruncated ?? false
|
|
394
|
+
continuationToken = res.NextContinuationToken
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return {
|
|
398
|
+
bucket: targetBucket,
|
|
399
|
+
totalObjects,
|
|
400
|
+
totalSize,
|
|
401
|
+
folders,
|
|
402
|
+
}
|
|
403
|
+
}
|
|
334
404
|
}
|