@hdriel/aws-utils 1.0.4 → 1.1.2
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 +723 -370
- package/dist/index.d.cts +126 -53
- package/dist/index.d.ts +126 -53
- package/dist/index.js +725 -367
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -19,6 +19,18 @@ var __spreadValues = (a, b) => {
|
|
|
19
19
|
return a;
|
|
20
20
|
};
|
|
21
21
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
22
|
+
var __objRest = (source, exclude) => {
|
|
23
|
+
var target = {};
|
|
24
|
+
for (var prop in source)
|
|
25
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
26
|
+
target[prop] = source[prop];
|
|
27
|
+
if (source != null && __getOwnPropSymbols)
|
|
28
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
29
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
30
|
+
target[prop] = source[prop];
|
|
31
|
+
}
|
|
32
|
+
return target;
|
|
33
|
+
};
|
|
22
34
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
23
35
|
var __async = (__this, __arguments, generator) => {
|
|
24
36
|
return new Promise((resolve, reject) => {
|
|
@@ -216,42 +228,19 @@ var LambdaUtil = class {
|
|
|
216
228
|
}
|
|
217
229
|
};
|
|
218
230
|
|
|
219
|
-
// src/aws/s3-
|
|
220
|
-
import
|
|
231
|
+
// src/aws/s3/s3-util.localstack.ts
|
|
232
|
+
import { ListObjectsV2Command as ListObjectsV2Command4 } from "@aws-sdk/client-s3";
|
|
233
|
+
|
|
234
|
+
// src/aws/s3/s3-stream.ts
|
|
221
235
|
import path from "pathe";
|
|
222
|
-
import http from "http";
|
|
223
|
-
import https from "https";
|
|
224
236
|
import { pipeline } from "stream";
|
|
225
237
|
import { promisify } from "util";
|
|
226
|
-
import {
|
|
227
|
-
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
228
|
-
import { NodeHttpHandler } from "@smithy/node-http-handler";
|
|
229
|
-
import { Buffer as Buffer2 } from "buffer";
|
|
238
|
+
import { Buffer as Buffer3 } from "buffer";
|
|
230
239
|
import archiver from "archiver";
|
|
231
|
-
import { Readable } from "stream";
|
|
232
|
-
import
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
S3Client,
|
|
236
|
-
DeleteBucketCommand,
|
|
237
|
-
HeadBucketCommand,
|
|
238
|
-
PutPublicAccessBlockCommand,
|
|
239
|
-
PutBucketPolicyCommand,
|
|
240
|
-
ListBucketsCommand,
|
|
241
|
-
GetBucketPolicyCommand,
|
|
242
|
-
GetBucketVersioningCommand,
|
|
243
|
-
GetBucketEncryptionCommand,
|
|
244
|
-
GetPublicAccessBlockCommand,
|
|
245
|
-
GetBucketAclCommand,
|
|
246
|
-
PutObjectCommand,
|
|
247
|
-
HeadObjectCommand,
|
|
248
|
-
ListObjectsCommand,
|
|
249
|
-
PutObjectTaggingCommand,
|
|
250
|
-
GetObjectTaggingCommand,
|
|
251
|
-
DeleteObjectCommand,
|
|
252
|
-
ListObjectsV2Command,
|
|
253
|
-
DeleteObjectsCommand
|
|
254
|
-
} from "@aws-sdk/client-s3";
|
|
240
|
+
import { Readable as Readable2 } from "stream";
|
|
241
|
+
import multerS3 from "multer-s3";
|
|
242
|
+
import multer from "multer";
|
|
243
|
+
import { GetObjectCommand as GetObjectCommand2 } from "@aws-sdk/client-s3";
|
|
255
244
|
|
|
256
245
|
// src/utils/consts.ts
|
|
257
246
|
var ACLs = /* @__PURE__ */ ((ACLs2) => {
|
|
@@ -265,11 +254,8 @@ var ACLs = /* @__PURE__ */ ((ACLs2) => {
|
|
|
265
254
|
import pLimit from "p-limit";
|
|
266
255
|
var s3Limiter = pLimit(4);
|
|
267
256
|
|
|
268
|
-
// src/
|
|
269
|
-
import multer from "multer";
|
|
270
|
-
import multerS3 from "multer-s3";
|
|
257
|
+
// src/utils/helpers.ts
|
|
271
258
|
import bytes from "bytes";
|
|
272
|
-
var pump = promisify(pipeline);
|
|
273
259
|
var parseRangeHeader = (range, contentLength, chunkSize) => {
|
|
274
260
|
if (!range || !range.startsWith("bytes=")) return null;
|
|
275
261
|
const rangeParts = range.replace("bytes=", "").split("-");
|
|
@@ -282,34 +268,88 @@ var parseRangeHeader = (range, contentLength, chunkSize) => {
|
|
|
282
268
|
}
|
|
283
269
|
return [start, Math.min(end, end)];
|
|
284
270
|
};
|
|
285
|
-
var getNormalizedPath = (directoryPath) =>
|
|
286
|
-
|
|
271
|
+
var getNormalizedPath = (directoryPath) => {
|
|
272
|
+
return decodeURIComponent((directoryPath == null ? void 0 : directoryPath.trim().replace(/^\/+/, "").replace(/\/+$/, "").replace(/\/+/g, "/")) || "");
|
|
273
|
+
};
|
|
274
|
+
var getFileSize = (maxFileSize, defaultMaxFileSize) => {
|
|
275
|
+
var _a2;
|
|
276
|
+
const fileSizeUnitValue = (_a2 = maxFileSize != null ? maxFileSize : defaultMaxFileSize) != null ? _a2 : "";
|
|
277
|
+
const fileSize = typeof fileSizeUnitValue === "number" ? fileSizeUnitValue : bytes(fileSizeUnitValue);
|
|
278
|
+
return fileSize != null ? fileSize : void 0;
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// src/aws/s3/s3-file.ts
|
|
282
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
283
|
+
import "stream";
|
|
284
|
+
import ms from "ms";
|
|
285
|
+
import { Upload } from "@aws-sdk/lib-storage";
|
|
286
|
+
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
287
|
+
import {
|
|
288
|
+
DeleteObjectCommand as DeleteObjectCommand2,
|
|
289
|
+
GetObjectCommand,
|
|
290
|
+
GetObjectTaggingCommand,
|
|
291
|
+
HeadObjectCommand as HeadObjectCommand2,
|
|
292
|
+
ListObjectsCommand,
|
|
293
|
+
ListObjectsV2Command as ListObjectsV2Command3,
|
|
294
|
+
PutObjectTaggingCommand
|
|
295
|
+
} from "@aws-sdk/client-s3";
|
|
296
|
+
|
|
297
|
+
// src/aws/s3/s3-directory.ts
|
|
298
|
+
import {
|
|
299
|
+
DeleteObjectCommand,
|
|
300
|
+
DeleteObjectsCommand as DeleteObjectsCommand2,
|
|
301
|
+
HeadObjectCommand,
|
|
302
|
+
ListObjectsV2Command as ListObjectsV2Command2,
|
|
303
|
+
PutObjectCommand
|
|
304
|
+
} from "@aws-sdk/client-s3";
|
|
305
|
+
|
|
306
|
+
// src/aws/s3/s3-bucket.ts
|
|
307
|
+
import {
|
|
308
|
+
CreateBucketCommand,
|
|
309
|
+
DeleteBucketCommand,
|
|
310
|
+
DeleteObjectsCommand,
|
|
311
|
+
GetBucketAclCommand,
|
|
312
|
+
GetBucketEncryptionCommand,
|
|
313
|
+
GetBucketPolicyCommand,
|
|
314
|
+
GetBucketVersioningCommand,
|
|
315
|
+
GetPublicAccessBlockCommand,
|
|
316
|
+
HeadBucketCommand,
|
|
317
|
+
ListBucketsCommand,
|
|
318
|
+
ListObjectsV2Command,
|
|
319
|
+
PutBucketPolicyCommand,
|
|
320
|
+
PutPublicAccessBlockCommand
|
|
321
|
+
} from "@aws-sdk/client-s3";
|
|
322
|
+
|
|
323
|
+
// src/aws/s3/s3-base.ts
|
|
324
|
+
import http from "http";
|
|
325
|
+
import https from "https";
|
|
326
|
+
import { NodeHttpHandler } from "@smithy/node-http-handler";
|
|
327
|
+
import { S3Client } from "@aws-sdk/client-s3";
|
|
328
|
+
var S3Base = class {
|
|
287
329
|
constructor({
|
|
288
330
|
logger: logger2,
|
|
289
|
-
bucket,
|
|
290
331
|
reqId,
|
|
291
332
|
accessKeyId = AWSConfigSharingUtil.accessKeyId,
|
|
292
333
|
secretAccessKey = AWSConfigSharingUtil.secretAccessKey,
|
|
293
334
|
endpoint = AWSConfigSharingUtil.endpoint,
|
|
294
335
|
region = AWSConfigSharingUtil.region,
|
|
295
336
|
s3ForcePathStyle = true,
|
|
296
|
-
|
|
337
|
+
// @ts-ignore
|
|
338
|
+
localstack = false
|
|
297
339
|
}) {
|
|
298
340
|
__publicField(this, "s3Client");
|
|
299
|
-
__publicField(this, "bucket");
|
|
300
341
|
__publicField(this, "endpoint");
|
|
301
342
|
__publicField(this, "region");
|
|
302
343
|
__publicField(this, "logger");
|
|
303
344
|
__publicField(this, "reqId");
|
|
304
|
-
__publicField(this, "
|
|
345
|
+
__publicField(this, "localstack", false);
|
|
305
346
|
const credentials = { accessKeyId, secretAccessKey };
|
|
306
347
|
const options = __spreadValues(__spreadValues(__spreadValues({}, accessKeyId && secretAccessKey && { credentials }), endpoint && { endpoint }), region && { region });
|
|
307
348
|
this.endpoint = endpoint;
|
|
308
349
|
this.region = region;
|
|
309
|
-
this.bucket = bucket;
|
|
310
350
|
this.logger = logger2;
|
|
311
351
|
this.reqId = reqId != null ? reqId : null;
|
|
312
|
-
this.
|
|
352
|
+
this.localstack = localstack;
|
|
313
353
|
const s3ClientParams = __spreadProps(__spreadValues(__spreadValues({}, options), s3ForcePathStyle && { forcePathStyle: s3ForcePathStyle }), {
|
|
314
354
|
requestHandler: new NodeHttpHandler({
|
|
315
355
|
httpAgent: new http.Agent({ keepAlive: true, maxSockets: 300 }),
|
|
@@ -320,17 +360,39 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
320
360
|
});
|
|
321
361
|
this.s3Client = new S3Client(s3ClientParams);
|
|
322
362
|
}
|
|
323
|
-
get link() {
|
|
324
|
-
return this.endpoint === "http://localhost:4566" ? `${this.endpoint}/${this.bucket}/` : `https://s3.${this.region}.amazonaws.com/${this.bucket}/`;
|
|
325
|
-
}
|
|
326
363
|
execute(command, options) {
|
|
327
364
|
return __async(this, null, function* () {
|
|
328
365
|
return this.s3Client.send(command, options);
|
|
329
366
|
});
|
|
330
367
|
}
|
|
331
|
-
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
// src/aws/s3/s3-bucket.ts
|
|
371
|
+
var S3Bucket = class extends S3Base {
|
|
372
|
+
constructor(_a2) {
|
|
373
|
+
var _b = _a2, { bucket } = _b, props = __objRest(_b, ["bucket"]);
|
|
374
|
+
super(props);
|
|
375
|
+
__publicField(this, "_bucket");
|
|
376
|
+
__publicField(this, "initializedBucket", "");
|
|
377
|
+
this._bucket = decodeURIComponent(bucket);
|
|
378
|
+
}
|
|
379
|
+
get link() {
|
|
380
|
+
return this.endpoint === "http://localhost:4566" ? `${this.endpoint}/${this.bucket}/` : `https://s3.${this.region}.amazonaws.com/${this.bucket}/`;
|
|
381
|
+
}
|
|
382
|
+
get bucket() {
|
|
383
|
+
return this._bucket;
|
|
384
|
+
}
|
|
385
|
+
changeBucket(bucket) {
|
|
386
|
+
this._bucket = decodeURIComponent(bucket);
|
|
387
|
+
this.initializedBucket = "";
|
|
388
|
+
}
|
|
332
389
|
getBucketList() {
|
|
333
|
-
return __async(this, arguments, function* (
|
|
390
|
+
return __async(this, arguments, function* (_c = {}) {
|
|
391
|
+
var _d = _c, {
|
|
392
|
+
includePublicAccess
|
|
393
|
+
} = _d, options = __objRest(_d, [
|
|
394
|
+
"includePublicAccess"
|
|
395
|
+
]);
|
|
334
396
|
const command = new ListBucketsCommand(options);
|
|
335
397
|
const response = yield this.execute(command);
|
|
336
398
|
const responseData = (response == null ? void 0 : response.Buckets) || null;
|
|
@@ -427,15 +489,22 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
427
489
|
});
|
|
428
490
|
}
|
|
429
491
|
initBucket() {
|
|
430
|
-
return __async(this, arguments, function* (acl = "private" /* private */,
|
|
492
|
+
return __async(this, arguments, function* (acl = "private" /* private */, {
|
|
493
|
+
includeConstraintLocation = false,
|
|
494
|
+
skipInitializedBucket = false
|
|
495
|
+
} = {}) {
|
|
431
496
|
var _a2;
|
|
432
497
|
const bucketName = this.bucket;
|
|
498
|
+
if (skipInitializedBucket && this.initializedBucket === bucketName) {
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
433
501
|
const isExists = yield this.isBucketExists();
|
|
434
502
|
if (isExists) {
|
|
435
503
|
(_a2 = this.logger) == null ? void 0 : _a2.info(this.reqId, `Bucket already exists.`, { bucketName });
|
|
436
504
|
return;
|
|
437
505
|
}
|
|
438
506
|
const data = acl === "private" /* private */ ? yield this.initAsPrivateBucket(includeConstraintLocation) : yield this.initAsPublicBucket();
|
|
507
|
+
this.initializedBucket = bucketName;
|
|
439
508
|
return data;
|
|
440
509
|
});
|
|
441
510
|
}
|
|
@@ -588,17 +657,42 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
588
657
|
return data;
|
|
589
658
|
});
|
|
590
659
|
}
|
|
591
|
-
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
// src/aws/s3/s3-directory.ts
|
|
663
|
+
var S3Directory = class extends S3Bucket {
|
|
664
|
+
constructor(props) {
|
|
665
|
+
super(props);
|
|
666
|
+
}
|
|
667
|
+
// todo: checked!
|
|
668
|
+
directoryExists(directoryPath) {
|
|
669
|
+
return __async(this, null, function* () {
|
|
670
|
+
var _a2;
|
|
671
|
+
try {
|
|
672
|
+
const normalizedKey = getNormalizedPath(directoryPath);
|
|
673
|
+
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
674
|
+
const command = new HeadObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
|
|
675
|
+
yield this.execute(command);
|
|
676
|
+
return true;
|
|
677
|
+
} catch (error) {
|
|
678
|
+
if (error.name === "NotFound" || ((_a2 = error.$metadata) == null ? void 0 : _a2.httpStatusCode) === 404) {
|
|
679
|
+
return false;
|
|
680
|
+
}
|
|
681
|
+
throw error;
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
// todo: checked!
|
|
592
686
|
createDirectory(directoryPath) {
|
|
593
687
|
return __async(this, null, function* () {
|
|
594
688
|
let normalizedPath = getNormalizedPath(directoryPath);
|
|
595
|
-
if (!normalizedPath) throw new Error("No directory path provided");
|
|
596
|
-
if (normalizedPath === "/") normalizedPath = "";
|
|
689
|
+
if (!normalizedPath || normalizedPath === "/") throw new Error("No directory path provided");
|
|
597
690
|
const command = new PutObjectCommand({ Bucket: this.bucket, Key: `${normalizedPath}/` });
|
|
598
691
|
const result = yield this.execute(command);
|
|
599
692
|
return result;
|
|
600
693
|
});
|
|
601
694
|
}
|
|
695
|
+
// todo: checked!
|
|
602
696
|
deleteDirectory(directoryPath) {
|
|
603
697
|
return __async(this, null, function* () {
|
|
604
698
|
var _a2, _b, _c, _d, _e, _f, _g;
|
|
@@ -609,7 +703,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
609
703
|
let ContinuationToken = void 0;
|
|
610
704
|
do {
|
|
611
705
|
const listResp = yield this.execute(
|
|
612
|
-
new
|
|
706
|
+
new ListObjectsV2Command2({
|
|
613
707
|
Bucket: this.bucket,
|
|
614
708
|
Prefix: normalizedPath,
|
|
615
709
|
ContinuationToken
|
|
@@ -617,7 +711,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
617
711
|
);
|
|
618
712
|
if (listResp.Contents && listResp.Contents.length > 0) {
|
|
619
713
|
const deleteResult = yield this.execute(
|
|
620
|
-
new
|
|
714
|
+
new DeleteObjectsCommand2({
|
|
621
715
|
Bucket: this.bucket,
|
|
622
716
|
Delete: {
|
|
623
717
|
Objects: listResp.Contents.map((obj) => ({ Key: obj.Key })),
|
|
@@ -636,7 +730,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
636
730
|
ContinuationToken = listResp.NextContinuationToken;
|
|
637
731
|
} while (ContinuationToken);
|
|
638
732
|
if (totalDeletedCount === 0) {
|
|
639
|
-
const directoryExists = yield this.
|
|
733
|
+
const directoryExists = yield this.directoryExists(normalizedPath);
|
|
640
734
|
if (!directoryExists) {
|
|
641
735
|
(_d = this.logger) == null ? void 0 : _d.debug(this.reqId, `Directory not found`, { directoryPath: normalizedPath });
|
|
642
736
|
return null;
|
|
@@ -668,34 +762,64 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
668
762
|
};
|
|
669
763
|
});
|
|
670
764
|
}
|
|
765
|
+
// todo: checked!
|
|
671
766
|
directoryList(directoryPath) {
|
|
672
767
|
return __async(this, null, function* () {
|
|
768
|
+
var _a2;
|
|
673
769
|
let normalizedPath = getNormalizedPath(directoryPath);
|
|
674
770
|
if (normalizedPath !== "/" && directoryPath !== "" && directoryPath !== void 0) normalizedPath += "/";
|
|
675
|
-
else normalizedPath = "";
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
771
|
+
else normalizedPath = this.localstack ? "" : "/";
|
|
772
|
+
let result;
|
|
773
|
+
if (normalizedPath === "") {
|
|
774
|
+
const [fileResponse, { CommonPrefixes }] = yield Promise.all([
|
|
775
|
+
this.execute(
|
|
776
|
+
new ListObjectsV2Command2({
|
|
777
|
+
Bucket: this.bucket,
|
|
778
|
+
Prefix: "/",
|
|
779
|
+
Delimiter: "/"
|
|
780
|
+
})
|
|
781
|
+
),
|
|
782
|
+
yield this.execute(
|
|
783
|
+
new ListObjectsV2Command2({
|
|
784
|
+
Bucket: this.bucket,
|
|
785
|
+
Prefix: "",
|
|
786
|
+
Delimiter: "/"
|
|
787
|
+
})
|
|
788
|
+
)
|
|
789
|
+
]);
|
|
790
|
+
result = fileResponse;
|
|
791
|
+
result.CommonPrefixes = CommonPrefixes;
|
|
792
|
+
} else {
|
|
793
|
+
result = yield this.execute(
|
|
794
|
+
new ListObjectsV2Command2({
|
|
795
|
+
Bucket: this.bucket,
|
|
796
|
+
Prefix: normalizedPath,
|
|
797
|
+
Delimiter: "/"
|
|
798
|
+
})
|
|
799
|
+
);
|
|
800
|
+
}
|
|
801
|
+
(_a2 = this.logger) == null ? void 0 : _a2.debug(null, "#### directoryList", {
|
|
802
|
+
normalizedPath,
|
|
803
|
+
CommonPrefixes: result.CommonPrefixes,
|
|
804
|
+
ContentFile: result.Contents
|
|
805
|
+
});
|
|
683
806
|
const directories = (result.CommonPrefixes || []).map((prefix) => prefix.Prefix).map((prefix) => {
|
|
684
807
|
const relativePath = prefix.replace(normalizedPath, "");
|
|
685
808
|
const dir = relativePath.replace(/\/$/, "");
|
|
686
809
|
return dir;
|
|
687
810
|
}).filter((dir) => dir);
|
|
688
811
|
const files = (result.Contents || []).filter((content) => {
|
|
689
|
-
var
|
|
690
|
-
return content.Key !== normalizedPath && !((
|
|
812
|
+
var _a3;
|
|
813
|
+
return content.Key !== normalizedPath && !((_a3 = content.Key) == null ? void 0 : _a3.endsWith("/"));
|
|
691
814
|
}).map((content) => __spreadProps(__spreadValues({}, content), {
|
|
692
815
|
Name: content.Key.replace(normalizedPath, "") || content.Key,
|
|
693
|
-
Location: `${this.link}${content.Key}`,
|
|
816
|
+
Location: `${this.link}${content.Key.replace(/^\//, "")}`,
|
|
694
817
|
LastModified: new Date(content.LastModified)
|
|
695
818
|
}));
|
|
696
819
|
return { directories, files };
|
|
697
820
|
});
|
|
698
821
|
}
|
|
822
|
+
// todo: checked!
|
|
699
823
|
directoryListPaginated(_0) {
|
|
700
824
|
return __async(this, arguments, function* (directoryPath, {
|
|
701
825
|
pageSize = 100,
|
|
@@ -710,8 +834,9 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
710
834
|
let allDirectories = [];
|
|
711
835
|
let allFiles = [];
|
|
712
836
|
while (currentPage <= pageNumber) {
|
|
713
|
-
|
|
714
|
-
|
|
837
|
+
let result;
|
|
838
|
+
result = yield this.execute(
|
|
839
|
+
new ListObjectsV2Command2({
|
|
715
840
|
Bucket: this.bucket,
|
|
716
841
|
Prefix: normalizedPath,
|
|
717
842
|
Delimiter: "/",
|
|
@@ -729,7 +854,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
729
854
|
return content.Key !== normalizedPath && !((_a2 = content.Key) == null ? void 0 : _a2.endsWith("/"));
|
|
730
855
|
}).map((content) => __spreadProps(__spreadValues({}, content), {
|
|
731
856
|
Name: content.Key.replace(normalizedPath, "") || content.Key,
|
|
732
|
-
Location: `${this.link}${content.Key}`,
|
|
857
|
+
Location: `${this.link}${content.Key.replace(/^\//, "")}`,
|
|
733
858
|
LastModified: new Date(content.LastModified)
|
|
734
859
|
}));
|
|
735
860
|
}
|
|
@@ -752,19 +877,21 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
752
877
|
*/
|
|
753
878
|
directoryListRecursive(directoryPath) {
|
|
754
879
|
return __async(this, null, function* () {
|
|
880
|
+
var _a2;
|
|
755
881
|
let normalizedPath = getNormalizedPath(directoryPath);
|
|
756
882
|
if (normalizedPath !== "/" && directoryPath !== "" && directoryPath !== void 0) normalizedPath += "/";
|
|
757
|
-
else normalizedPath = "";
|
|
883
|
+
else normalizedPath = "/";
|
|
758
884
|
const allDirectories = [];
|
|
759
885
|
const allFiles = [];
|
|
760
886
|
let ContinuationToken = void 0;
|
|
761
887
|
do {
|
|
762
|
-
const
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
888
|
+
const result = yield this.execute(
|
|
889
|
+
new ListObjectsV2Command2({
|
|
890
|
+
Bucket: this.bucket,
|
|
891
|
+
Prefix: normalizedPath,
|
|
892
|
+
ContinuationToken
|
|
893
|
+
})
|
|
894
|
+
);
|
|
768
895
|
if (result.Contents) {
|
|
769
896
|
for (const content of result.Contents) {
|
|
770
897
|
const fullPath = content.Key;
|
|
@@ -776,8 +903,8 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
776
903
|
allFiles.push(__spreadProps(__spreadValues({}, content), {
|
|
777
904
|
Name: filename,
|
|
778
905
|
Path: fullPath,
|
|
779
|
-
Location: `${this.link}${content.Key}
|
|
780
|
-
LastModified: new Date(content.LastModified)
|
|
906
|
+
Location: content.Key ? `${this.link}${(_a2 = content.Key) == null ? void 0 : _a2.replace(/^\//, "")}` : "",
|
|
907
|
+
LastModified: content.LastModified ? new Date(content.LastModified) : null
|
|
781
908
|
}));
|
|
782
909
|
}
|
|
783
910
|
}
|
|
@@ -833,7 +960,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
833
960
|
treeNode.children.push({
|
|
834
961
|
path: "/" + file.Key,
|
|
835
962
|
name: file.Name,
|
|
836
|
-
location: `${this.link}${file.Key}`,
|
|
963
|
+
location: `${this.link}${file.Key.replace(/^\//, "")}`,
|
|
837
964
|
type: "file",
|
|
838
965
|
size: file.Size,
|
|
839
966
|
lastModified: file.LastModified
|
|
@@ -847,14 +974,18 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
847
974
|
return treeNode;
|
|
848
975
|
});
|
|
849
976
|
}
|
|
850
|
-
|
|
977
|
+
};
|
|
978
|
+
|
|
979
|
+
// src/aws/s3/s3-file.ts
|
|
980
|
+
var S3File = class extends S3Directory {
|
|
981
|
+
constructor(props) {
|
|
982
|
+
super(props);
|
|
983
|
+
}
|
|
851
984
|
fileInfo(filePath) {
|
|
852
985
|
return __async(this, null, function* () {
|
|
853
986
|
const normalizedKey = getNormalizedPath(filePath);
|
|
854
|
-
if (!normalizedKey || normalizedKey === "/")
|
|
855
|
-
|
|
856
|
-
}
|
|
857
|
-
const command = new HeadObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
|
|
987
|
+
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
988
|
+
const command = new HeadObjectCommand2({ Bucket: this.bucket, Key: normalizedKey });
|
|
858
989
|
return yield this.execute(command);
|
|
859
990
|
});
|
|
860
991
|
}
|
|
@@ -863,7 +994,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
863
994
|
var _a2, _b;
|
|
864
995
|
let normalizedPath = getNormalizedPath(directoryPath);
|
|
865
996
|
if (normalizedPath !== "/" && directoryPath !== "" && directoryPath !== void 0) normalizedPath += "/";
|
|
866
|
-
else normalizedPath = "";
|
|
997
|
+
else normalizedPath = this.localstack ? "" : "/";
|
|
867
998
|
const prefix = normalizedPath + (fileNamePrefix || "");
|
|
868
999
|
const command = new ListObjectsCommand({
|
|
869
1000
|
Bucket: this.bucket,
|
|
@@ -873,10 +1004,10 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
873
1004
|
const result = yield this.execute(command);
|
|
874
1005
|
const files = ((_a2 = result.Contents) != null ? _a2 : []).filter((v) => v).map(
|
|
875
1006
|
(content) => {
|
|
876
|
-
var _a3, _b2;
|
|
1007
|
+
var _a3, _b2, _c;
|
|
877
1008
|
return __spreadProps(__spreadValues({}, content), {
|
|
878
1009
|
Name: (_b2 = (_a3 = content.Key) == null ? void 0 : _a3.replace(prefix, "")) != null ? _b2 : content.Key,
|
|
879
|
-
Location: `${this.link}${content.Key}
|
|
1010
|
+
Location: content.Key ? `${this.link}${(_c = content.Key) == null ? void 0 : _c.replace(/^\//, "")}` : "",
|
|
880
1011
|
LastModified: content.LastModified ? new Date(content.LastModified) : null
|
|
881
1012
|
});
|
|
882
1013
|
}
|
|
@@ -885,6 +1016,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
885
1016
|
return files;
|
|
886
1017
|
});
|
|
887
1018
|
}
|
|
1019
|
+
// todo: checked!
|
|
888
1020
|
fileListInfoPaginated(_0) {
|
|
889
1021
|
return __async(this, arguments, function* (directoryPath, {
|
|
890
1022
|
fileNamePrefix,
|
|
@@ -902,7 +1034,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
902
1034
|
let resultFiles = [];
|
|
903
1035
|
while (currentPage <= pageNumber) {
|
|
904
1036
|
const result = yield this.execute(
|
|
905
|
-
new
|
|
1037
|
+
new ListObjectsV2Command3({
|
|
906
1038
|
Bucket: this.bucket,
|
|
907
1039
|
Prefix: prefix,
|
|
908
1040
|
Delimiter: "/",
|
|
@@ -916,7 +1048,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
916
1048
|
var _a3, _b2;
|
|
917
1049
|
return __spreadProps(__spreadValues({}, content), {
|
|
918
1050
|
Name: (_b2 = (_a3 = content.Key) == null ? void 0 : _a3.replace(prefix, "")) != null ? _b2 : content.Key,
|
|
919
|
-
Location: `${this.link}${content.Key}
|
|
1051
|
+
Location: content.Key ? `${this.link}${content.Key.replace(/^\//, "")}` : "",
|
|
920
1052
|
LastModified: content.LastModified ? new Date(content.LastModified) : null
|
|
921
1053
|
});
|
|
922
1054
|
}
|
|
@@ -940,23 +1072,29 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
940
1072
|
};
|
|
941
1073
|
});
|
|
942
1074
|
}
|
|
943
|
-
|
|
1075
|
+
// todo: checked!
|
|
1076
|
+
taggingFile(filePath, tag) {
|
|
944
1077
|
return __async(this, null, function* () {
|
|
1078
|
+
var _a2;
|
|
1079
|
+
let normalizedKey = "";
|
|
1080
|
+
const tags = [].concat(tag);
|
|
945
1081
|
try {
|
|
946
|
-
|
|
1082
|
+
normalizedKey = getNormalizedPath(filePath);
|
|
947
1083
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
948
1084
|
const command = new PutObjectTaggingCommand({
|
|
949
1085
|
Bucket: this.bucket,
|
|
950
1086
|
Key: normalizedKey,
|
|
951
|
-
Tagging: { TagSet:
|
|
1087
|
+
Tagging: { TagSet: tags }
|
|
952
1088
|
});
|
|
953
1089
|
yield this.execute(command);
|
|
954
1090
|
return true;
|
|
955
|
-
} catch (
|
|
1091
|
+
} catch (error) {
|
|
1092
|
+
(_a2 = this.logger) == null ? void 0 : _a2.warn(null, "failed to tagging file", { errMsg: error.message, fileKey: normalizedKey, tags });
|
|
956
1093
|
return false;
|
|
957
1094
|
}
|
|
958
1095
|
});
|
|
959
1096
|
}
|
|
1097
|
+
// todo: checked!
|
|
960
1098
|
fileVersion(filePath) {
|
|
961
1099
|
return __async(this, null, function* () {
|
|
962
1100
|
var _a2, _b;
|
|
@@ -968,10 +1106,11 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
968
1106
|
return (_b = tag == null ? void 0 : tag.Value) != null ? _b : "";
|
|
969
1107
|
});
|
|
970
1108
|
}
|
|
1109
|
+
// todo: checked!
|
|
971
1110
|
fileUrl(filePath, expiresIn = "15m") {
|
|
972
1111
|
return __async(this, null, function* () {
|
|
973
1112
|
var _a2;
|
|
974
|
-
|
|
1113
|
+
let normalizedKey = getNormalizedPath(filePath);
|
|
975
1114
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
976
1115
|
const expiresInSeconds = typeof expiresIn === "number" ? expiresIn : ms(expiresIn) / 1e3;
|
|
977
1116
|
const command = new GetObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
|
|
@@ -989,7 +1128,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
989
1128
|
const normalizedKey = getNormalizedPath(filePath);
|
|
990
1129
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
991
1130
|
try {
|
|
992
|
-
const command = new
|
|
1131
|
+
const command = new HeadObjectCommand2({ Bucket: this.bucket, Key: normalizedKey });
|
|
993
1132
|
const headObject = yield this.execute(command);
|
|
994
1133
|
const bytes2 = (_a2 = headObject.ContentLength) != null ? _a2 : 0;
|
|
995
1134
|
switch (unit) {
|
|
@@ -1011,13 +1150,14 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1011
1150
|
}
|
|
1012
1151
|
});
|
|
1013
1152
|
}
|
|
1153
|
+
// todo: checked!
|
|
1014
1154
|
fileExists(filePath) {
|
|
1015
1155
|
return __async(this, null, function* () {
|
|
1016
1156
|
var _a2;
|
|
1017
1157
|
try {
|
|
1018
1158
|
const normalizedKey = getNormalizedPath(filePath);
|
|
1019
1159
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1020
|
-
const command = new
|
|
1160
|
+
const command = new HeadObjectCommand2({ Bucket: this.bucket, Key: normalizedKey });
|
|
1021
1161
|
yield this.execute(command);
|
|
1022
1162
|
return true;
|
|
1023
1163
|
} catch (error) {
|
|
@@ -1028,9 +1168,10 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1028
1168
|
}
|
|
1029
1169
|
});
|
|
1030
1170
|
}
|
|
1171
|
+
// todo: checked!
|
|
1031
1172
|
fileContent(filePath, format = "buffer") {
|
|
1032
1173
|
return __async(this, null, function* () {
|
|
1033
|
-
|
|
1174
|
+
let normalizedKey = getNormalizedPath(filePath);
|
|
1034
1175
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1035
1176
|
const command = new GetObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
|
|
1036
1177
|
const result = yield this.execute(command);
|
|
@@ -1061,6 +1202,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1061
1202
|
return buffer;
|
|
1062
1203
|
});
|
|
1063
1204
|
}
|
|
1205
|
+
// todo: checked!
|
|
1064
1206
|
uploadFile(_0, _1) {
|
|
1065
1207
|
return __async(this, arguments, function* (filePath, fileData, acl = "private" /* private */, version = "1.0.0") {
|
|
1066
1208
|
const normalizedKey = getNormalizedPath(filePath);
|
|
@@ -1084,38 +1226,130 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1084
1226
|
};
|
|
1085
1227
|
});
|
|
1086
1228
|
}
|
|
1229
|
+
// todo: checked!
|
|
1087
1230
|
deleteFile(filePath) {
|
|
1088
1231
|
return __async(this, null, function* () {
|
|
1089
1232
|
const normalizedKey = getNormalizedPath(filePath);
|
|
1090
1233
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1091
|
-
const command = new
|
|
1234
|
+
const command = new DeleteObjectCommand2({ Bucket: this.bucket, Key: normalizedKey });
|
|
1092
1235
|
return yield this.execute(command);
|
|
1093
1236
|
});
|
|
1094
1237
|
}
|
|
1095
|
-
|
|
1238
|
+
};
|
|
1239
|
+
|
|
1240
|
+
// src/aws/s3/s3-stream.ts
|
|
1241
|
+
var pump = promisify(pipeline);
|
|
1242
|
+
var S3Stream = class _S3Stream extends S3File {
|
|
1243
|
+
constructor(_a2) {
|
|
1244
|
+
var _b = _a2, { maxUploadFileSizeRestriction = "10GB" } = _b, props = __objRest(_b, ["maxUploadFileSizeRestriction"]);
|
|
1245
|
+
super(props);
|
|
1246
|
+
__publicField(this, "maxUploadFileSizeRestriction");
|
|
1247
|
+
// todo: LOCALSTACK SANITY CHECKED - WORKING WELL, DON'T TOUCH!
|
|
1248
|
+
__publicField(this, "getImageFileViewCtrl", ({
|
|
1249
|
+
fileKey: _fileKey,
|
|
1250
|
+
queryField = "file",
|
|
1251
|
+
cachingAge = 31536e3
|
|
1252
|
+
} = {}) => {
|
|
1253
|
+
return (req, res, next) => __async(this, null, function* () {
|
|
1254
|
+
var _a2, _b, _c, _d, _e;
|
|
1255
|
+
let fileKey = _fileKey || (((_a2 = req.query) == null ? void 0 : _a2[queryField]) ? decodeURIComponent((_b = req.query) == null ? void 0 : _b[queryField]) : void 0);
|
|
1256
|
+
if (!fileKey || fileKey === "/") {
|
|
1257
|
+
(_d = this.logger) == null ? void 0 : _d.warn(req.id, "image file view required file query field", {
|
|
1258
|
+
fileKey: (_c = req.query) == null ? void 0 : _c[queryField],
|
|
1259
|
+
queryField
|
|
1260
|
+
});
|
|
1261
|
+
next("image file key is required");
|
|
1262
|
+
return;
|
|
1263
|
+
}
|
|
1264
|
+
try {
|
|
1265
|
+
const imageBuffer = yield this.fileContent(fileKey, "buffer");
|
|
1266
|
+
const ext = path.extname(fileKey).slice(1).toLowerCase();
|
|
1267
|
+
const mimeTypeMap = {
|
|
1268
|
+
jpg: "image/jpeg",
|
|
1269
|
+
jpeg: "image/jpeg",
|
|
1270
|
+
png: "image/png",
|
|
1271
|
+
gif: "image/gif",
|
|
1272
|
+
webp: "image/webp",
|
|
1273
|
+
svg: "image/svg+xml",
|
|
1274
|
+
ico: "image/x-icon"
|
|
1275
|
+
};
|
|
1276
|
+
const contentType = mimeTypeMap[ext] || "application/octet-stream";
|
|
1277
|
+
res.setHeader("Content-Type", contentType);
|
|
1278
|
+
if (cachingAge) res.setHeader("Cache-Control", `public, max-age=${cachingAge}`);
|
|
1279
|
+
res.setHeader("Content-Length", imageBuffer.length);
|
|
1280
|
+
res.status(200).send(imageBuffer);
|
|
1281
|
+
} catch (error) {
|
|
1282
|
+
(_e = this.logger) == null ? void 0 : _e.warn(req.id, "image view fileKey not found", {
|
|
1283
|
+
fileKey,
|
|
1284
|
+
localstack: this.localstack
|
|
1285
|
+
});
|
|
1286
|
+
next(`Failed to retrieve image file: ${error.message}`);
|
|
1287
|
+
}
|
|
1288
|
+
});
|
|
1289
|
+
});
|
|
1290
|
+
// todo: LOCALSTACK SANITY CHECKED - WORKING WELL, DON'T TOUCH!
|
|
1291
|
+
__publicField(this, "getPdfFileViewCtrl", ({
|
|
1292
|
+
fileKey: _fileKey,
|
|
1293
|
+
queryField = "file",
|
|
1294
|
+
cachingAge = 31536e3
|
|
1295
|
+
} = {}) => {
|
|
1296
|
+
return (req, res, next) => __async(this, null, function* () {
|
|
1297
|
+
var _a2, _b;
|
|
1298
|
+
let fileKey = _fileKey || (((_a2 = req.query) == null ? void 0 : _a2[queryField]) ? decodeURIComponent((_b = req.query) == null ? void 0 : _b[queryField]) : void 0);
|
|
1299
|
+
if (!fileKey) {
|
|
1300
|
+
next("pdf file key is required");
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
try {
|
|
1304
|
+
const fileBuffer = yield this.fileContent(fileKey, "buffer");
|
|
1305
|
+
const ext = path.extname(fileKey).slice(1).toLowerCase();
|
|
1306
|
+
const mimeTypeMap = {
|
|
1307
|
+
pdf: "application/pdf",
|
|
1308
|
+
txt: "text/plain",
|
|
1309
|
+
doc: "application/msword",
|
|
1310
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
1311
|
+
xls: "application/vnd.ms-excel",
|
|
1312
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
1313
|
+
ppt: "application/vnd.ms-powerpoint",
|
|
1314
|
+
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
1315
|
+
};
|
|
1316
|
+
const contentType = mimeTypeMap[ext] || "application/octet-stream";
|
|
1317
|
+
res.setHeader("Content-Type", contentType);
|
|
1318
|
+
res.setHeader("Content-Disposition", `inline; filename="${path.basename(fileKey)}"`);
|
|
1319
|
+
res.setHeader("Cache-Control", `public, max-age=${cachingAge}`);
|
|
1320
|
+
res.setHeader("Content-Length", fileBuffer.length);
|
|
1321
|
+
res.status(200).send(fileBuffer);
|
|
1322
|
+
} catch (error) {
|
|
1323
|
+
next(`Failed to retrieve pdf file: ${error.message}`);
|
|
1324
|
+
}
|
|
1325
|
+
});
|
|
1326
|
+
});
|
|
1327
|
+
this.maxUploadFileSizeRestriction = maxUploadFileSizeRestriction;
|
|
1328
|
+
}
|
|
1096
1329
|
streamObjectFile(_0) {
|
|
1097
1330
|
return __async(this, arguments, function* (filePath, {
|
|
1098
1331
|
Range,
|
|
1099
1332
|
checkFileExists = true,
|
|
1100
1333
|
abortSignal
|
|
1101
1334
|
} = {}) {
|
|
1102
|
-
|
|
1335
|
+
let normalizedKey = getNormalizedPath(filePath);
|
|
1103
1336
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1104
1337
|
if (checkFileExists) {
|
|
1105
1338
|
const isExists = yield this.fileExists(normalizedKey);
|
|
1106
1339
|
if (!isExists) return null;
|
|
1107
1340
|
}
|
|
1108
|
-
const command = new
|
|
1341
|
+
const command = new GetObjectCommand2(__spreadValues({
|
|
1109
1342
|
Bucket: this.bucket,
|
|
1110
1343
|
Key: normalizedKey
|
|
1111
1344
|
}, Range ? { Range } : {}));
|
|
1112
1345
|
const response = yield this.execute(command, { abortSignal });
|
|
1113
|
-
if (!response.Body || !(response.Body instanceof
|
|
1346
|
+
if (!response.Body || !(response.Body instanceof Readable2)) {
|
|
1114
1347
|
throw new Error("Invalid response body: not a Readable stream");
|
|
1115
1348
|
}
|
|
1116
1349
|
return response.Body;
|
|
1117
1350
|
});
|
|
1118
1351
|
}
|
|
1352
|
+
// todo: LOCALSTACK SANITY CHECKED - WORKING WELL, DON'T TOUCH!
|
|
1119
1353
|
streamVideoFile(_0) {
|
|
1120
1354
|
return __async(this, arguments, function* ({
|
|
1121
1355
|
filePath,
|
|
@@ -1123,10 +1357,10 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1123
1357
|
abortSignal
|
|
1124
1358
|
}) {
|
|
1125
1359
|
var _a2;
|
|
1126
|
-
|
|
1360
|
+
let normalizedKey = getNormalizedPath(filePath);
|
|
1127
1361
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1128
1362
|
try {
|
|
1129
|
-
const cmd = new
|
|
1363
|
+
const cmd = new GetObjectCommand2(__spreadValues({
|
|
1130
1364
|
Bucket: this.bucket,
|
|
1131
1365
|
Key: normalizedKey
|
|
1132
1366
|
}, Range ? { Range } : {}));
|
|
@@ -1155,145 +1389,127 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1155
1389
|
}
|
|
1156
1390
|
});
|
|
1157
1391
|
}
|
|
1158
|
-
|
|
1392
|
+
// todo: LOCALSTACK SANITY CHECKED - WORKING WELL, DON'T TOUCH!
|
|
1393
|
+
getStreamVideoFileCtrl(_0) {
|
|
1159
1394
|
return __async(this, arguments, function* ({
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1395
|
+
fileKey,
|
|
1396
|
+
allowedWhitelist,
|
|
1397
|
+
contentType = "video/mp4",
|
|
1398
|
+
streamTimeoutMS = 3e4,
|
|
1399
|
+
bufferMB = 5
|
|
1163
1400
|
}) {
|
|
1164
1401
|
return (req, res, next) => __async(this, null, function* () {
|
|
1165
|
-
var _a2, _b, _c, _d, _e;
|
|
1166
|
-
|
|
1167
|
-
if (!
|
|
1168
|
-
|
|
1402
|
+
var _a2, _b, _c, _d, _e, _f;
|
|
1403
|
+
let normalizedKey = getNormalizedPath(fileKey);
|
|
1404
|
+
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1405
|
+
const isExists = yield this.fileExists(normalizedKey);
|
|
1406
|
+
const fileSize = yield this.sizeOf(normalizedKey);
|
|
1407
|
+
let Range;
|
|
1408
|
+
if (!isExists) {
|
|
1409
|
+
next(Error(`File does not exist: "${normalizedKey}"`));
|
|
1410
|
+
return;
|
|
1411
|
+
}
|
|
1412
|
+
try {
|
|
1413
|
+
if (req.method === "HEAD") {
|
|
1414
|
+
res.setHeader("Content-Type", contentType);
|
|
1415
|
+
res.setHeader("Accept-Ranges", "bytes");
|
|
1416
|
+
if (fileSize) res.setHeader("Content-Length", String(fileSize));
|
|
1417
|
+
return res.status(200).end();
|
|
1418
|
+
}
|
|
1419
|
+
const bufferSize = bufferMB;
|
|
1420
|
+
const CHUNK_SIZE = __pow(10, 6) * bufferSize;
|
|
1421
|
+
const rangeValues = parseRangeHeader(req.headers.range, fileSize, CHUNK_SIZE);
|
|
1422
|
+
let [start, end] = rangeValues || [];
|
|
1423
|
+
if (!rangeValues || start < 0 || start >= fileSize || end < 0 || end >= fileSize || start > end) {
|
|
1424
|
+
res.status(416).send("Requested Range Not Satisfiable");
|
|
1425
|
+
return;
|
|
1426
|
+
}
|
|
1427
|
+
res.statusCode = 206;
|
|
1428
|
+
const chunkLength = end - start + 1;
|
|
1429
|
+
res.setHeader("Content-Length", chunkLength);
|
|
1430
|
+
res.setHeader("Content-Range", `bytes ${start}-${end}/${fileSize}`);
|
|
1431
|
+
res.setHeader("Accept-Ranges", "bytes");
|
|
1432
|
+
res.setHeader("Content-Type", "video/mp4");
|
|
1433
|
+
Range = `bytes=${start}-${end}`;
|
|
1434
|
+
} catch (error) {
|
|
1435
|
+
next(error);
|
|
1436
|
+
return;
|
|
1169
1437
|
}
|
|
1170
|
-
let filename = _filename || (/* @__PURE__ */ new Date()).toISOString();
|
|
1171
|
-
filename = filename.endsWith(".zip") ? filename : `${filename}.zip`;
|
|
1172
1438
|
const abort = new AbortController();
|
|
1173
|
-
const onClose = () =>
|
|
1174
|
-
abort.abort();
|
|
1175
|
-
};
|
|
1439
|
+
const onClose = () => abort.abort();
|
|
1176
1440
|
req.once("close", onClose);
|
|
1177
1441
|
try {
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
if (abort.signal.aborted) return null;
|
|
1183
|
-
const stream = yield this.streamObjectFile(filePath2, { abortSignal: abort.signal });
|
|
1184
|
-
if (!stream) {
|
|
1185
|
-
(_a3 = this.logger) == null ? void 0 : _a3.warn(this.reqId, "File not found", { filePath: filePath2 });
|
|
1186
|
-
return null;
|
|
1187
|
-
}
|
|
1188
|
-
const chunks = [];
|
|
1189
|
-
try {
|
|
1190
|
-
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield iter.next()).done; more = false) {
|
|
1191
|
-
const chunk = temp.value;
|
|
1192
|
-
if (abort.signal.aborted) {
|
|
1193
|
-
stream.destroy();
|
|
1194
|
-
return null;
|
|
1195
|
-
}
|
|
1196
|
-
chunks.push(Buffer2.from(chunk));
|
|
1197
|
-
}
|
|
1198
|
-
} catch (temp) {
|
|
1199
|
-
error = [temp];
|
|
1200
|
-
} finally {
|
|
1201
|
-
try {
|
|
1202
|
-
more && (temp = iter.return) && (yield temp.call(iter));
|
|
1203
|
-
} finally {
|
|
1204
|
-
if (error)
|
|
1205
|
-
throw error[0];
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
const buffer = Buffer2.concat(chunks);
|
|
1209
|
-
const fileName = filePath2.split("/").pop() || filePath2;
|
|
1210
|
-
(_b2 = this.logger) == null ? void 0 : _b2.debug(this.reqId, "File downloaded", {
|
|
1211
|
-
filePath: filePath2,
|
|
1212
|
-
sizeMB: (buffer.length / (1024 * 1024)).toFixed(2)
|
|
1213
|
-
});
|
|
1214
|
-
return { buffer, name: fileName, path: filePath2 };
|
|
1215
|
-
} catch (error2) {
|
|
1216
|
-
(_c2 = this.logger) == null ? void 0 : _c2.warn(this.reqId, "Failed to download file", { filePath: filePath2, error: error2 });
|
|
1217
|
-
return null;
|
|
1218
|
-
}
|
|
1219
|
-
}));
|
|
1220
|
-
const fileBuffers = (yield Promise.all(downloadPromises)).filter(Boolean);
|
|
1221
|
-
if (abort.signal.aborted || fileBuffers.length === 0) {
|
|
1222
|
-
req.off("close", onClose);
|
|
1223
|
-
if (fileBuffers.length === 0) {
|
|
1224
|
-
next(new Error("No files available to zip"));
|
|
1225
|
-
}
|
|
1226
|
-
return;
|
|
1227
|
-
}
|
|
1228
|
-
(_b = this.logger) == null ? void 0 : _b.info(this.reqId, "All files downloaded, measuring zip size...", {
|
|
1229
|
-
fileCount: fileBuffers.length,
|
|
1230
|
-
totalSizeMB: (fileBuffers.reduce((sum, f) => sum + f.buffer.length, 0) / (1024 * 1024)).toFixed(2)
|
|
1231
|
-
});
|
|
1232
|
-
const measureArchive = archiver("zip", { zlib: { level: compressionLevel } });
|
|
1233
|
-
let actualZipSize = 0;
|
|
1234
|
-
measureArchive.on("data", (chunk) => {
|
|
1235
|
-
actualZipSize += chunk.length;
|
|
1442
|
+
const result = yield this.streamVideoFile({
|
|
1443
|
+
filePath: normalizedKey,
|
|
1444
|
+
Range,
|
|
1445
|
+
abortSignal: abort.signal
|
|
1236
1446
|
});
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
if (abort.signal.aborted) {
|
|
1243
|
-
req.off("close", onClose);
|
|
1244
|
-
return;
|
|
1447
|
+
const { body, meta } = result;
|
|
1448
|
+
const origin = Array.isArray(allowedWhitelist) ? allowedWhitelist.includes((_a2 = req.headers.origin) != null ? _a2 : "") ? req.headers.origin : void 0 : allowedWhitelist;
|
|
1449
|
+
if (origin) {
|
|
1450
|
+
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
1451
|
+
res.setHeader("Vary", "Origin");
|
|
1245
1452
|
}
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
res.setHeader("Access-Control-Expose-Headers", "Content-Type, Content-Disposition, Content-Length");
|
|
1255
|
-
actualArchive.on("error", (err) => {
|
|
1256
|
-
var _a3;
|
|
1257
|
-
(_a3 = this.logger) == null ? void 0 : _a3.error(this.reqId, "Archive error", { error: err });
|
|
1258
|
-
abort.abort();
|
|
1259
|
-
if (!res.headersSent) {
|
|
1260
|
-
next(err);
|
|
1453
|
+
const finalContentType = contentType.startsWith("video/") ? contentType : `video/${contentType}`;
|
|
1454
|
+
res.setHeader("Content-Type", (_b = meta.contentType) != null ? _b : finalContentType);
|
|
1455
|
+
res.setHeader("Accept-Ranges", (_c = meta.acceptRanges) != null ? _c : "bytes");
|
|
1456
|
+
if (Range && meta.contentRange) {
|
|
1457
|
+
res.status(206);
|
|
1458
|
+
res.setHeader("Content-Range", meta.contentRange);
|
|
1459
|
+
if (typeof meta.contentLength === "number") {
|
|
1460
|
+
res.setHeader("Content-Length", String(meta.contentLength));
|
|
1261
1461
|
}
|
|
1262
|
-
})
|
|
1263
|
-
|
|
1264
|
-
for (const file of fileBuffers) {
|
|
1265
|
-
if (abort.signal.aborted) break;
|
|
1266
|
-
actualArchive.append(file.buffer, { name: file.name });
|
|
1462
|
+
} else if (fileSize) {
|
|
1463
|
+
res.setHeader("Content-Length", String(fileSize));
|
|
1267
1464
|
}
|
|
1268
|
-
|
|
1269
|
-
(
|
|
1270
|
-
|
|
1271
|
-
|
|
1465
|
+
if (meta.etag) res.setHeader("ETag", meta.etag);
|
|
1466
|
+
if (meta.lastModified) res.setHeader("Last-Modified", meta.lastModified.toUTCString());
|
|
1467
|
+
const timeout = setTimeout(() => {
|
|
1468
|
+
abort.abort();
|
|
1469
|
+
if (!res.headersSent) res.status(504);
|
|
1470
|
+
res.end();
|
|
1471
|
+
}, streamTimeoutMS);
|
|
1472
|
+
res.once("close", () => {
|
|
1473
|
+
var _a3;
|
|
1474
|
+
clearTimeout(timeout);
|
|
1475
|
+
(_a3 = body.destroy) == null ? void 0 : _a3.call(body);
|
|
1476
|
+
req.off("close", onClose);
|
|
1272
1477
|
});
|
|
1273
|
-
|
|
1478
|
+
yield pump(body, res);
|
|
1479
|
+
clearTimeout(timeout);
|
|
1274
1480
|
} catch (error) {
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
if (isBenignError) {
|
|
1481
|
+
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";
|
|
1482
|
+
if (isBenignStreamError) {
|
|
1278
1483
|
return;
|
|
1279
1484
|
}
|
|
1280
1485
|
if (!res.headersSent) {
|
|
1281
|
-
(
|
|
1486
|
+
(_f = this.logger) == null ? void 0 : _f.warn(req.id, "caught exception in stream controller", {
|
|
1487
|
+
error: (_d = error == null ? void 0 : error.message) != null ? _d : String(error),
|
|
1488
|
+
key: fileKey,
|
|
1489
|
+
url: req.originalUrl,
|
|
1490
|
+
userId: (_e = req.user) == null ? void 0 : _e._id
|
|
1491
|
+
});
|
|
1282
1492
|
next(error);
|
|
1283
|
-
|
|
1493
|
+
return;
|
|
1494
|
+
}
|
|
1495
|
+
if (!res.writableEnded) {
|
|
1284
1496
|
try {
|
|
1285
1497
|
res.end();
|
|
1286
1498
|
} catch (e) {
|
|
1287
1499
|
}
|
|
1288
1500
|
}
|
|
1289
|
-
|
|
1290
|
-
req.off("close", onClose);
|
|
1501
|
+
return;
|
|
1291
1502
|
}
|
|
1292
1503
|
});
|
|
1293
1504
|
});
|
|
1294
1505
|
}
|
|
1506
|
+
// todo: LOCALSTACK SANITY CHECKED - WORKING WELL, DON'T TOUCH!
|
|
1295
1507
|
getStreamFileCtrl(_0) {
|
|
1296
|
-
return __async(this, arguments, function* ({
|
|
1508
|
+
return __async(this, arguments, function* ({
|
|
1509
|
+
filePath,
|
|
1510
|
+
filename,
|
|
1511
|
+
forDownloading = false
|
|
1512
|
+
}) {
|
|
1297
1513
|
return (req, res, next) => __async(this, null, function* () {
|
|
1298
1514
|
var _a2, _b;
|
|
1299
1515
|
const abort = new AbortController();
|
|
@@ -1304,7 +1520,7 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1304
1520
|
(_a3 = stream == null ? void 0 : stream.destroy) == null ? void 0 : _a3.call(stream);
|
|
1305
1521
|
};
|
|
1306
1522
|
req.once("close", onClose);
|
|
1307
|
-
|
|
1523
|
+
let normalizedKey = getNormalizedPath(filePath);
|
|
1308
1524
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1309
1525
|
try {
|
|
1310
1526
|
const isExists = yield this.fileExists(normalizedKey);
|
|
@@ -1325,7 +1541,9 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1325
1541
|
const fileInfo = yield this.fileInfo(normalizedKey);
|
|
1326
1542
|
const fileName = filename || normalizedKey.split("/").pop() || "download";
|
|
1327
1543
|
res.setHeader("Content-Type", fileInfo.ContentType || "application/octet-stream");
|
|
1328
|
-
|
|
1544
|
+
if (forDownloading) {
|
|
1545
|
+
res.setHeader("Content-Disposition", `attachment; filename="${fileName}"`);
|
|
1546
|
+
}
|
|
1329
1547
|
if (fileInfo.ContentLength) {
|
|
1330
1548
|
res.setHeader("Content-Length", String(fileInfo.ContentLength));
|
|
1331
1549
|
}
|
|
@@ -1366,115 +1584,140 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1366
1584
|
});
|
|
1367
1585
|
});
|
|
1368
1586
|
}
|
|
1369
|
-
|
|
1587
|
+
// todo: LOCALSTACK SANITY CHECKED - WORKING WELL, DON'T TOUCH!
|
|
1588
|
+
getStreamZipFileCtr(_0) {
|
|
1370
1589
|
return __async(this, arguments, function* ({
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
streamTimeoutMS = 3e4,
|
|
1375
|
-
bufferMB = 5
|
|
1590
|
+
filePath,
|
|
1591
|
+
filename: _filename,
|
|
1592
|
+
compressionLevel = 5
|
|
1376
1593
|
}) {
|
|
1377
1594
|
return (req, res, next) => __async(this, null, function* () {
|
|
1378
|
-
var _a2, _b, _c, _d, _e
|
|
1379
|
-
const
|
|
1380
|
-
if (!
|
|
1381
|
-
|
|
1382
|
-
const fileSize = yield this.sizeOf(normalizedKey);
|
|
1383
|
-
let Range;
|
|
1384
|
-
if (!isExists) {
|
|
1385
|
-
next(Error(`File does not exist: "${normalizedKey}"`));
|
|
1386
|
-
return;
|
|
1387
|
-
}
|
|
1388
|
-
try {
|
|
1389
|
-
if (req.method === "HEAD") {
|
|
1390
|
-
res.setHeader("Content-Type", contentType);
|
|
1391
|
-
res.setHeader("Accept-Ranges", "bytes");
|
|
1392
|
-
if (fileSize) res.setHeader("Content-Length", String(fileSize));
|
|
1393
|
-
return res.status(200).end();
|
|
1394
|
-
}
|
|
1395
|
-
const bufferSize = bufferMB;
|
|
1396
|
-
const CHUNK_SIZE = __pow(10, 6) * bufferSize;
|
|
1397
|
-
const rangeValues = parseRangeHeader(req.headers.range, fileSize, CHUNK_SIZE);
|
|
1398
|
-
let [start, end] = rangeValues || [];
|
|
1399
|
-
if (!rangeValues || start < 0 || start >= fileSize || end < 0 || end >= fileSize || start > end) {
|
|
1400
|
-
res.status(416).send("Requested Range Not Satisfiable");
|
|
1401
|
-
return;
|
|
1402
|
-
}
|
|
1403
|
-
res.statusCode = 206;
|
|
1404
|
-
const chunkLength = end - start + 1;
|
|
1405
|
-
res.setHeader("Content-Length", chunkLength);
|
|
1406
|
-
res.setHeader("Content-Range", `bytes ${start}-${end}/${fileSize}`);
|
|
1407
|
-
res.setHeader("Accept-Ranges", "bytes");
|
|
1408
|
-
res.setHeader("Content-Type", "video/mp4");
|
|
1409
|
-
Range = `bytes=${start}-${end}`;
|
|
1410
|
-
} catch (error) {
|
|
1411
|
-
next(error);
|
|
1412
|
-
return;
|
|
1595
|
+
var _a2, _b, _c, _d, _e;
|
|
1596
|
+
const filePaths = [].concat(filePath).map((filePath2) => getNormalizedPath(filePath2)).filter((v) => v && v !== "/");
|
|
1597
|
+
if (!filePaths.length) {
|
|
1598
|
+
throw new Error("No file keys provided");
|
|
1413
1599
|
}
|
|
1600
|
+
let filename = _filename || (/* @__PURE__ */ new Date()).toISOString();
|
|
1601
|
+
filename = filename.endsWith(".zip") ? filename : `${filename}.zip`;
|
|
1414
1602
|
const abort = new AbortController();
|
|
1415
|
-
const onClose = () =>
|
|
1603
|
+
const onClose = () => {
|
|
1604
|
+
abort.abort();
|
|
1605
|
+
};
|
|
1416
1606
|
req.once("close", onClose);
|
|
1417
1607
|
try {
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1608
|
+
(_a2 = this.logger) == null ? void 0 : _a2.info(this.reqId, "Starting parallel file download...", { fileCount: filePaths.length });
|
|
1609
|
+
const downloadPromises = filePaths.map((filePath2) => __async(this, null, function* () {
|
|
1610
|
+
var _a3, _b2, _c2;
|
|
1611
|
+
try {
|
|
1612
|
+
if (abort.signal.aborted) return null;
|
|
1613
|
+
const stream = yield this.streamObjectFile(filePath2, { abortSignal: abort.signal });
|
|
1614
|
+
if (!stream) {
|
|
1615
|
+
(_a3 = this.logger) == null ? void 0 : _a3.warn(this.reqId, "File not found", { filePath: filePath2 });
|
|
1616
|
+
return null;
|
|
1617
|
+
}
|
|
1618
|
+
const chunks = [];
|
|
1619
|
+
try {
|
|
1620
|
+
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield iter.next()).done; more = false) {
|
|
1621
|
+
const chunk = temp.value;
|
|
1622
|
+
if (abort.signal.aborted) {
|
|
1623
|
+
stream.destroy();
|
|
1624
|
+
return null;
|
|
1625
|
+
}
|
|
1626
|
+
chunks.push(Buffer3.from(chunk));
|
|
1627
|
+
}
|
|
1628
|
+
} catch (temp) {
|
|
1629
|
+
error = [temp];
|
|
1630
|
+
} finally {
|
|
1631
|
+
try {
|
|
1632
|
+
more && (temp = iter.return) && (yield temp.call(iter));
|
|
1633
|
+
} finally {
|
|
1634
|
+
if (error)
|
|
1635
|
+
throw error[0];
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
const buffer = Buffer3.concat(chunks);
|
|
1639
|
+
const fileName = filePath2.split("/").pop() || filePath2;
|
|
1640
|
+
(_b2 = this.logger) == null ? void 0 : _b2.debug(this.reqId, "File downloaded", {
|
|
1641
|
+
filePath: filePath2,
|
|
1642
|
+
sizeMB: (buffer.length / (1024 * 1024)).toFixed(2)
|
|
1643
|
+
});
|
|
1644
|
+
return { buffer, name: fileName, path: filePath2 };
|
|
1645
|
+
} catch (error2) {
|
|
1646
|
+
(_c2 = this.logger) == null ? void 0 : _c2.warn(this.reqId, "Failed to download file", { filePath: filePath2, error: error2 });
|
|
1647
|
+
return null;
|
|
1648
|
+
}
|
|
1649
|
+
}));
|
|
1650
|
+
const fileBuffers = (yield Promise.all(downloadPromises)).filter(Boolean);
|
|
1651
|
+
if (abort.signal.aborted || fileBuffers.length === 0) {
|
|
1652
|
+
req.off("close", onClose);
|
|
1653
|
+
if (fileBuffers.length === 0) {
|
|
1654
|
+
next(new Error("No files available to zip"));
|
|
1655
|
+
}
|
|
1656
|
+
return;
|
|
1657
|
+
}
|
|
1658
|
+
(_b = this.logger) == null ? void 0 : _b.info(this.reqId, "All files downloaded, measuring zip size...", {
|
|
1659
|
+
fileCount: fileBuffers.length,
|
|
1660
|
+
totalSizeMB: (fileBuffers.reduce((sum, f) => sum + f.buffer.length, 0) / (1024 * 1024)).toFixed(2)
|
|
1422
1661
|
});
|
|
1423
|
-
const
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1662
|
+
const measureArchive = archiver("zip", { zlib: { level: compressionLevel } });
|
|
1663
|
+
let actualZipSize = 0;
|
|
1664
|
+
measureArchive.on("data", (chunk) => {
|
|
1665
|
+
actualZipSize += chunk.length;
|
|
1666
|
+
});
|
|
1667
|
+
for (const file of fileBuffers) {
|
|
1668
|
+
if (abort.signal.aborted) break;
|
|
1669
|
+
measureArchive.append(file.buffer, { name: file.name });
|
|
1428
1670
|
}
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
res.status(206);
|
|
1434
|
-
res.setHeader("Content-Range", meta.contentRange);
|
|
1435
|
-
if (typeof meta.contentLength === "number") {
|
|
1436
|
-
res.setHeader("Content-Length", String(meta.contentLength));
|
|
1437
|
-
}
|
|
1438
|
-
} else if (fileSize) {
|
|
1439
|
-
res.setHeader("Content-Length", String(fileSize));
|
|
1671
|
+
yield measureArchive.finalize();
|
|
1672
|
+
if (abort.signal.aborted) {
|
|
1673
|
+
req.off("close", onClose);
|
|
1674
|
+
return;
|
|
1440
1675
|
}
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
res.
|
|
1676
|
+
(_c = this.logger) == null ? void 0 : _c.info(this.reqId, "Zip size calculated", {
|
|
1677
|
+
actualZipSize,
|
|
1678
|
+
sizeMB: (actualZipSize / (1024 * 1024)).toFixed(2)
|
|
1679
|
+
});
|
|
1680
|
+
const actualArchive = archiver("zip", { zlib: { level: compressionLevel } });
|
|
1681
|
+
res.setHeader("Content-Type", "application/zip");
|
|
1682
|
+
res.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
|
|
1683
|
+
res.setHeader("Content-Length", String(actualZipSize));
|
|
1684
|
+
res.setHeader("Access-Control-Expose-Headers", "Content-Type, Content-Disposition, Content-Length");
|
|
1685
|
+
actualArchive.on("error", (err) => {
|
|
1449
1686
|
var _a3;
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1687
|
+
(_a3 = this.logger) == null ? void 0 : _a3.error(this.reqId, "Archive error", { error: err });
|
|
1688
|
+
abort.abort();
|
|
1689
|
+
if (!res.headersSent) {
|
|
1690
|
+
next(err);
|
|
1691
|
+
}
|
|
1453
1692
|
});
|
|
1454
|
-
|
|
1455
|
-
|
|
1693
|
+
actualArchive.pipe(res);
|
|
1694
|
+
for (const file of fileBuffers) {
|
|
1695
|
+
if (abort.signal.aborted) break;
|
|
1696
|
+
actualArchive.append(file.buffer, { name: file.name });
|
|
1697
|
+
}
|
|
1698
|
+
yield actualArchive.finalize();
|
|
1699
|
+
(_d = this.logger) == null ? void 0 : _d.info(this.reqId, "Zip download completed", {
|
|
1700
|
+
fileCount: fileBuffers.length,
|
|
1701
|
+
totalSize: actualZipSize
|
|
1702
|
+
});
|
|
1703
|
+
req.off("close", onClose);
|
|
1456
1704
|
} catch (error) {
|
|
1457
|
-
|
|
1458
|
-
|
|
1705
|
+
abort.abort();
|
|
1706
|
+
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";
|
|
1707
|
+
if (isBenignError) {
|
|
1459
1708
|
return;
|
|
1460
1709
|
}
|
|
1461
1710
|
if (!res.headersSent) {
|
|
1462
|
-
(
|
|
1463
|
-
error: (_d = error == null ? void 0 : error.message) != null ? _d : String(error),
|
|
1464
|
-
key: fileKey,
|
|
1465
|
-
url: req.originalUrl,
|
|
1466
|
-
userId: (_e = req.user) == null ? void 0 : _e._id
|
|
1467
|
-
});
|
|
1711
|
+
(_e = this.logger) == null ? void 0 : _e.error(this.reqId, "Failed to create zip archive", { error });
|
|
1468
1712
|
next(error);
|
|
1469
|
-
|
|
1470
|
-
}
|
|
1471
|
-
if (!res.writableEnded) {
|
|
1713
|
+
} else if (!res.writableEnded) {
|
|
1472
1714
|
try {
|
|
1473
1715
|
res.end();
|
|
1474
1716
|
} catch (e) {
|
|
1475
1717
|
}
|
|
1476
1718
|
}
|
|
1477
|
-
|
|
1719
|
+
} finally {
|
|
1720
|
+
req.off("close", onClose);
|
|
1478
1721
|
}
|
|
1479
1722
|
});
|
|
1480
1723
|
});
|
|
@@ -1492,19 +1735,6 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1492
1735
|
return cb(new Error(errorMsg));
|
|
1493
1736
|
};
|
|
1494
1737
|
}
|
|
1495
|
-
getFileSize(maxFileSize) {
|
|
1496
|
-
var _a2;
|
|
1497
|
-
const fileSizeUnitValue = maxFileSize != null ? maxFileSize : this.maxUploadFileSizeRestriction;
|
|
1498
|
-
const fileSize = typeof fileSizeUnitValue === "number" ? fileSizeUnitValue : bytes(fileSizeUnitValue);
|
|
1499
|
-
if (!fileSize) {
|
|
1500
|
-
(_a2 = this.logger) == null ? void 0 : _a2.warn(this.reqId, "Failed to convert fileSize restriction, proceeding without limit", {
|
|
1501
|
-
maxFileSize,
|
|
1502
|
-
maxUploadFileSizeRestriction: this.maxUploadFileSizeRestriction
|
|
1503
|
-
});
|
|
1504
|
-
return void 0;
|
|
1505
|
-
}
|
|
1506
|
-
return fileSize;
|
|
1507
|
-
}
|
|
1508
1738
|
getUploadFileMW(directoryPath, {
|
|
1509
1739
|
acl = "private" /* private */,
|
|
1510
1740
|
maxFileSize,
|
|
@@ -1516,10 +1746,10 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1516
1746
|
let normalizedPath = getNormalizedPath(directoryPath);
|
|
1517
1747
|
if (normalizedPath !== "/" && directoryPath !== "" && directoryPath !== void 0) normalizedPath += "/";
|
|
1518
1748
|
else normalizedPath = "";
|
|
1519
|
-
const fileSize =
|
|
1749
|
+
const fileSize = getFileSize(maxFileSize, this.maxUploadFileSizeRestriction);
|
|
1520
1750
|
const fileTypes = [].concat(fileType);
|
|
1521
1751
|
const fileExts = [].concat(fileExt);
|
|
1522
|
-
const fileFilter = (fileTypes == null ? void 0 : fileTypes.length) || (fileExts == null ? void 0 : fileExts.length) ?
|
|
1752
|
+
const fileFilter = (fileTypes == null ? void 0 : fileTypes.length) || (fileExts == null ? void 0 : fileExts.length) ? _S3Stream.fileFilter(fileTypes, fileExts) : void 0;
|
|
1523
1753
|
return multer({
|
|
1524
1754
|
fileFilter,
|
|
1525
1755
|
limits: __spreadValues({}, fileSize && { fileSize }),
|
|
@@ -1556,14 +1786,19 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1556
1786
|
* Middleware for uploading a single file
|
|
1557
1787
|
* Adds the uploaded file info to req.s3File
|
|
1558
1788
|
*/
|
|
1559
|
-
uploadSingleFile(fieldName,
|
|
1560
|
-
|
|
1789
|
+
uploadSingleFile(fieldName, directoryPath, options = {}) {
|
|
1790
|
+
var _a2;
|
|
1791
|
+
let normalizedPath = getNormalizedPath(directoryPath);
|
|
1792
|
+
if (normalizedPath !== "/" && directoryPath !== "" && directoryPath !== void 0) normalizedPath += "/";
|
|
1793
|
+
else normalizedPath = "";
|
|
1794
|
+
(_a2 = this.logger) == null ? void 0 : _a2.debug(null, "####### uploadSingleFile", { directoryPath, normalizedPath, fieldName });
|
|
1795
|
+
const upload = this.getUploadFileMW(normalizedPath, options);
|
|
1561
1796
|
return (req, res, next) => {
|
|
1562
1797
|
const mw = upload.single(fieldName);
|
|
1563
1798
|
mw(req, res, (err) => {
|
|
1564
|
-
var
|
|
1799
|
+
var _a3, _b;
|
|
1565
1800
|
if (err) {
|
|
1566
|
-
(
|
|
1801
|
+
(_a3 = this.logger) == null ? void 0 : _a3.error(this.reqId, "Single file upload error", { fieldName, error: err.message });
|
|
1567
1802
|
return next(err);
|
|
1568
1803
|
}
|
|
1569
1804
|
if (req.file) {
|
|
@@ -1583,8 +1818,11 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1583
1818
|
* Middleware for uploading multiple files with the same field name
|
|
1584
1819
|
* Adds the uploaded files info to req.s3Files
|
|
1585
1820
|
*/
|
|
1586
|
-
uploadMultipleFiles(fieldName,
|
|
1587
|
-
|
|
1821
|
+
uploadMultipleFiles(fieldName, directoryPath, options = {}) {
|
|
1822
|
+
let normalizedPath = getNormalizedPath(directoryPath);
|
|
1823
|
+
if (normalizedPath !== "/" && directoryPath !== "" && directoryPath !== void 0) normalizedPath += "/";
|
|
1824
|
+
else normalizedPath = "";
|
|
1825
|
+
const upload = this.getUploadFileMW(normalizedPath, options);
|
|
1588
1826
|
return (req, res, next) => {
|
|
1589
1827
|
const mw = upload.array(fieldName, options.maxFilesCount || void 0);
|
|
1590
1828
|
mw(req, res, (err) => {
|
|
@@ -1605,49 +1843,15 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1605
1843
|
});
|
|
1606
1844
|
};
|
|
1607
1845
|
}
|
|
1608
|
-
/**
|
|
1609
|
-
* Middleware for uploading multiple files with different field names
|
|
1610
|
-
* Adds the uploaded files info to req.s3FilesByField
|
|
1611
|
-
*/
|
|
1612
|
-
uploadFieldsFiles(fields) {
|
|
1613
|
-
const fieldConfigs = fields.map((field) => {
|
|
1614
|
-
const upload = this.getUploadFileMW(field.directory, field.options || {});
|
|
1615
|
-
return {
|
|
1616
|
-
name: field.name,
|
|
1617
|
-
maxCount: field.maxCount || 1,
|
|
1618
|
-
upload,
|
|
1619
|
-
directory: field.directory
|
|
1620
|
-
};
|
|
1621
|
-
});
|
|
1622
|
-
return (req, res, next) => __async(this, null, function* () {
|
|
1623
|
-
const multerFields = fieldConfigs.map((f) => ({ name: f.name, maxCount: f.maxCount }));
|
|
1624
|
-
const upload = this.getUploadFileMW(fieldConfigs[0].directory);
|
|
1625
|
-
const mw = upload.fields(multerFields);
|
|
1626
|
-
mw(req, res, (err) => {
|
|
1627
|
-
var _a2, _b;
|
|
1628
|
-
if (err) {
|
|
1629
|
-
(_a2 = this.logger) == null ? void 0 : _a2.error(this.reqId, "Fields upload error", { error: err.message });
|
|
1630
|
-
return next(err);
|
|
1631
|
-
}
|
|
1632
|
-
if (req.files && typeof req.files === "object" && !Array.isArray(req.files)) {
|
|
1633
|
-
req.s3FilesByField = req.files;
|
|
1634
|
-
const uploadSummary = Object.entries(req.s3FilesByField).map(([field, files]) => ({
|
|
1635
|
-
field,
|
|
1636
|
-
count: files.length,
|
|
1637
|
-
keys: files.map((f) => f.key)
|
|
1638
|
-
}));
|
|
1639
|
-
(_b = this.logger) == null ? void 0 : _b.info(this.reqId, "Fields uploaded successfully", { uploadSummary });
|
|
1640
|
-
}
|
|
1641
|
-
next();
|
|
1642
|
-
});
|
|
1643
|
-
});
|
|
1644
|
-
}
|
|
1645
1846
|
/**
|
|
1646
1847
|
* Middleware for uploading any files (mixed field names)
|
|
1647
1848
|
* Adds the uploaded files info to req.s3AllFiles
|
|
1648
1849
|
*/
|
|
1649
|
-
uploadAnyFiles(
|
|
1650
|
-
|
|
1850
|
+
uploadAnyFiles(directoryPath, maxCount, options = {}) {
|
|
1851
|
+
let normalizedPath = getNormalizedPath(directoryPath);
|
|
1852
|
+
if (normalizedPath !== "/" && normalizedPath !== "" && directoryPath !== void 0) normalizedPath += "/";
|
|
1853
|
+
else normalizedPath = "";
|
|
1854
|
+
const upload = this.getUploadFileMW(normalizedPath, options);
|
|
1651
1855
|
return (req, res, next) => {
|
|
1652
1856
|
const anyUpload = maxCount ? upload.any() : upload.any();
|
|
1653
1857
|
anyUpload(req, res, (err) => {
|
|
@@ -1670,6 +1874,159 @@ var S3BucketUtil = class _S3BucketUtil {
|
|
|
1670
1874
|
});
|
|
1671
1875
|
};
|
|
1672
1876
|
}
|
|
1877
|
+
/**
|
|
1878
|
+
* Middleware for uploading multiple files with different field names
|
|
1879
|
+
* Adds the uploaded files info to req.s3FilesByField
|
|
1880
|
+
*/
|
|
1881
|
+
// uploadFieldsFiles(
|
|
1882
|
+
// fields: Array<{ name: string; directory: string; maxCount?: number; options?: S3UploadOptions }>
|
|
1883
|
+
// ): RequestHandler {
|
|
1884
|
+
// // Create separate multer instances for each field (since each might have different options)
|
|
1885
|
+
// const fieldConfigs = fields.map((field) => {
|
|
1886
|
+
// const upload = this.getUploadFileMW(field.directory, field.options || {});
|
|
1887
|
+
//
|
|
1888
|
+
// return {
|
|
1889
|
+
// name: getNormalizedPath(field.name),
|
|
1890
|
+
// directory: getNormalizedPath(field.directory),
|
|
1891
|
+
// maxCount: field.maxCount || 1,
|
|
1892
|
+
// upload,
|
|
1893
|
+
// };
|
|
1894
|
+
// });
|
|
1895
|
+
//
|
|
1896
|
+
// return async (
|
|
1897
|
+
// req: Request & { s3FilesByField?: Record<string, UploadedS3File[]> } & any,
|
|
1898
|
+
// res: Response,
|
|
1899
|
+
// next: NextFunction & any
|
|
1900
|
+
// ) => {
|
|
1901
|
+
// // We'll use the first upload instance but with fields configuration
|
|
1902
|
+
// const multerFields = fieldConfigs.map((f) => ({ name: f.name, maxCount: f.maxCount }));
|
|
1903
|
+
// const upload = this.getUploadFileMW(fieldConfigs[0].directory);
|
|
1904
|
+
//
|
|
1905
|
+
// const mw: RequestHandler & any = upload.fields(multerFields);
|
|
1906
|
+
// mw(req, res, (err: any) => {
|
|
1907
|
+
// if (err) {
|
|
1908
|
+
// this.logger?.error(this.reqId, 'Fields upload error', { error: err.message });
|
|
1909
|
+
// return next(err);
|
|
1910
|
+
// }
|
|
1911
|
+
//
|
|
1912
|
+
// if (req.files && typeof req.files === 'object' && !Array.isArray(req.files)) {
|
|
1913
|
+
// req.s3FilesByField = req.files as Record<string, UploadedS3File[]>;
|
|
1914
|
+
//
|
|
1915
|
+
// const uploadSummary = Object.entries(req.s3FilesByField).map(([field, files]: any) => ({
|
|
1916
|
+
// field,
|
|
1917
|
+
// count: files.length,
|
|
1918
|
+
// keys: files.map((f: any) => f.key),
|
|
1919
|
+
// }));
|
|
1920
|
+
//
|
|
1921
|
+
// this.logger?.info(this.reqId, 'Fields uploaded successfully', { uploadSummary });
|
|
1922
|
+
// }
|
|
1923
|
+
//
|
|
1924
|
+
// next();
|
|
1925
|
+
// });
|
|
1926
|
+
// };
|
|
1927
|
+
// }
|
|
1928
|
+
};
|
|
1929
|
+
|
|
1930
|
+
// src/aws/s3/s3-util.ts
|
|
1931
|
+
var S3Util = class extends S3Stream {
|
|
1932
|
+
constructor(props) {
|
|
1933
|
+
super(props);
|
|
1934
|
+
}
|
|
1935
|
+
};
|
|
1936
|
+
|
|
1937
|
+
// src/aws/s3/s3-util.localstack.ts
|
|
1938
|
+
var S3LocalstackUtil = class extends S3Util {
|
|
1939
|
+
constructor(props) {
|
|
1940
|
+
super(__spreadProps(__spreadValues({}, props), { localstack: true }));
|
|
1941
|
+
}
|
|
1942
|
+
// todo: checked!
|
|
1943
|
+
directoryList(directoryPath) {
|
|
1944
|
+
return __async(this, null, function* () {
|
|
1945
|
+
var _a2;
|
|
1946
|
+
let normalizedPath = getNormalizedPath(directoryPath);
|
|
1947
|
+
if (normalizedPath !== "/" && directoryPath !== "" && directoryPath !== void 0) normalizedPath += "/";
|
|
1948
|
+
else normalizedPath = "";
|
|
1949
|
+
let result;
|
|
1950
|
+
result = yield this.execute(
|
|
1951
|
+
new ListObjectsV2Command4({
|
|
1952
|
+
Bucket: this.bucket,
|
|
1953
|
+
Prefix: normalizedPath,
|
|
1954
|
+
Delimiter: "/"
|
|
1955
|
+
})
|
|
1956
|
+
);
|
|
1957
|
+
(_a2 = this.logger) == null ? void 0 : _a2.debug(null, "#### directoryList", {
|
|
1958
|
+
normalizedPath,
|
|
1959
|
+
CommonPrefixes: result.CommonPrefixes,
|
|
1960
|
+
ContentFile: result.Contents
|
|
1961
|
+
});
|
|
1962
|
+
const directories = (result.CommonPrefixes || []).map((prefix) => prefix.Prefix).map((prefix) => {
|
|
1963
|
+
const relativePath = prefix.replace(normalizedPath, "");
|
|
1964
|
+
const dir = relativePath.replace(/\/$/, "");
|
|
1965
|
+
return dir;
|
|
1966
|
+
}).filter((dir) => dir);
|
|
1967
|
+
const files = (result.Contents || []).filter((content) => {
|
|
1968
|
+
var _a3;
|
|
1969
|
+
return content.Key !== normalizedPath && !((_a3 = content.Key) == null ? void 0 : _a3.endsWith("/"));
|
|
1970
|
+
}).map((content) => __spreadProps(__spreadValues({}, content), {
|
|
1971
|
+
Name: content.Key.replace(normalizedPath, "") || content.Key,
|
|
1972
|
+
Location: `${this.link}${content.Key.replace(/^\//, "")}`,
|
|
1973
|
+
LastModified: new Date(content.LastModified)
|
|
1974
|
+
}));
|
|
1975
|
+
return { directories, files };
|
|
1976
|
+
});
|
|
1977
|
+
}
|
|
1978
|
+
// todo: checked!
|
|
1979
|
+
directoryListPaginated(_0) {
|
|
1980
|
+
return __async(this, arguments, function* (directoryPath, {
|
|
1981
|
+
pageSize = 100,
|
|
1982
|
+
pageNumber = 0
|
|
1983
|
+
// 0-based: page 0 = items 0-99, page 1 = items 100-199, page 2 = items 200-299
|
|
1984
|
+
} = {}) {
|
|
1985
|
+
let normalizedPath = getNormalizedPath(directoryPath);
|
|
1986
|
+
if (normalizedPath !== "/" && directoryPath !== "" && directoryPath !== void 0) normalizedPath += "/";
|
|
1987
|
+
else normalizedPath = this.localstack ? "" : "/";
|
|
1988
|
+
let continuationToken = void 0;
|
|
1989
|
+
let currentPage = 0;
|
|
1990
|
+
let allDirectories = [];
|
|
1991
|
+
let allFiles = [];
|
|
1992
|
+
while (currentPage <= pageNumber) {
|
|
1993
|
+
let result;
|
|
1994
|
+
result = yield this.execute(
|
|
1995
|
+
new ListObjectsV2Command4({
|
|
1996
|
+
Bucket: this.bucket,
|
|
1997
|
+
Prefix: normalizedPath,
|
|
1998
|
+
Delimiter: "/",
|
|
1999
|
+
MaxKeys: pageSize,
|
|
2000
|
+
ContinuationToken: continuationToken
|
|
2001
|
+
})
|
|
2002
|
+
);
|
|
2003
|
+
if (currentPage === pageNumber) {
|
|
2004
|
+
allDirectories = (result.CommonPrefixes || []).map((prefix) => prefix.Prefix).map((prefix) => {
|
|
2005
|
+
const relativePath = prefix.replace(normalizedPath, "");
|
|
2006
|
+
return relativePath.replace(/\/$/, "");
|
|
2007
|
+
}).filter((dir) => dir);
|
|
2008
|
+
allFiles = (result.Contents || []).filter((content) => {
|
|
2009
|
+
var _a2;
|
|
2010
|
+
return content.Key !== normalizedPath && !((_a2 = content.Key) == null ? void 0 : _a2.endsWith("/"));
|
|
2011
|
+
}).map((content) => __spreadProps(__spreadValues({}, content), {
|
|
2012
|
+
Name: content.Key.replace(normalizedPath, "") || content.Key,
|
|
2013
|
+
Location: `${this.link}${content.Key.replace(/^\//, "")}`,
|
|
2014
|
+
LastModified: new Date(content.LastModified)
|
|
2015
|
+
}));
|
|
2016
|
+
}
|
|
2017
|
+
continuationToken = result.NextContinuationToken;
|
|
2018
|
+
if (!result.IsTruncated || !continuationToken) {
|
|
2019
|
+
break;
|
|
2020
|
+
}
|
|
2021
|
+
currentPage++;
|
|
2022
|
+
}
|
|
2023
|
+
return {
|
|
2024
|
+
directories: allDirectories,
|
|
2025
|
+
files: allFiles,
|
|
2026
|
+
totalFetched: allFiles.length + allDirectories.length
|
|
2027
|
+
};
|
|
2028
|
+
});
|
|
2029
|
+
}
|
|
1673
2030
|
};
|
|
1674
2031
|
|
|
1675
2032
|
// src/aws/sns.ts
|
|
@@ -1707,6 +2064,7 @@ export {
|
|
|
1707
2064
|
AWSConfigSharingUtil,
|
|
1708
2065
|
IAMUtil,
|
|
1709
2066
|
LambdaUtil,
|
|
1710
|
-
|
|
2067
|
+
S3LocalstackUtil,
|
|
2068
|
+
S3Util,
|
|
1711
2069
|
SNSUtil
|
|
1712
2070
|
};
|