@hdriel/aws-utils 1.0.3 → 1.1.1
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/dist/index.cjs +715 -362
- package/dist/index.d.cts +131 -55
- package/dist/index.d.ts +131 -55
- package/dist/index.js +717 -359
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -24,6 +24,18 @@ var __spreadValues = (a, b) => {
|
|
|
24
24
|
return a;
|
|
25
25
|
};
|
|
26
26
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
27
|
+
var __objRest = (source, exclude) => {
|
|
28
|
+
var target = {};
|
|
29
|
+
for (var prop in source)
|
|
30
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
31
|
+
target[prop] = source[prop];
|
|
32
|
+
if (source != null && __getOwnPropSymbols)
|
|
33
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
34
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
35
|
+
target[prop] = source[prop];
|
|
36
|
+
}
|
|
37
|
+
return target;
|
|
38
|
+
};
|
|
27
39
|
var __export = (target, all) => {
|
|
28
40
|
for (var name in all)
|
|
29
41
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -75,7 +87,8 @@ __export(index_exports, {
|
|
|
75
87
|
AWSConfigSharingUtil: () => AWSConfigSharingUtil,
|
|
76
88
|
IAMUtil: () => IAMUtil,
|
|
77
89
|
LambdaUtil: () => LambdaUtil,
|
|
78
|
-
|
|
90
|
+
S3LocalstackUtil: () => S3LocalstackUtil,
|
|
91
|
+
S3Util: () => S3Util,
|
|
79
92
|
SNSUtil: () => SNSUtil
|
|
80
93
|
});
|
|
81
94
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -254,20 +267,19 @@ var LambdaUtil = class {
|
|
|
254
267
|
}
|
|
255
268
|
};
|
|
256
269
|
|
|
257
|
-
// src/aws/s3-
|
|
258
|
-
var
|
|
270
|
+
// src/aws/s3/s3-util.localstack.ts
|
|
271
|
+
var import_client_s36 = require("@aws-sdk/client-s3");
|
|
272
|
+
|
|
273
|
+
// src/aws/s3/s3-stream.ts
|
|
259
274
|
var import_pathe = __toESM(require("pathe"), 1);
|
|
260
|
-
var import_http = __toESM(require("http"), 1);
|
|
261
|
-
var import_https = __toESM(require("https"), 1);
|
|
262
275
|
var import_stream = require("stream");
|
|
263
276
|
var import_util = require("util");
|
|
264
|
-
var
|
|
265
|
-
var import_s3_request_presigner = require("@aws-sdk/s3-request-presigner");
|
|
266
|
-
var import_node_http_handler = require("@smithy/node-http-handler");
|
|
267
|
-
var import_buffer = require("buffer");
|
|
277
|
+
var import_buffer2 = require("buffer");
|
|
268
278
|
var import_archiver = __toESM(require("archiver"), 1);
|
|
269
|
-
var
|
|
270
|
-
var
|
|
279
|
+
var import_node_stream2 = require("stream");
|
|
280
|
+
var import_multer_s3 = __toESM(require("multer-s3"), 1);
|
|
281
|
+
var import_multer = __toESM(require("multer"), 1);
|
|
282
|
+
var import_client_s35 = require("@aws-sdk/client-s3");
|
|
271
283
|
|
|
272
284
|
// src/utils/consts.ts
|
|
273
285
|
var ACLs = /* @__PURE__ */ ((ACLs2) => {
|
|
@@ -281,11 +293,8 @@ var ACLs = /* @__PURE__ */ ((ACLs2) => {
|
|
|
281
293
|
var import_p_limit = __toESM(require("p-limit"), 1);
|
|
282
294
|
var s3Limiter = (0, import_p_limit.default)(4);
|
|
283
295
|
|
|
284
|
-
// src/
|
|
285
|
-
var import_multer = __toESM(require("multer"), 1);
|
|
286
|
-
var import_multer_s3 = __toESM(require("multer-s3"), 1);
|
|
296
|
+
// src/utils/helpers.ts
|
|
287
297
|
var import_bytes = __toESM(require("bytes"), 1);
|
|
288
|
-
var pump = (0, import_util.promisify)(import_stream.pipeline);
|
|
289
298
|
var parseRangeHeader = (range, contentLength, chunkSize) => {
|
|
290
299
|
if (!range || !range.startsWith("bytes=")) return null;
|
|
291
300
|
const rangeParts = range.replace("bytes=", "").split("-");
|
|
@@ -298,34 +307,60 @@ var parseRangeHeader = (range, contentLength, chunkSize) => {
|
|
|
298
307
|
}
|
|
299
308
|
return [start, Math.min(end, end)];
|
|
300
309
|
};
|
|
301
|
-
var getNormalizedPath = (directoryPath) =>
|
|
302
|
-
|
|
310
|
+
var getNormalizedPath = (directoryPath) => {
|
|
311
|
+
return decodeURIComponent((directoryPath == null ? void 0 : directoryPath.trim().replace(/^\/+/, "").replace(/\/+$/, "").replace(/\/+/g, "/")) || "");
|
|
312
|
+
};
|
|
313
|
+
var getFileSize = (maxFileSize, defaultMaxFileSize) => {
|
|
314
|
+
var _a2;
|
|
315
|
+
const fileSizeUnitValue = (_a2 = maxFileSize != null ? maxFileSize : defaultMaxFileSize) != null ? _a2 : "";
|
|
316
|
+
const fileSize = typeof fileSizeUnitValue === "number" ? fileSizeUnitValue : (0, import_bytes.default)(fileSizeUnitValue);
|
|
317
|
+
return fileSize != null ? fileSize : void 0;
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// src/aws/s3/s3-file.ts
|
|
321
|
+
var import_buffer = require("buffer");
|
|
322
|
+
var import_node_stream = require("stream");
|
|
323
|
+
var import_ms = __toESM(require("ms"), 1);
|
|
324
|
+
var import_lib_storage = require("@aws-sdk/lib-storage");
|
|
325
|
+
var import_s3_request_presigner = require("@aws-sdk/s3-request-presigner");
|
|
326
|
+
var import_client_s34 = require("@aws-sdk/client-s3");
|
|
327
|
+
|
|
328
|
+
// src/aws/s3/s3-directory.ts
|
|
329
|
+
var import_client_s33 = require("@aws-sdk/client-s3");
|
|
330
|
+
|
|
331
|
+
// src/aws/s3/s3-bucket.ts
|
|
332
|
+
var import_client_s32 = require("@aws-sdk/client-s3");
|
|
333
|
+
|
|
334
|
+
// src/aws/s3/s3-base.ts
|
|
335
|
+
var import_http = __toESM(require("http"), 1);
|
|
336
|
+
var import_https = __toESM(require("https"), 1);
|
|
337
|
+
var import_node_http_handler = require("@smithy/node-http-handler");
|
|
338
|
+
var import_client_s3 = require("@aws-sdk/client-s3");
|
|
339
|
+
var S3Base = class {
|
|
303
340
|
constructor({
|
|
304
341
|
logger: logger2,
|
|
305
|
-
bucket,
|
|
306
342
|
reqId,
|
|
307
343
|
accessKeyId = AWSConfigSharingUtil.accessKeyId,
|
|
308
344
|
secretAccessKey = AWSConfigSharingUtil.secretAccessKey,
|
|
309
345
|
endpoint = AWSConfigSharingUtil.endpoint,
|
|
310
346
|
region = AWSConfigSharingUtil.region,
|
|
311
347
|
s3ForcePathStyle = true,
|
|
312
|
-
|
|
348
|
+
// @ts-ignore
|
|
349
|
+
localstack = false
|
|
313
350
|
}) {
|
|
314
351
|
__publicField(this, "s3Client");
|
|
315
|
-
__publicField(this, "bucket");
|
|
316
352
|
__publicField(this, "endpoint");
|
|
317
353
|
__publicField(this, "region");
|
|
318
354
|
__publicField(this, "logger");
|
|
319
355
|
__publicField(this, "reqId");
|
|
320
|
-
__publicField(this, "
|
|
356
|
+
__publicField(this, "localstack", false);
|
|
321
357
|
const credentials = { accessKeyId, secretAccessKey };
|
|
322
358
|
const options = __spreadValues(__spreadValues(__spreadValues({}, accessKeyId && secretAccessKey && { credentials }), endpoint && { endpoint }), region && { region });
|
|
323
359
|
this.endpoint = endpoint;
|
|
324
360
|
this.region = region;
|
|
325
|
-
this.bucket = bucket;
|
|
326
361
|
this.logger = logger2;
|
|
327
362
|
this.reqId = reqId != null ? reqId : null;
|
|
328
|
-
this.
|
|
363
|
+
this.localstack = localstack;
|
|
329
364
|
const s3ClientParams = __spreadProps(__spreadValues(__spreadValues({}, options), s3ForcePathStyle && { forcePathStyle: s3ForcePathStyle }), {
|
|
330
365
|
requestHandler: new import_node_http_handler.NodeHttpHandler({
|
|
331
366
|
httpAgent: new import_http.default.Agent({ keepAlive: true, maxSockets: 300 }),
|
|
@@ -336,18 +371,40 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
336
371
|
});
|
|
337
372
|
this.s3Client = new import_client_s3.S3Client(s3ClientParams);
|
|
338
373
|
}
|
|
339
|
-
get link() {
|
|
340
|
-
return this.endpoint === "http://localhost:4566" ? `${this.endpoint}/${this.bucket}/` : `https://s3.${this.region}.amazonaws.com/${this.bucket}/`;
|
|
341
|
-
}
|
|
342
374
|
execute(command, options) {
|
|
343
375
|
return __async(this, null, function* () {
|
|
344
376
|
return this.s3Client.send(command, options);
|
|
345
377
|
});
|
|
346
378
|
}
|
|
347
|
-
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
// src/aws/s3/s3-bucket.ts
|
|
382
|
+
var S3Bucket = class extends S3Base {
|
|
383
|
+
constructor(_a2) {
|
|
384
|
+
var _b = _a2, { bucket } = _b, props = __objRest(_b, ["bucket"]);
|
|
385
|
+
super(props);
|
|
386
|
+
__publicField(this, "_bucket");
|
|
387
|
+
__publicField(this, "initializedBucket", "");
|
|
388
|
+
this._bucket = decodeURIComponent(bucket);
|
|
389
|
+
}
|
|
390
|
+
get link() {
|
|
391
|
+
return this.endpoint === "http://localhost:4566" ? `${this.endpoint}/${this.bucket}/` : `https://s3.${this.region}.amazonaws.com/${this.bucket}/`;
|
|
392
|
+
}
|
|
393
|
+
get bucket() {
|
|
394
|
+
return this._bucket;
|
|
395
|
+
}
|
|
396
|
+
changeBucket(bucket) {
|
|
397
|
+
this._bucket = decodeURIComponent(bucket);
|
|
398
|
+
this.initializedBucket = "";
|
|
399
|
+
}
|
|
348
400
|
getBucketList() {
|
|
349
|
-
return __async(this, arguments, function* (
|
|
350
|
-
|
|
401
|
+
return __async(this, arguments, function* (_c = {}) {
|
|
402
|
+
var _d = _c, {
|
|
403
|
+
includePublicAccess
|
|
404
|
+
} = _d, options = __objRest(_d, [
|
|
405
|
+
"includePublicAccess"
|
|
406
|
+
]);
|
|
407
|
+
const command = new import_client_s32.ListBucketsCommand(options);
|
|
351
408
|
const response = yield this.execute(command);
|
|
352
409
|
const responseData = (response == null ? void 0 : response.Buckets) || null;
|
|
353
410
|
if (!responseData) return null;
|
|
@@ -355,7 +412,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
355
412
|
yield Promise.allSettled(
|
|
356
413
|
responseData.map((data) => __async(this, null, function* () {
|
|
357
414
|
const result = yield this.execute(
|
|
358
|
-
new
|
|
415
|
+
new import_client_s32.GetPublicAccessBlockCommand({ Bucket: data.Name })
|
|
359
416
|
);
|
|
360
417
|
data.PublicAccessBlockConfiguration = result.PublicAccessBlockConfiguration;
|
|
361
418
|
}))
|
|
@@ -369,7 +426,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
369
426
|
var _a2, _b;
|
|
370
427
|
const bucketName = this.bucket;
|
|
371
428
|
try {
|
|
372
|
-
yield this.execute(new
|
|
429
|
+
yield this.execute(new import_client_s32.HeadBucketCommand({ Bucket: bucketName }));
|
|
373
430
|
return true;
|
|
374
431
|
} catch (err) {
|
|
375
432
|
if (err.name !== "NotFound" && ((_a2 = err.$metadata) == null ? void 0 : _a2.httpStatusCode) !== 404) {
|
|
@@ -390,9 +447,9 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
390
447
|
(_a2 = this.logger) == null ? void 0 : _a2.info(this.reqId, `Bucket already exists.`, { bucketName });
|
|
391
448
|
return;
|
|
392
449
|
}
|
|
393
|
-
const data = yield this.execute(new
|
|
450
|
+
const data = yield this.execute(new import_client_s32.CreateBucketCommand({ Bucket: bucketName }));
|
|
394
451
|
CREATE_PUBLICK_ACCESS_BLOCK: {
|
|
395
|
-
const command = new
|
|
452
|
+
const command = new import_client_s32.PutPublicAccessBlockCommand({
|
|
396
453
|
Bucket: bucketName,
|
|
397
454
|
PublicAccessBlockConfiguration: {
|
|
398
455
|
BlockPublicAcls: false,
|
|
@@ -416,7 +473,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
416
473
|
}
|
|
417
474
|
]
|
|
418
475
|
};
|
|
419
|
-
const command = new
|
|
476
|
+
const command = new import_client_s32.PutBucketPolicyCommand({ Bucket: bucketName, Policy: JSON.stringify(policy) });
|
|
420
477
|
yield this.execute(command);
|
|
421
478
|
}
|
|
422
479
|
(_b = this.logger) == null ? void 0 : _b.info(this.reqId, `Public bucket created successfully.`, { bucketName });
|
|
@@ -437,21 +494,28 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
437
494
|
}, includeConstraintLocation && {
|
|
438
495
|
CreateBucketConfiguration: { LocationConstraint: this.region }
|
|
439
496
|
});
|
|
440
|
-
const data = yield this.execute(new
|
|
497
|
+
const data = yield this.execute(new import_client_s32.CreateBucketCommand(createParams));
|
|
441
498
|
(_b = this.logger) == null ? void 0 : _b.info(this.reqId, `Private bucket created successfully.`, { bucketName });
|
|
442
499
|
return data;
|
|
443
500
|
});
|
|
444
501
|
}
|
|
445
502
|
initBucket() {
|
|
446
|
-
return __async(this, arguments, function* (acl = "private" /* private */,
|
|
503
|
+
return __async(this, arguments, function* (acl = "private" /* private */, {
|
|
504
|
+
includeConstraintLocation = false,
|
|
505
|
+
skipInitializedBucket = false
|
|
506
|
+
} = {}) {
|
|
447
507
|
var _a2;
|
|
448
508
|
const bucketName = this.bucket;
|
|
509
|
+
if (skipInitializedBucket && this.initializedBucket === bucketName) {
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
449
512
|
const isExists = yield this.isBucketExists();
|
|
450
513
|
if (isExists) {
|
|
451
514
|
(_a2 = this.logger) == null ? void 0 : _a2.info(this.reqId, `Bucket already exists.`, { bucketName });
|
|
452
515
|
return;
|
|
453
516
|
}
|
|
454
517
|
const data = acl === "private" /* private */ ? yield this.initAsPrivateBucket(includeConstraintLocation) : yield this.initAsPublicBucket();
|
|
518
|
+
this.initializedBucket = bucketName;
|
|
455
519
|
return data;
|
|
456
520
|
});
|
|
457
521
|
}
|
|
@@ -460,14 +524,14 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
460
524
|
let ContinuationToken = void 0;
|
|
461
525
|
do {
|
|
462
526
|
const listResp = yield this.execute(
|
|
463
|
-
new
|
|
527
|
+
new import_client_s32.ListObjectsV2Command({
|
|
464
528
|
Bucket: this.bucket,
|
|
465
529
|
ContinuationToken
|
|
466
530
|
})
|
|
467
531
|
);
|
|
468
532
|
if (listResp.Contents && listResp.Contents.length > 0) {
|
|
469
533
|
yield this.execute(
|
|
470
|
-
new
|
|
534
|
+
new import_client_s32.DeleteObjectsCommand({
|
|
471
535
|
Bucket: this.bucket,
|
|
472
536
|
Delete: {
|
|
473
537
|
Objects: listResp.Contents.map((obj) => ({ Key: obj.Key }))
|
|
@@ -492,7 +556,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
492
556
|
try {
|
|
493
557
|
try {
|
|
494
558
|
const headBucketResponse = yield this.execute(
|
|
495
|
-
new
|
|
559
|
+
new import_client_s32.HeadBucketCommand(__spreadValues({ Bucket: bucketName }, options))
|
|
496
560
|
);
|
|
497
561
|
(_a2 = this.logger) == null ? void 0 : _a2.debug("bucketInfo", "HeadBucketCommandOutput", headBucketResponse);
|
|
498
562
|
info.exists = true;
|
|
@@ -516,7 +580,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
516
580
|
}
|
|
517
581
|
try {
|
|
518
582
|
const aclResponse = yield this.execute(
|
|
519
|
-
new
|
|
583
|
+
new import_client_s32.GetBucketAclCommand({ Bucket: bucketName })
|
|
520
584
|
);
|
|
521
585
|
(_e = this.logger) == null ? void 0 : _e.debug("bucketInfo", "GetBucketAclCommandOutput", aclResponse);
|
|
522
586
|
info.acl = (_f = aclResponse.Grants) == null ? void 0 : _f.map((grant) => {
|
|
@@ -531,7 +595,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
531
595
|
}
|
|
532
596
|
try {
|
|
533
597
|
const publicAccessResponse = yield this.execute(
|
|
534
|
-
new
|
|
598
|
+
new import_client_s32.GetPublicAccessBlockCommand({ Bucket: bucketName })
|
|
535
599
|
);
|
|
536
600
|
(_h = this.logger) == null ? void 0 : _h.debug("bucketInfo", "GetPublicAccessBlockCommandOutput", publicAccessResponse);
|
|
537
601
|
info.publicAccessBlock = publicAccessResponse.PublicAccessBlockConfiguration;
|
|
@@ -542,7 +606,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
542
606
|
}
|
|
543
607
|
try {
|
|
544
608
|
const policyResponse = yield this.execute(
|
|
545
|
-
new
|
|
609
|
+
new import_client_s32.GetBucketPolicyCommand({ Bucket: bucketName })
|
|
546
610
|
);
|
|
547
611
|
(_j = this.logger) == null ? void 0 : _j.debug("bucketInfo", "GetBucketPolicyCommandOutput", policyResponse);
|
|
548
612
|
if (policyResponse.Policy) {
|
|
@@ -555,7 +619,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
555
619
|
}
|
|
556
620
|
try {
|
|
557
621
|
const versioningResponse = yield this.execute(
|
|
558
|
-
new
|
|
622
|
+
new import_client_s32.GetBucketVersioningCommand({ Bucket: bucketName })
|
|
559
623
|
);
|
|
560
624
|
(_l = this.logger) == null ? void 0 : _l.debug("bucketInfo", "GetBucketVersioningCommandOutput", versioningResponse);
|
|
561
625
|
info.versioning = versioningResponse.Status || "Disabled";
|
|
@@ -564,7 +628,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
564
628
|
}
|
|
565
629
|
try {
|
|
566
630
|
const encryptionResponse = yield this.execute(
|
|
567
|
-
new
|
|
631
|
+
new import_client_s32.GetBucketEncryptionCommand({ Bucket: bucketName })
|
|
568
632
|
);
|
|
569
633
|
(_n = this.logger) == null ? void 0 : _n.debug("bucketInfo", "GetBucketEncryptionCommandOutput", encryptionResponse);
|
|
570
634
|
info.encryption = {
|
|
@@ -600,21 +664,46 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
600
664
|
yield this.emptyBucket();
|
|
601
665
|
}
|
|
602
666
|
const createParams = { Bucket: bucketName };
|
|
603
|
-
const data = yield this.execute(new
|
|
667
|
+
const data = yield this.execute(new import_client_s32.DeleteBucketCommand(createParams));
|
|
604
668
|
return data;
|
|
605
669
|
});
|
|
606
670
|
}
|
|
607
|
-
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
// src/aws/s3/s3-directory.ts
|
|
674
|
+
var S3Directory = class extends S3Bucket {
|
|
675
|
+
constructor(props) {
|
|
676
|
+
super(props);
|
|
677
|
+
}
|
|
678
|
+
// todo: checked!
|
|
679
|
+
directoryExists(directoryPath) {
|
|
680
|
+
return __async(this, null, function* () {
|
|
681
|
+
var _a2;
|
|
682
|
+
try {
|
|
683
|
+
const normalizedKey = getNormalizedPath(directoryPath);
|
|
684
|
+
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
685
|
+
const command = new import_client_s33.HeadObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
|
|
686
|
+
yield this.execute(command);
|
|
687
|
+
return true;
|
|
688
|
+
} catch (error) {
|
|
689
|
+
if (error.name === "NotFound" || ((_a2 = error.$metadata) == null ? void 0 : _a2.httpStatusCode) === 404) {
|
|
690
|
+
return false;
|
|
691
|
+
}
|
|
692
|
+
throw error;
|
|
693
|
+
}
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
// todo: checked!
|
|
608
697
|
createDirectory(directoryPath) {
|
|
609
698
|
return __async(this, null, function* () {
|
|
610
699
|
let normalizedPath = getNormalizedPath(directoryPath);
|
|
611
|
-
if (!normalizedPath) throw new Error("No directory path provided");
|
|
612
|
-
|
|
613
|
-
const command = new import_client_s3.PutObjectCommand({ Bucket: this.bucket, Key: `${normalizedPath}/` });
|
|
700
|
+
if (!normalizedPath || normalizedPath === "/") throw new Error("No directory path provided");
|
|
701
|
+
const command = new import_client_s33.PutObjectCommand({ Bucket: this.bucket, Key: `${normalizedPath}/` });
|
|
614
702
|
const result = yield this.execute(command);
|
|
615
703
|
return result;
|
|
616
704
|
});
|
|
617
705
|
}
|
|
706
|
+
// todo: checked!
|
|
618
707
|
deleteDirectory(directoryPath) {
|
|
619
708
|
return __async(this, null, function* () {
|
|
620
709
|
var _a2, _b, _c, _d, _e, _f, _g;
|
|
@@ -625,7 +714,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
625
714
|
let ContinuationToken = void 0;
|
|
626
715
|
do {
|
|
627
716
|
const listResp = yield this.execute(
|
|
628
|
-
new
|
|
717
|
+
new import_client_s33.ListObjectsV2Command({
|
|
629
718
|
Bucket: this.bucket,
|
|
630
719
|
Prefix: normalizedPath,
|
|
631
720
|
ContinuationToken
|
|
@@ -633,7 +722,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
633
722
|
);
|
|
634
723
|
if (listResp.Contents && listResp.Contents.length > 0) {
|
|
635
724
|
const deleteResult = yield this.execute(
|
|
636
|
-
new
|
|
725
|
+
new import_client_s33.DeleteObjectsCommand({
|
|
637
726
|
Bucket: this.bucket,
|
|
638
727
|
Delete: {
|
|
639
728
|
Objects: listResp.Contents.map((obj) => ({ Key: obj.Key })),
|
|
@@ -652,7 +741,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
652
741
|
ContinuationToken = listResp.NextContinuationToken;
|
|
653
742
|
} while (ContinuationToken);
|
|
654
743
|
if (totalDeletedCount === 0) {
|
|
655
|
-
const directoryExists = yield this.
|
|
744
|
+
const directoryExists = yield this.directoryExists(normalizedPath);
|
|
656
745
|
if (!directoryExists) {
|
|
657
746
|
(_d = this.logger) == null ? void 0 : _d.debug(this.reqId, `Directory not found`, { directoryPath: normalizedPath });
|
|
658
747
|
return null;
|
|
@@ -660,7 +749,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
660
749
|
}
|
|
661
750
|
try {
|
|
662
751
|
yield this.execute(
|
|
663
|
-
new
|
|
752
|
+
new import_client_s33.DeleteObjectCommand({
|
|
664
753
|
Bucket: this.bucket,
|
|
665
754
|
Key: normalizedPath
|
|
666
755
|
})
|
|
@@ -684,33 +773,64 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
684
773
|
};
|
|
685
774
|
});
|
|
686
775
|
}
|
|
776
|
+
// todo: checked!
|
|
687
777
|
directoryList(directoryPath) {
|
|
688
778
|
return __async(this, null, function* () {
|
|
779
|
+
var _a2;
|
|
689
780
|
let normalizedPath = getNormalizedPath(directoryPath);
|
|
690
781
|
if (normalizedPath !== "/" && directoryPath !== "" && directoryPath !== void 0) normalizedPath += "/";
|
|
691
|
-
else normalizedPath = "";
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
782
|
+
else normalizedPath = this.localstack ? "" : "/";
|
|
783
|
+
let result;
|
|
784
|
+
if (normalizedPath === "") {
|
|
785
|
+
const [fileResponse, { CommonPrefixes }] = yield Promise.all([
|
|
786
|
+
this.execute(
|
|
787
|
+
new import_client_s33.ListObjectsV2Command({
|
|
788
|
+
Bucket: this.bucket,
|
|
789
|
+
Prefix: "/",
|
|
790
|
+
Delimiter: "/"
|
|
791
|
+
})
|
|
792
|
+
),
|
|
793
|
+
yield this.execute(
|
|
794
|
+
new import_client_s33.ListObjectsV2Command({
|
|
795
|
+
Bucket: this.bucket,
|
|
796
|
+
Prefix: "",
|
|
797
|
+
Delimiter: "/"
|
|
798
|
+
})
|
|
799
|
+
)
|
|
800
|
+
]);
|
|
801
|
+
result = fileResponse;
|
|
802
|
+
result.CommonPrefixes = CommonPrefixes;
|
|
803
|
+
} else {
|
|
804
|
+
result = yield this.execute(
|
|
805
|
+
new import_client_s33.ListObjectsV2Command({
|
|
806
|
+
Bucket: this.bucket,
|
|
807
|
+
Prefix: normalizedPath,
|
|
808
|
+
Delimiter: "/"
|
|
809
|
+
})
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
(_a2 = this.logger) == null ? void 0 : _a2.debug(null, "#### directoryList", {
|
|
813
|
+
normalizedPath,
|
|
814
|
+
CommonPrefixes: result.CommonPrefixes,
|
|
815
|
+
ContentFile: result.Contents
|
|
816
|
+
});
|
|
699
817
|
const directories = (result.CommonPrefixes || []).map((prefix) => prefix.Prefix).map((prefix) => {
|
|
700
818
|
const relativePath = prefix.replace(normalizedPath, "");
|
|
701
819
|
const dir = relativePath.replace(/\/$/, "");
|
|
702
820
|
return dir;
|
|
703
821
|
}).filter((dir) => dir);
|
|
704
822
|
const files = (result.Contents || []).filter((content) => {
|
|
705
|
-
var
|
|
706
|
-
return content.Key !== normalizedPath && !((
|
|
823
|
+
var _a3;
|
|
824
|
+
return content.Key !== normalizedPath && !((_a3 = content.Key) == null ? void 0 : _a3.endsWith("/"));
|
|
707
825
|
}).map((content) => __spreadProps(__spreadValues({}, content), {
|
|
708
826
|
Name: content.Key.replace(normalizedPath, "") || content.Key,
|
|
827
|
+
Location: `${this.link}${content.Key.replace(/^\//, "")}`,
|
|
709
828
|
LastModified: new Date(content.LastModified)
|
|
710
829
|
}));
|
|
711
830
|
return { directories, files };
|
|
712
831
|
});
|
|
713
832
|
}
|
|
833
|
+
// todo: checked!
|
|
714
834
|
directoryListPaginated(_0) {
|
|
715
835
|
return __async(this, arguments, function* (directoryPath, {
|
|
716
836
|
pageSize = 100,
|
|
@@ -725,8 +845,9 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
725
845
|
let allDirectories = [];
|
|
726
846
|
let allFiles = [];
|
|
727
847
|
while (currentPage <= pageNumber) {
|
|
728
|
-
|
|
729
|
-
|
|
848
|
+
let result;
|
|
849
|
+
result = yield this.execute(
|
|
850
|
+
new import_client_s33.ListObjectsV2Command({
|
|
730
851
|
Bucket: this.bucket,
|
|
731
852
|
Prefix: normalizedPath,
|
|
732
853
|
Delimiter: "/",
|
|
@@ -744,6 +865,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
744
865
|
return content.Key !== normalizedPath && !((_a2 = content.Key) == null ? void 0 : _a2.endsWith("/"));
|
|
745
866
|
}).map((content) => __spreadProps(__spreadValues({}, content), {
|
|
746
867
|
Name: content.Key.replace(normalizedPath, "") || content.Key,
|
|
868
|
+
Location: `${this.link}${content.Key.replace(/^\//, "")}`,
|
|
747
869
|
LastModified: new Date(content.LastModified)
|
|
748
870
|
}));
|
|
749
871
|
}
|
|
@@ -766,19 +888,21 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
766
888
|
*/
|
|
767
889
|
directoryListRecursive(directoryPath) {
|
|
768
890
|
return __async(this, null, function* () {
|
|
891
|
+
var _a2;
|
|
769
892
|
let normalizedPath = getNormalizedPath(directoryPath);
|
|
770
893
|
if (normalizedPath !== "/" && directoryPath !== "" && directoryPath !== void 0) normalizedPath += "/";
|
|
771
|
-
else normalizedPath = "";
|
|
894
|
+
else normalizedPath = "/";
|
|
772
895
|
const allDirectories = [];
|
|
773
896
|
const allFiles = [];
|
|
774
897
|
let ContinuationToken = void 0;
|
|
775
898
|
do {
|
|
776
|
-
const
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
899
|
+
const result = yield this.execute(
|
|
900
|
+
new import_client_s33.ListObjectsV2Command({
|
|
901
|
+
Bucket: this.bucket,
|
|
902
|
+
Prefix: normalizedPath,
|
|
903
|
+
ContinuationToken
|
|
904
|
+
})
|
|
905
|
+
);
|
|
782
906
|
if (result.Contents) {
|
|
783
907
|
for (const content of result.Contents) {
|
|
784
908
|
const fullPath = content.Key;
|
|
@@ -790,7 +914,8 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
790
914
|
allFiles.push(__spreadProps(__spreadValues({}, content), {
|
|
791
915
|
Name: filename,
|
|
792
916
|
Path: fullPath,
|
|
793
|
-
|
|
917
|
+
Location: content.Key ? `${this.link}${(_a2 = content.Key) == null ? void 0 : _a2.replace(/^\//, "")}` : "",
|
|
918
|
+
LastModified: content.LastModified ? new Date(content.LastModified) : null
|
|
794
919
|
}));
|
|
795
920
|
}
|
|
796
921
|
}
|
|
@@ -846,6 +971,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
846
971
|
treeNode.children.push({
|
|
847
972
|
path: "/" + file.Key,
|
|
848
973
|
name: file.Name,
|
|
974
|
+
location: `${this.link}${file.Key.replace(/^\//, "")}`,
|
|
849
975
|
type: "file",
|
|
850
976
|
size: file.Size,
|
|
851
977
|
lastModified: file.LastModified
|
|
@@ -859,14 +985,18 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
859
985
|
return treeNode;
|
|
860
986
|
});
|
|
861
987
|
}
|
|
862
|
-
|
|
988
|
+
};
|
|
989
|
+
|
|
990
|
+
// src/aws/s3/s3-file.ts
|
|
991
|
+
var S3File = class extends S3Directory {
|
|
992
|
+
constructor(props) {
|
|
993
|
+
super(props);
|
|
994
|
+
}
|
|
863
995
|
fileInfo(filePath) {
|
|
864
996
|
return __async(this, null, function* () {
|
|
865
997
|
const normalizedKey = getNormalizedPath(filePath);
|
|
866
|
-
if (!normalizedKey || normalizedKey === "/")
|
|
867
|
-
|
|
868
|
-
}
|
|
869
|
-
const command = new import_client_s3.HeadObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
|
|
998
|
+
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
999
|
+
const command = new import_client_s34.HeadObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
|
|
870
1000
|
return yield this.execute(command);
|
|
871
1001
|
});
|
|
872
1002
|
}
|
|
@@ -875,9 +1005,9 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
875
1005
|
var _a2, _b;
|
|
876
1006
|
let normalizedPath = getNormalizedPath(directoryPath);
|
|
877
1007
|
if (normalizedPath !== "/" && directoryPath !== "" && directoryPath !== void 0) normalizedPath += "/";
|
|
878
|
-
else normalizedPath = "";
|
|
1008
|
+
else normalizedPath = this.localstack ? "" : "/";
|
|
879
1009
|
const prefix = normalizedPath + (fileNamePrefix || "");
|
|
880
|
-
const command = new
|
|
1010
|
+
const command = new import_client_s34.ListObjectsCommand({
|
|
881
1011
|
Bucket: this.bucket,
|
|
882
1012
|
Prefix: prefix,
|
|
883
1013
|
Delimiter: "/"
|
|
@@ -885,9 +1015,10 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
885
1015
|
const result = yield this.execute(command);
|
|
886
1016
|
const files = ((_a2 = result.Contents) != null ? _a2 : []).filter((v) => v).map(
|
|
887
1017
|
(content) => {
|
|
888
|
-
var _a3, _b2;
|
|
1018
|
+
var _a3, _b2, _c;
|
|
889
1019
|
return __spreadProps(__spreadValues({}, content), {
|
|
890
1020
|
Name: (_b2 = (_a3 = content.Key) == null ? void 0 : _a3.replace(prefix, "")) != null ? _b2 : content.Key,
|
|
1021
|
+
Location: content.Key ? `${this.link}${(_c = content.Key) == null ? void 0 : _c.replace(/^\//, "")}` : "",
|
|
891
1022
|
LastModified: content.LastModified ? new Date(content.LastModified) : null
|
|
892
1023
|
});
|
|
893
1024
|
}
|
|
@@ -896,6 +1027,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
896
1027
|
return files;
|
|
897
1028
|
});
|
|
898
1029
|
}
|
|
1030
|
+
// todo: checked!
|
|
899
1031
|
fileListInfoPaginated(_0) {
|
|
900
1032
|
return __async(this, arguments, function* (directoryPath, {
|
|
901
1033
|
fileNamePrefix,
|
|
@@ -913,7 +1045,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
913
1045
|
let resultFiles = [];
|
|
914
1046
|
while (currentPage <= pageNumber) {
|
|
915
1047
|
const result = yield this.execute(
|
|
916
|
-
new
|
|
1048
|
+
new import_client_s34.ListObjectsV2Command({
|
|
917
1049
|
Bucket: this.bucket,
|
|
918
1050
|
Prefix: prefix,
|
|
919
1051
|
Delimiter: "/",
|
|
@@ -927,6 +1059,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
927
1059
|
var _a3, _b2;
|
|
928
1060
|
return __spreadProps(__spreadValues({}, content), {
|
|
929
1061
|
Name: (_b2 = (_a3 = content.Key) == null ? void 0 : _a3.replace(prefix, "")) != null ? _b2 : content.Key,
|
|
1062
|
+
Location: content.Key ? `${this.link}${content.Key.replace(/^\//, "")}` : "",
|
|
930
1063
|
LastModified: content.LastModified ? new Date(content.LastModified) : null
|
|
931
1064
|
});
|
|
932
1065
|
}
|
|
@@ -950,41 +1083,48 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
950
1083
|
};
|
|
951
1084
|
});
|
|
952
1085
|
}
|
|
953
|
-
|
|
1086
|
+
// todo: checked!
|
|
1087
|
+
taggingFile(filePath, tag) {
|
|
954
1088
|
return __async(this, null, function* () {
|
|
1089
|
+
var _a2;
|
|
1090
|
+
let normalizedKey = "";
|
|
1091
|
+
const tags = [].concat(tag);
|
|
955
1092
|
try {
|
|
956
|
-
|
|
1093
|
+
normalizedKey = getNormalizedPath(filePath);
|
|
957
1094
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
958
|
-
const command = new
|
|
1095
|
+
const command = new import_client_s34.PutObjectTaggingCommand({
|
|
959
1096
|
Bucket: this.bucket,
|
|
960
1097
|
Key: normalizedKey,
|
|
961
|
-
Tagging: { TagSet:
|
|
1098
|
+
Tagging: { TagSet: tags }
|
|
962
1099
|
});
|
|
963
1100
|
yield this.execute(command);
|
|
964
1101
|
return true;
|
|
965
|
-
} catch (
|
|
1102
|
+
} catch (error) {
|
|
1103
|
+
(_a2 = this.logger) == null ? void 0 : _a2.warn(null, "failed to tagging file", { errMsg: error.message, fileKey: normalizedKey, tags });
|
|
966
1104
|
return false;
|
|
967
1105
|
}
|
|
968
1106
|
});
|
|
969
1107
|
}
|
|
1108
|
+
// todo: checked!
|
|
970
1109
|
fileVersion(filePath) {
|
|
971
1110
|
return __async(this, null, function* () {
|
|
972
1111
|
var _a2, _b;
|
|
973
1112
|
const normalizedKey = getNormalizedPath(filePath);
|
|
974
1113
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
975
|
-
const command = new
|
|
1114
|
+
const command = new import_client_s34.GetObjectTaggingCommand({ Bucket: this.bucket, Key: normalizedKey });
|
|
976
1115
|
const result = yield this.execute(command);
|
|
977
1116
|
const tag = (_a2 = result.TagSet) == null ? void 0 : _a2.find((tag2) => tag2.Key === "version");
|
|
978
1117
|
return (_b = tag == null ? void 0 : tag.Value) != null ? _b : "";
|
|
979
1118
|
});
|
|
980
1119
|
}
|
|
1120
|
+
// todo: checked!
|
|
981
1121
|
fileUrl(filePath, expiresIn = "15m") {
|
|
982
1122
|
return __async(this, null, function* () {
|
|
983
1123
|
var _a2;
|
|
984
|
-
|
|
1124
|
+
let normalizedKey = getNormalizedPath(filePath);
|
|
985
1125
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
986
1126
|
const expiresInSeconds = typeof expiresIn === "number" ? expiresIn : (0, import_ms.default)(expiresIn) / 1e3;
|
|
987
|
-
const command = new
|
|
1127
|
+
const command = new import_client_s34.GetObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
|
|
988
1128
|
const url = yield (0, import_s3_request_presigner.getSignedUrl)(this.s3Client, command, {
|
|
989
1129
|
expiresIn: expiresInSeconds
|
|
990
1130
|
// is using 3600 it's will expire in 1 hour (default is 900 seconds = 15 minutes)
|
|
@@ -999,7 +1139,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
999
1139
|
const normalizedKey = getNormalizedPath(filePath);
|
|
1000
1140
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1001
1141
|
try {
|
|
1002
|
-
const command = new
|
|
1142
|
+
const command = new import_client_s34.HeadObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
|
|
1003
1143
|
const headObject = yield this.execute(command);
|
|
1004
1144
|
const bytes2 = (_a2 = headObject.ContentLength) != null ? _a2 : 0;
|
|
1005
1145
|
switch (unit) {
|
|
@@ -1021,13 +1161,14 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1021
1161
|
}
|
|
1022
1162
|
});
|
|
1023
1163
|
}
|
|
1164
|
+
// todo: checked!
|
|
1024
1165
|
fileExists(filePath) {
|
|
1025
1166
|
return __async(this, null, function* () {
|
|
1026
1167
|
var _a2;
|
|
1027
1168
|
try {
|
|
1028
1169
|
const normalizedKey = getNormalizedPath(filePath);
|
|
1029
1170
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1030
|
-
const command = new
|
|
1171
|
+
const command = new import_client_s34.HeadObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
|
|
1031
1172
|
yield this.execute(command);
|
|
1032
1173
|
return true;
|
|
1033
1174
|
} catch (error) {
|
|
@@ -1038,11 +1179,12 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1038
1179
|
}
|
|
1039
1180
|
});
|
|
1040
1181
|
}
|
|
1182
|
+
// todo: checked!
|
|
1041
1183
|
fileContent(filePath, format = "buffer") {
|
|
1042
1184
|
return __async(this, null, function* () {
|
|
1043
|
-
|
|
1185
|
+
let normalizedKey = getNormalizedPath(filePath);
|
|
1044
1186
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1045
|
-
const command = new
|
|
1187
|
+
const command = new import_client_s34.GetObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
|
|
1046
1188
|
const result = yield this.execute(command);
|
|
1047
1189
|
if (!result.Body) {
|
|
1048
1190
|
throw new Error("File body is empty");
|
|
@@ -1071,6 +1213,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1071
1213
|
return buffer;
|
|
1072
1214
|
});
|
|
1073
1215
|
}
|
|
1216
|
+
// todo: checked!
|
|
1074
1217
|
uploadFile(_0, _1) {
|
|
1075
1218
|
return __async(this, arguments, function* (filePath, fileData, acl = "private" /* private */, version = "1.0.0") {
|
|
1076
1219
|
const normalizedKey = getNormalizedPath(filePath);
|
|
@@ -1094,38 +1237,130 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1094
1237
|
};
|
|
1095
1238
|
});
|
|
1096
1239
|
}
|
|
1240
|
+
// todo: checked!
|
|
1097
1241
|
deleteFile(filePath) {
|
|
1098
1242
|
return __async(this, null, function* () {
|
|
1099
1243
|
const normalizedKey = getNormalizedPath(filePath);
|
|
1100
1244
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1101
|
-
const command = new
|
|
1245
|
+
const command = new import_client_s34.DeleteObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
|
|
1102
1246
|
return yield this.execute(command);
|
|
1103
1247
|
});
|
|
1104
1248
|
}
|
|
1105
|
-
|
|
1249
|
+
};
|
|
1250
|
+
|
|
1251
|
+
// src/aws/s3/s3-stream.ts
|
|
1252
|
+
var pump = (0, import_util.promisify)(import_stream.pipeline);
|
|
1253
|
+
var S3Stream = class _S3Stream extends S3File {
|
|
1254
|
+
constructor(_a2) {
|
|
1255
|
+
var _b = _a2, { maxUploadFileSizeRestriction = "10GB" } = _b, props = __objRest(_b, ["maxUploadFileSizeRestriction"]);
|
|
1256
|
+
super(props);
|
|
1257
|
+
__publicField(this, "maxUploadFileSizeRestriction");
|
|
1258
|
+
// todo: LOCALSTACK SANITY CHECKED - WORKING WELL, DON'T TOUCH!
|
|
1259
|
+
__publicField(this, "getImageFileViewCtrl", ({
|
|
1260
|
+
fileKey: _fileKey,
|
|
1261
|
+
queryField = "file",
|
|
1262
|
+
cachingAge = 31536e3
|
|
1263
|
+
} = {}) => {
|
|
1264
|
+
return (req, res, next) => __async(this, null, function* () {
|
|
1265
|
+
var _a2, _b, _c, _d, _e;
|
|
1266
|
+
let fileKey = _fileKey || (((_a2 = req.query) == null ? void 0 : _a2[queryField]) ? decodeURIComponent((_b = req.query) == null ? void 0 : _b[queryField]) : void 0);
|
|
1267
|
+
if (!fileKey || fileKey === "/") {
|
|
1268
|
+
(_d = this.logger) == null ? void 0 : _d.warn(req.id, "image file view required file query field", {
|
|
1269
|
+
fileKey: (_c = req.query) == null ? void 0 : _c[queryField],
|
|
1270
|
+
queryField
|
|
1271
|
+
});
|
|
1272
|
+
next("image file key is required");
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
try {
|
|
1276
|
+
const imageBuffer = yield this.fileContent(fileKey, "buffer");
|
|
1277
|
+
const ext = import_pathe.default.extname(fileKey).slice(1).toLowerCase();
|
|
1278
|
+
const mimeTypeMap = {
|
|
1279
|
+
jpg: "image/jpeg",
|
|
1280
|
+
jpeg: "image/jpeg",
|
|
1281
|
+
png: "image/png",
|
|
1282
|
+
gif: "image/gif",
|
|
1283
|
+
webp: "image/webp",
|
|
1284
|
+
svg: "image/svg+xml",
|
|
1285
|
+
ico: "image/x-icon"
|
|
1286
|
+
};
|
|
1287
|
+
const contentType = mimeTypeMap[ext] || "application/octet-stream";
|
|
1288
|
+
res.setHeader("Content-Type", contentType);
|
|
1289
|
+
if (cachingAge) res.setHeader("Cache-Control", `public, max-age=${cachingAge}`);
|
|
1290
|
+
res.setHeader("Content-Length", imageBuffer.length);
|
|
1291
|
+
res.status(200).send(imageBuffer);
|
|
1292
|
+
} catch (error) {
|
|
1293
|
+
(_e = this.logger) == null ? void 0 : _e.warn(req.id, "image view fileKey not found", {
|
|
1294
|
+
fileKey,
|
|
1295
|
+
localstack: this.localstack
|
|
1296
|
+
});
|
|
1297
|
+
next(`Failed to retrieve image file: ${error.message}`);
|
|
1298
|
+
}
|
|
1299
|
+
});
|
|
1300
|
+
});
|
|
1301
|
+
// todo: LOCALSTACK SANITY CHECKED - WORKING WELL, DON'T TOUCH!
|
|
1302
|
+
__publicField(this, "getPdfFileViewCtrl", ({
|
|
1303
|
+
fileKey: _fileKey,
|
|
1304
|
+
queryField = "file",
|
|
1305
|
+
cachingAge = 31536e3
|
|
1306
|
+
} = {}) => {
|
|
1307
|
+
return (req, res, next) => __async(this, null, function* () {
|
|
1308
|
+
var _a2, _b;
|
|
1309
|
+
let fileKey = _fileKey || (((_a2 = req.query) == null ? void 0 : _a2[queryField]) ? decodeURIComponent((_b = req.query) == null ? void 0 : _b[queryField]) : void 0);
|
|
1310
|
+
if (!fileKey) {
|
|
1311
|
+
next("pdf file key is required");
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
try {
|
|
1315
|
+
const fileBuffer = yield this.fileContent(fileKey, "buffer");
|
|
1316
|
+
const ext = import_pathe.default.extname(fileKey).slice(1).toLowerCase();
|
|
1317
|
+
const mimeTypeMap = {
|
|
1318
|
+
pdf: "application/pdf",
|
|
1319
|
+
txt: "text/plain",
|
|
1320
|
+
doc: "application/msword",
|
|
1321
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
1322
|
+
xls: "application/vnd.ms-excel",
|
|
1323
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
1324
|
+
ppt: "application/vnd.ms-powerpoint",
|
|
1325
|
+
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
1326
|
+
};
|
|
1327
|
+
const contentType = mimeTypeMap[ext] || "application/octet-stream";
|
|
1328
|
+
res.setHeader("Content-Type", contentType);
|
|
1329
|
+
res.setHeader("Content-Disposition", `inline; filename="${import_pathe.default.basename(fileKey)}"`);
|
|
1330
|
+
res.setHeader("Cache-Control", `public, max-age=${cachingAge}`);
|
|
1331
|
+
res.setHeader("Content-Length", fileBuffer.length);
|
|
1332
|
+
res.status(200).send(fileBuffer);
|
|
1333
|
+
} catch (error) {
|
|
1334
|
+
next(`Failed to retrieve pdf file: ${error.message}`);
|
|
1335
|
+
}
|
|
1336
|
+
});
|
|
1337
|
+
});
|
|
1338
|
+
this.maxUploadFileSizeRestriction = maxUploadFileSizeRestriction;
|
|
1339
|
+
}
|
|
1106
1340
|
streamObjectFile(_0) {
|
|
1107
1341
|
return __async(this, arguments, function* (filePath, {
|
|
1108
1342
|
Range,
|
|
1109
1343
|
checkFileExists = true,
|
|
1110
1344
|
abortSignal
|
|
1111
1345
|
} = {}) {
|
|
1112
|
-
|
|
1346
|
+
let normalizedKey = getNormalizedPath(filePath);
|
|
1113
1347
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1114
1348
|
if (checkFileExists) {
|
|
1115
1349
|
const isExists = yield this.fileExists(normalizedKey);
|
|
1116
1350
|
if (!isExists) return null;
|
|
1117
1351
|
}
|
|
1118
|
-
const command = new
|
|
1352
|
+
const command = new import_client_s35.GetObjectCommand(__spreadValues({
|
|
1119
1353
|
Bucket: this.bucket,
|
|
1120
1354
|
Key: normalizedKey
|
|
1121
1355
|
}, Range ? { Range } : {}));
|
|
1122
1356
|
const response = yield this.execute(command, { abortSignal });
|
|
1123
|
-
if (!response.Body || !(response.Body instanceof
|
|
1357
|
+
if (!response.Body || !(response.Body instanceof import_node_stream2.Readable)) {
|
|
1124
1358
|
throw new Error("Invalid response body: not a Readable stream");
|
|
1125
1359
|
}
|
|
1126
1360
|
return response.Body;
|
|
1127
1361
|
});
|
|
1128
1362
|
}
|
|
1363
|
+
// todo: LOCALSTACK SANITY CHECKED - WORKING WELL, DON'T TOUCH!
|
|
1129
1364
|
streamVideoFile(_0) {
|
|
1130
1365
|
return __async(this, arguments, function* ({
|
|
1131
1366
|
filePath,
|
|
@@ -1133,10 +1368,10 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1133
1368
|
abortSignal
|
|
1134
1369
|
}) {
|
|
1135
1370
|
var _a2;
|
|
1136
|
-
|
|
1371
|
+
let normalizedKey = getNormalizedPath(filePath);
|
|
1137
1372
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1138
1373
|
try {
|
|
1139
|
-
const cmd = new
|
|
1374
|
+
const cmd = new import_client_s35.GetObjectCommand(__spreadValues({
|
|
1140
1375
|
Bucket: this.bucket,
|
|
1141
1376
|
Key: normalizedKey
|
|
1142
1377
|
}, Range ? { Range } : {}));
|
|
@@ -1165,143 +1400,121 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1165
1400
|
}
|
|
1166
1401
|
});
|
|
1167
1402
|
}
|
|
1168
|
-
|
|
1403
|
+
// todo: LOCALSTACK SANITY CHECKED - WORKING WELL, DON'T TOUCH!
|
|
1404
|
+
getStreamVideoFileCtrl(_0) {
|
|
1169
1405
|
return __async(this, arguments, function* ({
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1406
|
+
fileKey,
|
|
1407
|
+
allowedWhitelist,
|
|
1408
|
+
contentType = "video/mp4",
|
|
1409
|
+
streamTimeoutMS = 3e4,
|
|
1410
|
+
bufferMB = 5
|
|
1173
1411
|
}) {
|
|
1174
1412
|
return (req, res, next) => __async(this, null, function* () {
|
|
1175
|
-
var _a2, _b, _c, _d, _e;
|
|
1176
|
-
|
|
1177
|
-
if (!
|
|
1178
|
-
|
|
1413
|
+
var _a2, _b, _c, _d, _e, _f;
|
|
1414
|
+
let normalizedKey = getNormalizedPath(fileKey);
|
|
1415
|
+
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1416
|
+
const isExists = yield this.fileExists(normalizedKey);
|
|
1417
|
+
const fileSize = yield this.sizeOf(normalizedKey);
|
|
1418
|
+
let Range;
|
|
1419
|
+
if (!isExists) {
|
|
1420
|
+
next(Error(`File does not exist: "${normalizedKey}"`));
|
|
1421
|
+
return;
|
|
1179
1422
|
}
|
|
1180
|
-
let filename = _filename || (/* @__PURE__ */ new Date()).toISOString();
|
|
1181
|
-
filename = filename.endsWith(".zip") ? filename : `${filename}.zip`;
|
|
1182
|
-
const abort = new AbortController();
|
|
1183
|
-
const onClose = () => {
|
|
1184
|
-
abort.abort();
|
|
1185
|
-
};
|
|
1186
|
-
req.once("close", onClose);
|
|
1187
1423
|
try {
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
const stream = yield this.streamObjectFile(filePath2, { abortSignal: abort.signal });
|
|
1194
|
-
if (!stream) {
|
|
1195
|
-
(_a3 = this.logger) == null ? void 0 : _a3.warn(this.reqId, "File not found", { filePath: filePath2 });
|
|
1196
|
-
return null;
|
|
1197
|
-
}
|
|
1198
|
-
const chunks = [];
|
|
1199
|
-
try {
|
|
1200
|
-
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield iter.next()).done; more = false) {
|
|
1201
|
-
const chunk = temp.value;
|
|
1202
|
-
if (abort.signal.aborted) {
|
|
1203
|
-
stream.destroy();
|
|
1204
|
-
return null;
|
|
1205
|
-
}
|
|
1206
|
-
chunks.push(import_buffer.Buffer.from(chunk));
|
|
1207
|
-
}
|
|
1208
|
-
} catch (temp) {
|
|
1209
|
-
error = [temp];
|
|
1210
|
-
} finally {
|
|
1211
|
-
try {
|
|
1212
|
-
more && (temp = iter.return) && (yield temp.call(iter));
|
|
1213
|
-
} finally {
|
|
1214
|
-
if (error)
|
|
1215
|
-
throw error[0];
|
|
1216
|
-
}
|
|
1217
|
-
}
|
|
1218
|
-
const buffer = import_buffer.Buffer.concat(chunks);
|
|
1219
|
-
const fileName = filePath2.split("/").pop() || filePath2;
|
|
1220
|
-
(_b2 = this.logger) == null ? void 0 : _b2.debug(this.reqId, "File downloaded", {
|
|
1221
|
-
filePath: filePath2,
|
|
1222
|
-
sizeMB: (buffer.length / (1024 * 1024)).toFixed(2)
|
|
1223
|
-
});
|
|
1224
|
-
return { buffer, name: fileName, path: filePath2 };
|
|
1225
|
-
} catch (error2) {
|
|
1226
|
-
(_c2 = this.logger) == null ? void 0 : _c2.warn(this.reqId, "Failed to download file", { filePath: filePath2, error: error2 });
|
|
1227
|
-
return null;
|
|
1228
|
-
}
|
|
1229
|
-
}));
|
|
1230
|
-
const fileBuffers = (yield Promise.all(downloadPromises)).filter(Boolean);
|
|
1231
|
-
if (abort.signal.aborted || fileBuffers.length === 0) {
|
|
1232
|
-
req.off("close", onClose);
|
|
1233
|
-
if (fileBuffers.length === 0) {
|
|
1234
|
-
next(new Error("No files available to zip"));
|
|
1235
|
-
}
|
|
1236
|
-
return;
|
|
1237
|
-
}
|
|
1238
|
-
(_b = this.logger) == null ? void 0 : _b.info(this.reqId, "All files downloaded, measuring zip size...", {
|
|
1239
|
-
fileCount: fileBuffers.length,
|
|
1240
|
-
totalSizeMB: (fileBuffers.reduce((sum, f) => sum + f.buffer.length, 0) / (1024 * 1024)).toFixed(2)
|
|
1241
|
-
});
|
|
1242
|
-
const measureArchive = (0, import_archiver.default)("zip", { zlib: { level: compressionLevel } });
|
|
1243
|
-
let actualZipSize = 0;
|
|
1244
|
-
measureArchive.on("data", (chunk) => {
|
|
1245
|
-
actualZipSize += chunk.length;
|
|
1246
|
-
});
|
|
1247
|
-
for (const file of fileBuffers) {
|
|
1248
|
-
if (abort.signal.aborted) break;
|
|
1249
|
-
measureArchive.append(file.buffer, { name: file.name });
|
|
1424
|
+
if (req.method === "HEAD") {
|
|
1425
|
+
res.setHeader("Content-Type", contentType);
|
|
1426
|
+
res.setHeader("Accept-Ranges", "bytes");
|
|
1427
|
+
if (fileSize) res.setHeader("Content-Length", String(fileSize));
|
|
1428
|
+
return res.status(200).end();
|
|
1250
1429
|
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1430
|
+
const bufferSize = bufferMB;
|
|
1431
|
+
const CHUNK_SIZE = __pow(10, 6) * bufferSize;
|
|
1432
|
+
const rangeValues = parseRangeHeader(req.headers.range, fileSize, CHUNK_SIZE);
|
|
1433
|
+
let [start, end] = rangeValues || [];
|
|
1434
|
+
if (!rangeValues || start < 0 || start >= fileSize || end < 0 || end >= fileSize || start > end) {
|
|
1435
|
+
res.status(416).send("Requested Range Not Satisfiable");
|
|
1254
1436
|
return;
|
|
1255
1437
|
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
});
|
|
1260
|
-
|
|
1261
|
-
res.setHeader("Content-Type", "
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1438
|
+
res.statusCode = 206;
|
|
1439
|
+
const chunkLength = end - start + 1;
|
|
1440
|
+
res.setHeader("Content-Length", chunkLength);
|
|
1441
|
+
res.setHeader("Content-Range", `bytes ${start}-${end}/${fileSize}`);
|
|
1442
|
+
res.setHeader("Accept-Ranges", "bytes");
|
|
1443
|
+
res.setHeader("Content-Type", "video/mp4");
|
|
1444
|
+
Range = `bytes=${start}-${end}`;
|
|
1445
|
+
} catch (error) {
|
|
1446
|
+
next(error);
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1449
|
+
const abort = new AbortController();
|
|
1450
|
+
const onClose = () => abort.abort();
|
|
1451
|
+
req.once("close", onClose);
|
|
1452
|
+
try {
|
|
1453
|
+
const result = yield this.streamVideoFile({
|
|
1454
|
+
filePath: normalizedKey,
|
|
1455
|
+
Range,
|
|
1456
|
+
abortSignal: abort.signal
|
|
1272
1457
|
});
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1458
|
+
const { body, meta } = result;
|
|
1459
|
+
const origin = Array.isArray(allowedWhitelist) ? allowedWhitelist.includes((_a2 = req.headers.origin) != null ? _a2 : "") ? req.headers.origin : void 0 : allowedWhitelist;
|
|
1460
|
+
if (origin) {
|
|
1461
|
+
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
1462
|
+
res.setHeader("Vary", "Origin");
|
|
1277
1463
|
}
|
|
1278
|
-
|
|
1279
|
-
(
|
|
1280
|
-
|
|
1281
|
-
|
|
1464
|
+
const finalContentType = contentType.startsWith("video/") ? contentType : `video/${contentType}`;
|
|
1465
|
+
res.setHeader("Content-Type", (_b = meta.contentType) != null ? _b : finalContentType);
|
|
1466
|
+
res.setHeader("Accept-Ranges", (_c = meta.acceptRanges) != null ? _c : "bytes");
|
|
1467
|
+
if (Range && meta.contentRange) {
|
|
1468
|
+
res.status(206);
|
|
1469
|
+
res.setHeader("Content-Range", meta.contentRange);
|
|
1470
|
+
if (typeof meta.contentLength === "number") {
|
|
1471
|
+
res.setHeader("Content-Length", String(meta.contentLength));
|
|
1472
|
+
}
|
|
1473
|
+
} else if (fileSize) {
|
|
1474
|
+
res.setHeader("Content-Length", String(fileSize));
|
|
1475
|
+
}
|
|
1476
|
+
if (meta.etag) res.setHeader("ETag", meta.etag);
|
|
1477
|
+
if (meta.lastModified) res.setHeader("Last-Modified", meta.lastModified.toUTCString());
|
|
1478
|
+
const timeout = setTimeout(() => {
|
|
1479
|
+
abort.abort();
|
|
1480
|
+
if (!res.headersSent) res.status(504);
|
|
1481
|
+
res.end();
|
|
1482
|
+
}, streamTimeoutMS);
|
|
1483
|
+
res.once("close", () => {
|
|
1484
|
+
var _a3;
|
|
1485
|
+
clearTimeout(timeout);
|
|
1486
|
+
(_a3 = body.destroy) == null ? void 0 : _a3.call(body);
|
|
1487
|
+
req.off("close", onClose);
|
|
1282
1488
|
});
|
|
1283
|
-
|
|
1489
|
+
yield pump(body, res);
|
|
1490
|
+
clearTimeout(timeout);
|
|
1284
1491
|
} catch (error) {
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
if (isBenignError) {
|
|
1492
|
+
const isBenignStreamError = (error == null ? void 0 : error.code) === "ERR_STREAM_PREMATURE_CLOSE" || (error == null ? void 0 : error.name) === "AbortError" || (error == null ? void 0 : error.code) === "ECONNRESET";
|
|
1493
|
+
if (isBenignStreamError) {
|
|
1288
1494
|
return;
|
|
1289
1495
|
}
|
|
1290
1496
|
if (!res.headersSent) {
|
|
1291
|
-
(
|
|
1497
|
+
(_f = this.logger) == null ? void 0 : _f.warn(req.id, "caught exception in stream controller", {
|
|
1498
|
+
error: (_d = error == null ? void 0 : error.message) != null ? _d : String(error),
|
|
1499
|
+
key: fileKey,
|
|
1500
|
+
url: req.originalUrl,
|
|
1501
|
+
userId: (_e = req.user) == null ? void 0 : _e._id
|
|
1502
|
+
});
|
|
1292
1503
|
next(error);
|
|
1293
|
-
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
if (!res.writableEnded) {
|
|
1294
1507
|
try {
|
|
1295
1508
|
res.end();
|
|
1296
1509
|
} catch (e) {
|
|
1297
1510
|
}
|
|
1298
1511
|
}
|
|
1299
|
-
|
|
1300
|
-
req.off("close", onClose);
|
|
1512
|
+
return;
|
|
1301
1513
|
}
|
|
1302
1514
|
});
|
|
1303
1515
|
});
|
|
1304
1516
|
}
|
|
1517
|
+
// todo: LOCALSTACK SANITY CHECKED - WORKING WELL, DON'T TOUCH!
|
|
1305
1518
|
getStreamFileCtrl(_0) {
|
|
1306
1519
|
return __async(this, arguments, function* ({ filePath, filename }) {
|
|
1307
1520
|
return (req, res, next) => __async(this, null, function* () {
|
|
@@ -1314,7 +1527,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1314
1527
|
(_a3 = stream == null ? void 0 : stream.destroy) == null ? void 0 : _a3.call(stream);
|
|
1315
1528
|
};
|
|
1316
1529
|
req.once("close", onClose);
|
|
1317
|
-
|
|
1530
|
+
let normalizedKey = getNormalizedPath(filePath);
|
|
1318
1531
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1319
1532
|
try {
|
|
1320
1533
|
const isExists = yield this.fileExists(normalizedKey);
|
|
@@ -1376,115 +1589,140 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1376
1589
|
});
|
|
1377
1590
|
});
|
|
1378
1591
|
}
|
|
1379
|
-
|
|
1592
|
+
// todo: LOCALSTACK SANITY CHECKED - WORKING WELL, DON'T TOUCH!
|
|
1593
|
+
getStreamZipFileCtr(_0) {
|
|
1380
1594
|
return __async(this, arguments, function* ({
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
streamTimeoutMS = 3e4,
|
|
1385
|
-
bufferMB = 5
|
|
1595
|
+
filePath,
|
|
1596
|
+
filename: _filename,
|
|
1597
|
+
compressionLevel = 5
|
|
1386
1598
|
}) {
|
|
1387
1599
|
return (req, res, next) => __async(this, null, function* () {
|
|
1388
|
-
var _a2, _b, _c, _d, _e
|
|
1389
|
-
const
|
|
1390
|
-
if (!
|
|
1391
|
-
|
|
1392
|
-
const fileSize = yield this.sizeOf(normalizedKey);
|
|
1393
|
-
let Range;
|
|
1394
|
-
if (!isExists) {
|
|
1395
|
-
next(Error(`File does not exist: "${normalizedKey}"`));
|
|
1396
|
-
return;
|
|
1397
|
-
}
|
|
1398
|
-
try {
|
|
1399
|
-
if (req.method === "HEAD") {
|
|
1400
|
-
res.setHeader("Content-Type", contentType);
|
|
1401
|
-
res.setHeader("Accept-Ranges", "bytes");
|
|
1402
|
-
if (fileSize) res.setHeader("Content-Length", String(fileSize));
|
|
1403
|
-
return res.status(200).end();
|
|
1404
|
-
}
|
|
1405
|
-
const bufferSize = bufferMB;
|
|
1406
|
-
const CHUNK_SIZE = __pow(10, 6) * bufferSize;
|
|
1407
|
-
const rangeValues = parseRangeHeader(req.headers.range, fileSize, CHUNK_SIZE);
|
|
1408
|
-
let [start, end] = rangeValues || [];
|
|
1409
|
-
if (!rangeValues || start < 0 || start >= fileSize || end < 0 || end >= fileSize || start > end) {
|
|
1410
|
-
res.status(416).send("Requested Range Not Satisfiable");
|
|
1411
|
-
return;
|
|
1412
|
-
}
|
|
1413
|
-
res.statusCode = 206;
|
|
1414
|
-
const chunkLength = end - start + 1;
|
|
1415
|
-
res.setHeader("Content-Length", chunkLength);
|
|
1416
|
-
res.setHeader("Content-Range", `bytes ${start}-${end}/${fileSize}`);
|
|
1417
|
-
res.setHeader("Accept-Ranges", "bytes");
|
|
1418
|
-
res.setHeader("Content-Type", "video/mp4");
|
|
1419
|
-
Range = `bytes=${start}-${end}`;
|
|
1420
|
-
} catch (error) {
|
|
1421
|
-
next(error);
|
|
1422
|
-
return;
|
|
1600
|
+
var _a2, _b, _c, _d, _e;
|
|
1601
|
+
const filePaths = [].concat(filePath).map((filePath2) => getNormalizedPath(filePath2)).filter((v) => v && v !== "/");
|
|
1602
|
+
if (!filePaths.length) {
|
|
1603
|
+
throw new Error("No file keys provided");
|
|
1423
1604
|
}
|
|
1605
|
+
let filename = _filename || (/* @__PURE__ */ new Date()).toISOString();
|
|
1606
|
+
filename = filename.endsWith(".zip") ? filename : `${filename}.zip`;
|
|
1424
1607
|
const abort = new AbortController();
|
|
1425
|
-
const onClose = () =>
|
|
1608
|
+
const onClose = () => {
|
|
1609
|
+
abort.abort();
|
|
1610
|
+
};
|
|
1426
1611
|
req.once("close", onClose);
|
|
1427
1612
|
try {
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1613
|
+
(_a2 = this.logger) == null ? void 0 : _a2.info(this.reqId, "Starting parallel file download...", { fileCount: filePaths.length });
|
|
1614
|
+
const downloadPromises = filePaths.map((filePath2) => __async(this, null, function* () {
|
|
1615
|
+
var _a3, _b2, _c2;
|
|
1616
|
+
try {
|
|
1617
|
+
if (abort.signal.aborted) return null;
|
|
1618
|
+
const stream = yield this.streamObjectFile(filePath2, { abortSignal: abort.signal });
|
|
1619
|
+
if (!stream) {
|
|
1620
|
+
(_a3 = this.logger) == null ? void 0 : _a3.warn(this.reqId, "File not found", { filePath: filePath2 });
|
|
1621
|
+
return null;
|
|
1622
|
+
}
|
|
1623
|
+
const chunks = [];
|
|
1624
|
+
try {
|
|
1625
|
+
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield iter.next()).done; more = false) {
|
|
1626
|
+
const chunk = temp.value;
|
|
1627
|
+
if (abort.signal.aborted) {
|
|
1628
|
+
stream.destroy();
|
|
1629
|
+
return null;
|
|
1630
|
+
}
|
|
1631
|
+
chunks.push(import_buffer2.Buffer.from(chunk));
|
|
1632
|
+
}
|
|
1633
|
+
} catch (temp) {
|
|
1634
|
+
error = [temp];
|
|
1635
|
+
} finally {
|
|
1636
|
+
try {
|
|
1637
|
+
more && (temp = iter.return) && (yield temp.call(iter));
|
|
1638
|
+
} finally {
|
|
1639
|
+
if (error)
|
|
1640
|
+
throw error[0];
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
const buffer = import_buffer2.Buffer.concat(chunks);
|
|
1644
|
+
const fileName = filePath2.split("/").pop() || filePath2;
|
|
1645
|
+
(_b2 = this.logger) == null ? void 0 : _b2.debug(this.reqId, "File downloaded", {
|
|
1646
|
+
filePath: filePath2,
|
|
1647
|
+
sizeMB: (buffer.length / (1024 * 1024)).toFixed(2)
|
|
1648
|
+
});
|
|
1649
|
+
return { buffer, name: fileName, path: filePath2 };
|
|
1650
|
+
} catch (error2) {
|
|
1651
|
+
(_c2 = this.logger) == null ? void 0 : _c2.warn(this.reqId, "Failed to download file", { filePath: filePath2, error: error2 });
|
|
1652
|
+
return null;
|
|
1653
|
+
}
|
|
1654
|
+
}));
|
|
1655
|
+
const fileBuffers = (yield Promise.all(downloadPromises)).filter(Boolean);
|
|
1656
|
+
if (abort.signal.aborted || fileBuffers.length === 0) {
|
|
1657
|
+
req.off("close", onClose);
|
|
1658
|
+
if (fileBuffers.length === 0) {
|
|
1659
|
+
next(new Error("No files available to zip"));
|
|
1660
|
+
}
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
(_b = this.logger) == null ? void 0 : _b.info(this.reqId, "All files downloaded, measuring zip size...", {
|
|
1664
|
+
fileCount: fileBuffers.length,
|
|
1665
|
+
totalSizeMB: (fileBuffers.reduce((sum, f) => sum + f.buffer.length, 0) / (1024 * 1024)).toFixed(2)
|
|
1432
1666
|
});
|
|
1433
|
-
const
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1667
|
+
const measureArchive = (0, import_archiver.default)("zip", { zlib: { level: compressionLevel } });
|
|
1668
|
+
let actualZipSize = 0;
|
|
1669
|
+
measureArchive.on("data", (chunk) => {
|
|
1670
|
+
actualZipSize += chunk.length;
|
|
1671
|
+
});
|
|
1672
|
+
for (const file of fileBuffers) {
|
|
1673
|
+
if (abort.signal.aborted) break;
|
|
1674
|
+
measureArchive.append(file.buffer, { name: file.name });
|
|
1438
1675
|
}
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
res.status(206);
|
|
1444
|
-
res.setHeader("Content-Range", meta.contentRange);
|
|
1445
|
-
if (typeof meta.contentLength === "number") {
|
|
1446
|
-
res.setHeader("Content-Length", String(meta.contentLength));
|
|
1447
|
-
}
|
|
1448
|
-
} else if (fileSize) {
|
|
1449
|
-
res.setHeader("Content-Length", String(fileSize));
|
|
1676
|
+
yield measureArchive.finalize();
|
|
1677
|
+
if (abort.signal.aborted) {
|
|
1678
|
+
req.off("close", onClose);
|
|
1679
|
+
return;
|
|
1450
1680
|
}
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
res.
|
|
1681
|
+
(_c = this.logger) == null ? void 0 : _c.info(this.reqId, "Zip size calculated", {
|
|
1682
|
+
actualZipSize,
|
|
1683
|
+
sizeMB: (actualZipSize / (1024 * 1024)).toFixed(2)
|
|
1684
|
+
});
|
|
1685
|
+
const actualArchive = (0, import_archiver.default)("zip", { zlib: { level: compressionLevel } });
|
|
1686
|
+
res.setHeader("Content-Type", "application/zip");
|
|
1687
|
+
res.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
|
|
1688
|
+
res.setHeader("Content-Length", String(actualZipSize));
|
|
1689
|
+
res.setHeader("Access-Control-Expose-Headers", "Content-Type, Content-Disposition, Content-Length");
|
|
1690
|
+
actualArchive.on("error", (err) => {
|
|
1459
1691
|
var _a3;
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1692
|
+
(_a3 = this.logger) == null ? void 0 : _a3.error(this.reqId, "Archive error", { error: err });
|
|
1693
|
+
abort.abort();
|
|
1694
|
+
if (!res.headersSent) {
|
|
1695
|
+
next(err);
|
|
1696
|
+
}
|
|
1463
1697
|
});
|
|
1464
|
-
|
|
1465
|
-
|
|
1698
|
+
actualArchive.pipe(res);
|
|
1699
|
+
for (const file of fileBuffers) {
|
|
1700
|
+
if (abort.signal.aborted) break;
|
|
1701
|
+
actualArchive.append(file.buffer, { name: file.name });
|
|
1702
|
+
}
|
|
1703
|
+
yield actualArchive.finalize();
|
|
1704
|
+
(_d = this.logger) == null ? void 0 : _d.info(this.reqId, "Zip download completed", {
|
|
1705
|
+
fileCount: fileBuffers.length,
|
|
1706
|
+
totalSize: actualZipSize
|
|
1707
|
+
});
|
|
1708
|
+
req.off("close", onClose);
|
|
1466
1709
|
} catch (error) {
|
|
1467
|
-
|
|
1468
|
-
|
|
1710
|
+
abort.abort();
|
|
1711
|
+
const isBenignError = (error == null ? void 0 : error.code) === "ERR_STREAM_PREMATURE_CLOSE" || (error == null ? void 0 : error.name) === "AbortError" || (error == null ? void 0 : error.code) === "ECONNRESET";
|
|
1712
|
+
if (isBenignError) {
|
|
1469
1713
|
return;
|
|
1470
1714
|
}
|
|
1471
1715
|
if (!res.headersSent) {
|
|
1472
|
-
(
|
|
1473
|
-
error: (_d = error == null ? void 0 : error.message) != null ? _d : String(error),
|
|
1474
|
-
key: fileKey,
|
|
1475
|
-
url: req.originalUrl,
|
|
1476
|
-
userId: (_e = req.user) == null ? void 0 : _e._id
|
|
1477
|
-
});
|
|
1716
|
+
(_e = this.logger) == null ? void 0 : _e.error(this.reqId, "Failed to create zip archive", { error });
|
|
1478
1717
|
next(error);
|
|
1479
|
-
|
|
1480
|
-
}
|
|
1481
|
-
if (!res.writableEnded) {
|
|
1718
|
+
} else if (!res.writableEnded) {
|
|
1482
1719
|
try {
|
|
1483
1720
|
res.end();
|
|
1484
1721
|
} catch (e) {
|
|
1485
1722
|
}
|
|
1486
1723
|
}
|
|
1487
|
-
|
|
1724
|
+
} finally {
|
|
1725
|
+
req.off("close", onClose);
|
|
1488
1726
|
}
|
|
1489
1727
|
});
|
|
1490
1728
|
});
|
|
@@ -1502,19 +1740,6 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1502
1740
|
return cb(new Error(errorMsg));
|
|
1503
1741
|
};
|
|
1504
1742
|
}
|
|
1505
|
-
getFileSize(maxFileSize) {
|
|
1506
|
-
var _a2;
|
|
1507
|
-
const fileSizeUnitValue = maxFileSize != null ? maxFileSize : this.maxUploadFileSizeRestriction;
|
|
1508
|
-
const fileSize = typeof fileSizeUnitValue === "number" ? fileSizeUnitValue : (0, import_bytes.default)(fileSizeUnitValue);
|
|
1509
|
-
if (!fileSize) {
|
|
1510
|
-
(_a2 = this.logger) == null ? void 0 : _a2.warn(this.reqId, "Failed to convert fileSize restriction, proceeding without limit", {
|
|
1511
|
-
maxFileSize,
|
|
1512
|
-
maxUploadFileSizeRestriction: this.maxUploadFileSizeRestriction
|
|
1513
|
-
});
|
|
1514
|
-
return void 0;
|
|
1515
|
-
}
|
|
1516
|
-
return fileSize;
|
|
1517
|
-
}
|
|
1518
1743
|
getUploadFileMW(directoryPath, {
|
|
1519
1744
|
acl = "private" /* private */,
|
|
1520
1745
|
maxFileSize,
|
|
@@ -1526,10 +1751,10 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1526
1751
|
let normalizedPath = getNormalizedPath(directoryPath);
|
|
1527
1752
|
if (normalizedPath !== "/" && directoryPath !== "" && directoryPath !== void 0) normalizedPath += "/";
|
|
1528
1753
|
else normalizedPath = "";
|
|
1529
|
-
const fileSize =
|
|
1754
|
+
const fileSize = getFileSize(maxFileSize, this.maxUploadFileSizeRestriction);
|
|
1530
1755
|
const fileTypes = [].concat(fileType);
|
|
1531
1756
|
const fileExts = [].concat(fileExt);
|
|
1532
|
-
const fileFilter = (fileTypes == null ? void 0 : fileTypes.length) || (fileExts == null ? void 0 : fileExts.length) ?
|
|
1757
|
+
const fileFilter = (fileTypes == null ? void 0 : fileTypes.length) || (fileExts == null ? void 0 : fileExts.length) ? _S3Stream.fileFilter(fileTypes, fileExts) : void 0;
|
|
1533
1758
|
return (0, import_multer.default)({
|
|
1534
1759
|
fileFilter,
|
|
1535
1760
|
limits: __spreadValues({}, fileSize && { fileSize }),
|
|
@@ -1566,14 +1791,19 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1566
1791
|
* Middleware for uploading a single file
|
|
1567
1792
|
* Adds the uploaded file info to req.s3File
|
|
1568
1793
|
*/
|
|
1569
|
-
uploadSingleFile(fieldName,
|
|
1570
|
-
|
|
1794
|
+
uploadSingleFile(fieldName, directoryPath, options = {}) {
|
|
1795
|
+
var _a2;
|
|
1796
|
+
let normalizedPath = getNormalizedPath(directoryPath);
|
|
1797
|
+
if (normalizedPath !== "/" && directoryPath !== "" && directoryPath !== void 0) normalizedPath += "/";
|
|
1798
|
+
else normalizedPath = "";
|
|
1799
|
+
(_a2 = this.logger) == null ? void 0 : _a2.debug(null, "####### uploadSingleFile", { directoryPath, normalizedPath, fieldName });
|
|
1800
|
+
const upload = this.getUploadFileMW(normalizedPath, options);
|
|
1571
1801
|
return (req, res, next) => {
|
|
1572
1802
|
const mw = upload.single(fieldName);
|
|
1573
1803
|
mw(req, res, (err) => {
|
|
1574
|
-
var
|
|
1804
|
+
var _a3, _b;
|
|
1575
1805
|
if (err) {
|
|
1576
|
-
(
|
|
1806
|
+
(_a3 = this.logger) == null ? void 0 : _a3.error(this.reqId, "Single file upload error", { fieldName, error: err.message });
|
|
1577
1807
|
return next(err);
|
|
1578
1808
|
}
|
|
1579
1809
|
if (req.file) {
|
|
@@ -1593,8 +1823,11 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1593
1823
|
* Middleware for uploading multiple files with the same field name
|
|
1594
1824
|
* Adds the uploaded files info to req.s3Files
|
|
1595
1825
|
*/
|
|
1596
|
-
uploadMultipleFiles(fieldName,
|
|
1597
|
-
|
|
1826
|
+
uploadMultipleFiles(fieldName, directoryPath, options = {}) {
|
|
1827
|
+
let normalizedPath = getNormalizedPath(directoryPath);
|
|
1828
|
+
if (normalizedPath !== "/" && directoryPath !== "" && directoryPath !== void 0) normalizedPath += "/";
|
|
1829
|
+
else normalizedPath = "";
|
|
1830
|
+
const upload = this.getUploadFileMW(normalizedPath, options);
|
|
1598
1831
|
return (req, res, next) => {
|
|
1599
1832
|
const mw = upload.array(fieldName, options.maxFilesCount || void 0);
|
|
1600
1833
|
mw(req, res, (err) => {
|
|
@@ -1615,49 +1848,15 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1615
1848
|
});
|
|
1616
1849
|
};
|
|
1617
1850
|
}
|
|
1618
|
-
/**
|
|
1619
|
-
* Middleware for uploading multiple files with different field names
|
|
1620
|
-
* Adds the uploaded files info to req.s3FilesByField
|
|
1621
|
-
*/
|
|
1622
|
-
uploadFieldsFiles(fields) {
|
|
1623
|
-
const fieldConfigs = fields.map((field) => {
|
|
1624
|
-
const upload = this.getUploadFileMW(field.directory, field.options || {});
|
|
1625
|
-
return {
|
|
1626
|
-
name: field.name,
|
|
1627
|
-
maxCount: field.maxCount || 1,
|
|
1628
|
-
upload,
|
|
1629
|
-
directory: field.directory
|
|
1630
|
-
};
|
|
1631
|
-
});
|
|
1632
|
-
return (req, res, next) => __async(this, null, function* () {
|
|
1633
|
-
const multerFields = fieldConfigs.map((f) => ({ name: f.name, maxCount: f.maxCount }));
|
|
1634
|
-
const upload = this.getUploadFileMW(fieldConfigs[0].directory);
|
|
1635
|
-
const mw = upload.fields(multerFields);
|
|
1636
|
-
mw(req, res, (err) => {
|
|
1637
|
-
var _a2, _b;
|
|
1638
|
-
if (err) {
|
|
1639
|
-
(_a2 = this.logger) == null ? void 0 : _a2.error(this.reqId, "Fields upload error", { error: err.message });
|
|
1640
|
-
return next(err);
|
|
1641
|
-
}
|
|
1642
|
-
if (req.files && typeof req.files === "object" && !Array.isArray(req.files)) {
|
|
1643
|
-
req.s3FilesByField = req.files;
|
|
1644
|
-
const uploadSummary = Object.entries(req.s3FilesByField).map(([field, files]) => ({
|
|
1645
|
-
field,
|
|
1646
|
-
count: files.length,
|
|
1647
|
-
keys: files.map((f) => f.key)
|
|
1648
|
-
}));
|
|
1649
|
-
(_b = this.logger) == null ? void 0 : _b.info(this.reqId, "Fields uploaded successfully", { uploadSummary });
|
|
1650
|
-
}
|
|
1651
|
-
next();
|
|
1652
|
-
});
|
|
1653
|
-
});
|
|
1654
|
-
}
|
|
1655
1851
|
/**
|
|
1656
1852
|
* Middleware for uploading any files (mixed field names)
|
|
1657
1853
|
* Adds the uploaded files info to req.s3AllFiles
|
|
1658
1854
|
*/
|
|
1659
|
-
uploadAnyFiles(
|
|
1660
|
-
|
|
1855
|
+
uploadAnyFiles(directoryPath, maxCount, options = {}) {
|
|
1856
|
+
let normalizedPath = getNormalizedPath(directoryPath);
|
|
1857
|
+
if (normalizedPath !== "/" && normalizedPath !== "" && directoryPath !== void 0) normalizedPath += "/";
|
|
1858
|
+
else normalizedPath = "";
|
|
1859
|
+
const upload = this.getUploadFileMW(normalizedPath, options);
|
|
1661
1860
|
return (req, res, next) => {
|
|
1662
1861
|
const anyUpload = maxCount ? upload.any() : upload.any();
|
|
1663
1862
|
anyUpload(req, res, (err) => {
|
|
@@ -1680,6 +1879,159 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1680
1879
|
});
|
|
1681
1880
|
};
|
|
1682
1881
|
}
|
|
1882
|
+
/**
|
|
1883
|
+
* Middleware for uploading multiple files with different field names
|
|
1884
|
+
* Adds the uploaded files info to req.s3FilesByField
|
|
1885
|
+
*/
|
|
1886
|
+
// uploadFieldsFiles(
|
|
1887
|
+
// fields: Array<{ name: string; directory: string; maxCount?: number; options?: S3UploadOptions }>
|
|
1888
|
+
// ): RequestHandler {
|
|
1889
|
+
// // Create separate multer instances for each field (since each might have different options)
|
|
1890
|
+
// const fieldConfigs = fields.map((field) => {
|
|
1891
|
+
// const upload = this.getUploadFileMW(field.directory, field.options || {});
|
|
1892
|
+
//
|
|
1893
|
+
// return {
|
|
1894
|
+
// name: getNormalizedPath(field.name),
|
|
1895
|
+
// directory: getNormalizedPath(field.directory),
|
|
1896
|
+
// maxCount: field.maxCount || 1,
|
|
1897
|
+
// upload,
|
|
1898
|
+
// };
|
|
1899
|
+
// });
|
|
1900
|
+
//
|
|
1901
|
+
// return async (
|
|
1902
|
+
// req: Request & { s3FilesByField?: Record<string, UploadedS3File[]> } & any,
|
|
1903
|
+
// res: Response,
|
|
1904
|
+
// next: NextFunction & any
|
|
1905
|
+
// ) => {
|
|
1906
|
+
// // We'll use the first upload instance but with fields configuration
|
|
1907
|
+
// const multerFields = fieldConfigs.map((f) => ({ name: f.name, maxCount: f.maxCount }));
|
|
1908
|
+
// const upload = this.getUploadFileMW(fieldConfigs[0].directory);
|
|
1909
|
+
//
|
|
1910
|
+
// const mw: RequestHandler & any = upload.fields(multerFields);
|
|
1911
|
+
// mw(req, res, (err: any) => {
|
|
1912
|
+
// if (err) {
|
|
1913
|
+
// this.logger?.error(this.reqId, 'Fields upload error', { error: err.message });
|
|
1914
|
+
// return next(err);
|
|
1915
|
+
// }
|
|
1916
|
+
//
|
|
1917
|
+
// if (req.files && typeof req.files === 'object' && !Array.isArray(req.files)) {
|
|
1918
|
+
// req.s3FilesByField = req.files as Record<string, UploadedS3File[]>;
|
|
1919
|
+
//
|
|
1920
|
+
// const uploadSummary = Object.entries(req.s3FilesByField).map(([field, files]: any) => ({
|
|
1921
|
+
// field,
|
|
1922
|
+
// count: files.length,
|
|
1923
|
+
// keys: files.map((f: any) => f.key),
|
|
1924
|
+
// }));
|
|
1925
|
+
//
|
|
1926
|
+
// this.logger?.info(this.reqId, 'Fields uploaded successfully', { uploadSummary });
|
|
1927
|
+
// }
|
|
1928
|
+
//
|
|
1929
|
+
// next();
|
|
1930
|
+
// });
|
|
1931
|
+
// };
|
|
1932
|
+
// }
|
|
1933
|
+
};
|
|
1934
|
+
|
|
1935
|
+
// src/aws/s3/s3-util.ts
|
|
1936
|
+
var S3Util = class extends S3Stream {
|
|
1937
|
+
constructor(props) {
|
|
1938
|
+
super(props);
|
|
1939
|
+
}
|
|
1940
|
+
};
|
|
1941
|
+
|
|
1942
|
+
// src/aws/s3/s3-util.localstack.ts
|
|
1943
|
+
var S3LocalstackUtil = class extends S3Util {
|
|
1944
|
+
constructor(props) {
|
|
1945
|
+
super(__spreadProps(__spreadValues({}, props), { localstack: true }));
|
|
1946
|
+
}
|
|
1947
|
+
// todo: checked!
|
|
1948
|
+
directoryList(directoryPath) {
|
|
1949
|
+
return __async(this, null, function* () {
|
|
1950
|
+
var _a2;
|
|
1951
|
+
let normalizedPath = getNormalizedPath(directoryPath);
|
|
1952
|
+
if (normalizedPath !== "/" && directoryPath !== "" && directoryPath !== void 0) normalizedPath += "/";
|
|
1953
|
+
else normalizedPath = "";
|
|
1954
|
+
let result;
|
|
1955
|
+
result = yield this.execute(
|
|
1956
|
+
new import_client_s36.ListObjectsV2Command({
|
|
1957
|
+
Bucket: this.bucket,
|
|
1958
|
+
Prefix: normalizedPath,
|
|
1959
|
+
Delimiter: "/"
|
|
1960
|
+
})
|
|
1961
|
+
);
|
|
1962
|
+
(_a2 = this.logger) == null ? void 0 : _a2.debug(null, "#### directoryList", {
|
|
1963
|
+
normalizedPath,
|
|
1964
|
+
CommonPrefixes: result.CommonPrefixes,
|
|
1965
|
+
ContentFile: result.Contents
|
|
1966
|
+
});
|
|
1967
|
+
const directories = (result.CommonPrefixes || []).map((prefix) => prefix.Prefix).map((prefix) => {
|
|
1968
|
+
const relativePath = prefix.replace(normalizedPath, "");
|
|
1969
|
+
const dir = relativePath.replace(/\/$/, "");
|
|
1970
|
+
return dir;
|
|
1971
|
+
}).filter((dir) => dir);
|
|
1972
|
+
const files = (result.Contents || []).filter((content) => {
|
|
1973
|
+
var _a3;
|
|
1974
|
+
return content.Key !== normalizedPath && !((_a3 = content.Key) == null ? void 0 : _a3.endsWith("/"));
|
|
1975
|
+
}).map((content) => __spreadProps(__spreadValues({}, content), {
|
|
1976
|
+
Name: content.Key.replace(normalizedPath, "") || content.Key,
|
|
1977
|
+
Location: `${this.link}${content.Key.replace(/^\//, "")}`,
|
|
1978
|
+
LastModified: new Date(content.LastModified)
|
|
1979
|
+
}));
|
|
1980
|
+
return { directories, files };
|
|
1981
|
+
});
|
|
1982
|
+
}
|
|
1983
|
+
// todo: checked!
|
|
1984
|
+
directoryListPaginated(_0) {
|
|
1985
|
+
return __async(this, arguments, function* (directoryPath, {
|
|
1986
|
+
pageSize = 100,
|
|
1987
|
+
pageNumber = 0
|
|
1988
|
+
// 0-based: page 0 = items 0-99, page 1 = items 100-199, page 2 = items 200-299
|
|
1989
|
+
} = {}) {
|
|
1990
|
+
let normalizedPath = getNormalizedPath(directoryPath);
|
|
1991
|
+
if (normalizedPath !== "/" && directoryPath !== "" && directoryPath !== void 0) normalizedPath += "/";
|
|
1992
|
+
else normalizedPath = this.localstack ? "" : "/";
|
|
1993
|
+
let continuationToken = void 0;
|
|
1994
|
+
let currentPage = 0;
|
|
1995
|
+
let allDirectories = [];
|
|
1996
|
+
let allFiles = [];
|
|
1997
|
+
while (currentPage <= pageNumber) {
|
|
1998
|
+
let result;
|
|
1999
|
+
result = yield this.execute(
|
|
2000
|
+
new import_client_s36.ListObjectsV2Command({
|
|
2001
|
+
Bucket: this.bucket,
|
|
2002
|
+
Prefix: normalizedPath,
|
|
2003
|
+
Delimiter: "/",
|
|
2004
|
+
MaxKeys: pageSize,
|
|
2005
|
+
ContinuationToken: continuationToken
|
|
2006
|
+
})
|
|
2007
|
+
);
|
|
2008
|
+
if (currentPage === pageNumber) {
|
|
2009
|
+
allDirectories = (result.CommonPrefixes || []).map((prefix) => prefix.Prefix).map((prefix) => {
|
|
2010
|
+
const relativePath = prefix.replace(normalizedPath, "");
|
|
2011
|
+
return relativePath.replace(/\/$/, "");
|
|
2012
|
+
}).filter((dir) => dir);
|
|
2013
|
+
allFiles = (result.Contents || []).filter((content) => {
|
|
2014
|
+
var _a2;
|
|
2015
|
+
return content.Key !== normalizedPath && !((_a2 = content.Key) == null ? void 0 : _a2.endsWith("/"));
|
|
2016
|
+
}).map((content) => __spreadProps(__spreadValues({}, content), {
|
|
2017
|
+
Name: content.Key.replace(normalizedPath, "") || content.Key,
|
|
2018
|
+
Location: `${this.link}${content.Key.replace(/^\//, "")}`,
|
|
2019
|
+
LastModified: new Date(content.LastModified)
|
|
2020
|
+
}));
|
|
2021
|
+
}
|
|
2022
|
+
continuationToken = result.NextContinuationToken;
|
|
2023
|
+
if (!result.IsTruncated || !continuationToken) {
|
|
2024
|
+
break;
|
|
2025
|
+
}
|
|
2026
|
+
currentPage++;
|
|
2027
|
+
}
|
|
2028
|
+
return {
|
|
2029
|
+
directories: allDirectories,
|
|
2030
|
+
files: allFiles,
|
|
2031
|
+
totalFetched: allFiles.length + allDirectories.length
|
|
2032
|
+
};
|
|
2033
|
+
});
|
|
2034
|
+
}
|
|
1683
2035
|
};
|
|
1684
2036
|
|
|
1685
2037
|
// src/aws/sns.ts
|
|
@@ -1718,6 +2070,7 @@ var SNSUtil = class {
|
|
|
1718
2070
|
AWSConfigSharingUtil,
|
|
1719
2071
|
IAMUtil,
|
|
1720
2072
|
LambdaUtil,
|
|
1721
|
-
|
|
2073
|
+
S3LocalstackUtil,
|
|
2074
|
+
S3Util,
|
|
1722
2075
|
SNSUtil
|
|
1723
2076
|
});
|