@hdriel/aws-utils 1.0.0

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.js ADDED
@@ -0,0 +1,1631 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
8
+ var __pow = Math.pow;
9
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
10
+ var __spreadValues = (a, b) => {
11
+ for (var prop in b || (b = {}))
12
+ if (__hasOwnProp.call(b, prop))
13
+ __defNormalProp(a, prop, b[prop]);
14
+ if (__getOwnPropSymbols)
15
+ for (var prop of __getOwnPropSymbols(b)) {
16
+ if (__propIsEnum.call(b, prop))
17
+ __defNormalProp(a, prop, b[prop]);
18
+ }
19
+ return a;
20
+ };
21
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
22
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
23
+ var __async = (__this, __arguments, generator) => {
24
+ return new Promise((resolve, reject) => {
25
+ var fulfilled = (value) => {
26
+ try {
27
+ step(generator.next(value));
28
+ } catch (e) {
29
+ reject(e);
30
+ }
31
+ };
32
+ var rejected = (value) => {
33
+ try {
34
+ step(generator.throw(value));
35
+ } catch (e) {
36
+ reject(e);
37
+ }
38
+ };
39
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
40
+ step((generator = generator.apply(__this, __arguments)).next());
41
+ });
42
+ };
43
+ var __forAwait = (obj, it, method) => (it = obj[__knownSymbol("asyncIterator")]) ? it.call(obj) : (obj = obj[__knownSymbol("iterator")](), it = {}, method = (key, fn) => (fn = obj[key]) && (it[key] = (arg) => new Promise((yes, no, done) => (arg = fn.call(obj, arg), done = arg.done, Promise.resolve(arg.value).then((value) => yes({ value, done }), no)))), method("next"), method("return"), it);
44
+
45
+ // src/aws/iam.ts
46
+ import { IAMClient, ListUsersCommand } from "@aws-sdk/client-iam";
47
+
48
+ // src/aws/configuration.ts
49
+ var _AWSConfigSharingUtil = class _AWSConfigSharingUtil {
50
+ constructor() {
51
+ }
52
+ static setConfig({
53
+ accessKeyId,
54
+ secretAccessKey,
55
+ endpoint,
56
+ region
57
+ }) {
58
+ _AWSConfigSharingUtil.accessKeyId = accessKeyId;
59
+ _AWSConfigSharingUtil.secretAccessKey = secretAccessKey;
60
+ _AWSConfigSharingUtil.endpoint = endpoint;
61
+ _AWSConfigSharingUtil.region = region;
62
+ }
63
+ static getConfig() {
64
+ return {
65
+ accessKeyId: _AWSConfigSharingUtil.accessKeyId,
66
+ secretAccessKey: _AWSConfigSharingUtil.secretAccessKey,
67
+ region: _AWSConfigSharingUtil.region,
68
+ endpoint: _AWSConfigSharingUtil.endpoint
69
+ };
70
+ }
71
+ };
72
+ __publicField(_AWSConfigSharingUtil, "accessKeyId");
73
+ __publicField(_AWSConfigSharingUtil, "secretAccessKey");
74
+ __publicField(_AWSConfigSharingUtil, "endpoint");
75
+ __publicField(_AWSConfigSharingUtil, "region");
76
+ var AWSConfigSharingUtil = _AWSConfigSharingUtil;
77
+
78
+ // src/aws/iam.ts
79
+ var IAMUtil = class {
80
+ constructor({
81
+ accessKeyId = AWSConfigSharingUtil.accessKeyId,
82
+ secretAccessKey = AWSConfigSharingUtil.secretAccessKey,
83
+ endpoint = AWSConfigSharingUtil.endpoint,
84
+ region = AWSConfigSharingUtil.region,
85
+ debug = false
86
+ } = {}) {
87
+ __publicField(this, "iam");
88
+ const credentials = { accessKeyId, secretAccessKey };
89
+ const options = __spreadValues(__spreadValues(__spreadValues({}, accessKeyId && secretAccessKey && { credentials }), endpoint && { endpoint }), region && { region });
90
+ if (debug) {
91
+ console.log("IAMUtil client options", options);
92
+ }
93
+ this.iam = new IAMClient(options);
94
+ }
95
+ get client() {
96
+ return this.iam;
97
+ }
98
+ getUserList() {
99
+ return __async(this, null, function* () {
100
+ const command = new ListUsersCommand({});
101
+ return this.iam.send(command);
102
+ });
103
+ }
104
+ listUsers(maxItems) {
105
+ return __async(this, null, function* () {
106
+ try {
107
+ const command = new ListUsersCommand({ MaxItems: maxItems });
108
+ const response = yield this.iam.send(command);
109
+ return response.Users;
110
+ } catch (error) {
111
+ console.error("Error listing IAM users:", error);
112
+ return null;
113
+ }
114
+ });
115
+ }
116
+ };
117
+
118
+ // src/aws/lambda.ts
119
+ import { Lambda, InvocationType, InvokeCommand } from "@aws-sdk/client-lambda";
120
+
121
+ // src/utils/logger.ts
122
+ import { Logger } from "stack-trace-logger";
123
+ var includeCloudWatchOptions = process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY && process.env.AWS_REGION && process.env.AWS_LOG_GROUP_NAME && process.env.AWS_LOG_STREAM_NAME;
124
+ var _a;
125
+ var logger = new Logger(__spreadValues({
126
+ serviceName: process.env.SERVICE_NAME || "SERVER",
127
+ loggingModeLevel: process.env.LOGGING_MODE,
128
+ lineTraceLevels: (_a = process.env.LOGGING_STACK_TRACE_LEVELS) == null ? void 0 : _a.split(","),
129
+ stackTraceLines: { error: 3, warn: 3, info: 1 },
130
+ tags: ["reqId?", "url?"],
131
+ runLocally: true
132
+ }, includeCloudWatchOptions && {
133
+ transportCloudWatchOptions: {
134
+ awsAccessKeyId: process.env.AWS_ACCESS_KEY_ID,
135
+ awsSecretKey: process.env.AWS_SECRET_ACCESS_KEY,
136
+ awsRegion: process.env.AWS_REGION,
137
+ logGroupName: process.env.AWS_LOG_GROUP_NAME,
138
+ logStreamName: process.env.AWS_LOG_STREAM_NAME,
139
+ retentionInDays: +process.env.AWS_LOG_RETENTION_IN_DAY
140
+ }
141
+ }));
142
+
143
+ // src/aws/lambda.ts
144
+ var LambdaUtil = class {
145
+ constructor({
146
+ accessKeyId = AWSConfigSharingUtil.accessKeyId,
147
+ secretAccessKey = AWSConfigSharingUtil.secretAccessKey,
148
+ endpoint = AWSConfigSharingUtil.endpoint,
149
+ region = AWSConfigSharingUtil.region,
150
+ serviceFunctionName,
151
+ debug = false
152
+ }) {
153
+ __publicField(this, "lambda");
154
+ __publicField(this, "serviceFunctionName");
155
+ const credentials = { accessKeyId, secretAccessKey };
156
+ const options = __spreadValues(__spreadValues(__spreadValues({}, accessKeyId && secretAccessKey && { credentials }), endpoint && { endpoint }), region && { region });
157
+ if (debug) {
158
+ console.log("LambdaUtil client options", options);
159
+ }
160
+ this.serviceFunctionName = serviceFunctionName;
161
+ this.lambda = new Lambda(__spreadValues(__spreadValues(__spreadValues({}, credentials && { credentials }), endpoint && { endpoint }), region && { region }));
162
+ }
163
+ directInvoke(_0) {
164
+ return __async(this, arguments, function* ({
165
+ payload = {},
166
+ invocationType = InvocationType.Event
167
+ }) {
168
+ var _a2;
169
+ const Payload = JSON.stringify(payload);
170
+ try {
171
+ const command = new InvokeCommand({
172
+ FunctionName: this.serviceFunctionName,
173
+ Payload,
174
+ InvocationType: invocationType
175
+ });
176
+ const data = yield this.lambda.send(command);
177
+ if (invocationType === InvocationType.RequestResponse) {
178
+ logger.info(null, "directInvoke lambda function response", { FunctionName, data });
179
+ }
180
+ const status = (_a2 = data.StatusCode) != null ? _a2 : 200;
181
+ const result = data.Payload;
182
+ return status >= 200 && status < 300 ? result : Promise.reject(result);
183
+ } catch (err) {
184
+ logger.error(null, "failed to directInvoke lambda function", {
185
+ err,
186
+ Payload,
187
+ FunctionName
188
+ });
189
+ throw err;
190
+ }
191
+ });
192
+ }
193
+ runLambdaInDryRunMode(payload) {
194
+ return __async(this, null, function* () {
195
+ return this.directInvoke({
196
+ payload,
197
+ invocationType: InvocationType.DryRun
198
+ });
199
+ });
200
+ }
201
+ triggerLambdaEvent(payload) {
202
+ return __async(this, null, function* () {
203
+ return this.directInvoke({
204
+ payload,
205
+ invocationType: InvocationType.Event
206
+ });
207
+ });
208
+ }
209
+ runAndGetLambdaResponse(payload) {
210
+ return __async(this, null, function* () {
211
+ return this.directInvoke({
212
+ payload,
213
+ invocationType: InvocationType.RequestResponse
214
+ });
215
+ });
216
+ }
217
+ };
218
+
219
+ // src/aws/s3-bucket.ts
220
+ import ms from "ms";
221
+ import path from "pathe";
222
+ import http from "http";
223
+ import https from "https";
224
+ import { pipeline } from "stream";
225
+ import { promisify } from "util";
226
+ import { Upload } from "@aws-sdk/lib-storage";
227
+ import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
228
+ import { NodeHttpHandler } from "@smithy/node-http-handler";
229
+ import { Buffer as Buffer2 } from "buffer";
230
+ import archiver from "archiver";
231
+ import { Readable } from "stream";
232
+ import {
233
+ CreateBucketCommand,
234
+ GetObjectCommand,
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";
255
+
256
+ // src/utils/consts.ts
257
+ var ACLs = /* @__PURE__ */ ((ACLs2) => {
258
+ ACLs2["private"] = "private";
259
+ ACLs2["publicRead"] = "public-read";
260
+ ACLs2["publicReadWrite"] = "public-read-write";
261
+ return ACLs2;
262
+ })(ACLs || {});
263
+
264
+ // src/utils/concurrency.ts
265
+ import pLimit from "p-limit";
266
+ var s3Limiter = pLimit(4);
267
+
268
+ // src/aws/s3-bucket.ts
269
+ import multer from "multer";
270
+ import multerS3 from "multer-s3";
271
+ import bytes from "bytes";
272
+ var pump = promisify(pipeline);
273
+ var parseRangeHeader = (range, contentLength, chunkSize) => {
274
+ if (!range || !range.startsWith("bytes=")) return null;
275
+ const rangeParts = range.replace("bytes=", "").split("-");
276
+ const start = parseInt(rangeParts[0], 10);
277
+ let end = parseInt(rangeParts[1], 10);
278
+ end = end || start + chunkSize - 1;
279
+ if (isNaN(start) || start < 0 || start >= contentLength) return null;
280
+ if (isNaN(end) || end < start || end >= contentLength) {
281
+ return [start, contentLength - 1];
282
+ }
283
+ return [start, Math.min(end, end)];
284
+ };
285
+ var S3BucketUtil = class _S3BucketUtil {
286
+ constructor({
287
+ logger: logger2,
288
+ bucket,
289
+ reqId,
290
+ accessKeyId = AWSConfigSharingUtil.accessKeyId,
291
+ secretAccessKey = AWSConfigSharingUtil.secretAccessKey,
292
+ endpoint = AWSConfigSharingUtil.endpoint,
293
+ region = AWSConfigSharingUtil.region,
294
+ s3ForcePathStyle = true,
295
+ maxUploadFileSizeRestriction = "10GB"
296
+ }) {
297
+ __publicField(this, "s3Client");
298
+ __publicField(this, "bucket");
299
+ __publicField(this, "endpoint");
300
+ __publicField(this, "region");
301
+ __publicField(this, "logger");
302
+ __publicField(this, "reqId");
303
+ __publicField(this, "maxUploadFileSizeRestriction");
304
+ const credentials = { accessKeyId, secretAccessKey };
305
+ const options = __spreadValues(__spreadValues(__spreadValues({}, accessKeyId && secretAccessKey && { credentials }), endpoint && { endpoint }), region && { region });
306
+ this.endpoint = endpoint;
307
+ this.region = region;
308
+ this.bucket = bucket;
309
+ this.logger = logger2;
310
+ this.reqId = reqId != null ? reqId : null;
311
+ this.maxUploadFileSizeRestriction = maxUploadFileSizeRestriction;
312
+ const s3ClientParams = __spreadProps(__spreadValues(__spreadValues({}, options), s3ForcePathStyle && { forcePathStyle: s3ForcePathStyle }), {
313
+ requestHandler: new NodeHttpHandler({
314
+ httpAgent: new http.Agent({ keepAlive: true, maxSockets: 300 }),
315
+ httpsAgent: new https.Agent({ keepAlive: true, maxSockets: 300 }),
316
+ connectionTimeout: 3e3,
317
+ socketTimeout: 3e4
318
+ })
319
+ });
320
+ this.s3Client = new S3Client(s3ClientParams);
321
+ }
322
+ get link() {
323
+ return this.endpoint === "http://localhost:4566" ? `${this.endpoint}/${this.bucket}/` : `https://s3.${this.region}.amazonaws.com/${this.bucket}/`;
324
+ }
325
+ execute(command, options) {
326
+ return __async(this, null, function* () {
327
+ return this.s3Client.send(command, options);
328
+ });
329
+ }
330
+ // ##### BUCKET BLOCK ##########################
331
+ getBucketList() {
332
+ return __async(this, arguments, function* (options = {}, includePublicAccess = false) {
333
+ const command = new ListBucketsCommand(options);
334
+ const response = yield this.execute(command);
335
+ const responseData = (response == null ? void 0 : response.Buckets) || null;
336
+ if (!responseData) return null;
337
+ if (includePublicAccess) {
338
+ yield Promise.allSettled(
339
+ responseData.map((data) => __async(this, null, function* () {
340
+ const result = yield this.execute(
341
+ new GetPublicAccessBlockCommand({ Bucket: data.Name })
342
+ );
343
+ data.PublicAccessBlockConfiguration = result.PublicAccessBlockConfiguration;
344
+ }))
345
+ );
346
+ }
347
+ return responseData;
348
+ });
349
+ }
350
+ isBucketExists() {
351
+ return __async(this, null, function* () {
352
+ var _a2, _b;
353
+ const bucketName = this.bucket;
354
+ try {
355
+ yield this.execute(new HeadBucketCommand({ Bucket: bucketName }));
356
+ return true;
357
+ } catch (err) {
358
+ if (err.name !== "NotFound" && ((_a2 = err.$metadata) == null ? void 0 : _a2.httpStatusCode) !== 404) {
359
+ (_b = this.logger) == null ? void 0 : _b.error(this.reqId, "Error checking bucket:", err);
360
+ throw err;
361
+ } else {
362
+ return false;
363
+ }
364
+ }
365
+ });
366
+ }
367
+ initAsPublicBucket() {
368
+ return __async(this, null, function* () {
369
+ var _a2, _b;
370
+ const bucketName = this.bucket;
371
+ const isExists = yield this.isBucketExists();
372
+ if (isExists) {
373
+ (_a2 = this.logger) == null ? void 0 : _a2.info(this.reqId, `Bucket already exists.`, { bucketName });
374
+ return;
375
+ }
376
+ const data = yield this.execute(new CreateBucketCommand({ Bucket: bucketName }));
377
+ CREATE_PUBLICK_ACCESS_BLOCK: {
378
+ const command = new PutPublicAccessBlockCommand({
379
+ Bucket: bucketName,
380
+ PublicAccessBlockConfiguration: {
381
+ BlockPublicAcls: false,
382
+ IgnorePublicAcls: false,
383
+ BlockPublicPolicy: false,
384
+ RestrictPublicBuckets: false
385
+ }
386
+ });
387
+ yield this.execute(command);
388
+ }
389
+ UPDATE_PUBLICK_ACCESS_POLICY: {
390
+ const policy = {
391
+ Version: "2012-10-17",
392
+ Statement: [
393
+ {
394
+ Sid: "PublicReadGetObject",
395
+ Effect: "Allow",
396
+ Principal: "*",
397
+ Action: "s3:GetObject",
398
+ Resource: `arn:aws:s3:::${bucketName}/*`
399
+ }
400
+ ]
401
+ };
402
+ const command = new PutBucketPolicyCommand({ Bucket: bucketName, Policy: JSON.stringify(policy) });
403
+ yield this.execute(command);
404
+ }
405
+ (_b = this.logger) == null ? void 0 : _b.info(this.reqId, `Public bucket created successfully.`, { bucketName });
406
+ return data;
407
+ });
408
+ }
409
+ initAsPrivateBucket(includeConstraintLocation) {
410
+ return __async(this, null, function* () {
411
+ var _a2, _b;
412
+ const bucketName = this.bucket;
413
+ const isExists = yield this.isBucketExists();
414
+ if (isExists) {
415
+ (_a2 = this.logger) == null ? void 0 : _a2.info(this.reqId, `Bucket already exists.`, { bucketName });
416
+ return;
417
+ }
418
+ const createParams = __spreadValues({
419
+ Bucket: bucketName
420
+ }, includeConstraintLocation && {
421
+ CreateBucketConfiguration: { LocationConstraint: this.region }
422
+ });
423
+ const data = yield this.execute(new CreateBucketCommand(createParams));
424
+ (_b = this.logger) == null ? void 0 : _b.info(this.reqId, `Private bucket created successfully.`, { bucketName });
425
+ return data;
426
+ });
427
+ }
428
+ initBucket() {
429
+ return __async(this, arguments, function* (acl = "private" /* private */, includeConstraintLocation = false) {
430
+ var _a2;
431
+ const bucketName = this.bucket;
432
+ const isExists = yield this.isBucketExists();
433
+ if (isExists) {
434
+ (_a2 = this.logger) == null ? void 0 : _a2.info(this.reqId, `Bucket already exists.`, { bucketName });
435
+ return;
436
+ }
437
+ const data = acl === "private" /* private */ ? yield this.initAsPrivateBucket(includeConstraintLocation) : yield this.initAsPublicBucket();
438
+ return data;
439
+ });
440
+ }
441
+ emptyBucket() {
442
+ return __async(this, null, function* () {
443
+ let ContinuationToken = void 0;
444
+ do {
445
+ const listResp = yield this.execute(
446
+ new ListObjectsV2Command({
447
+ Bucket: this.bucket,
448
+ ContinuationToken
449
+ })
450
+ );
451
+ if (listResp.Contents && listResp.Contents.length > 0) {
452
+ yield this.execute(
453
+ new DeleteObjectsCommand({
454
+ Bucket: this.bucket,
455
+ Delete: {
456
+ Objects: listResp.Contents.map((obj) => ({ Key: obj.Key }))
457
+ }
458
+ })
459
+ );
460
+ }
461
+ ContinuationToken = listResp.NextContinuationToken;
462
+ } while (ContinuationToken);
463
+ });
464
+ }
465
+ bucketInfo(options) {
466
+ return __async(this, null, function* () {
467
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
468
+ const bucketName = this.bucket;
469
+ const info = {
470
+ name: bucketName,
471
+ region: this.region,
472
+ endpoint: this.endpoint,
473
+ exists: false
474
+ };
475
+ try {
476
+ try {
477
+ const headBucketResponse = yield this.execute(
478
+ new HeadBucketCommand(__spreadValues({ Bucket: bucketName }, options))
479
+ );
480
+ (_a2 = this.logger) == null ? void 0 : _a2.debug("bucketInfo", "HeadBucketCommandOutput", headBucketResponse);
481
+ info.exists = true;
482
+ info.bucketRegion = headBucketResponse.BucketRegion;
483
+ info.accessPointAlias = headBucketResponse.AccessPointAlias;
484
+ } catch (err) {
485
+ if (err.name === "NotFound" || ((_b = err.$metadata) == null ? void 0 : _b.httpStatusCode) === 404) {
486
+ return info;
487
+ }
488
+ throw err;
489
+ }
490
+ try {
491
+ const buckets = yield this.getBucketList({ Prefix: this.bucket, BucketRegion: this.region });
492
+ (_c = this.logger) == null ? void 0 : _c.debug("bucketInfo", "getBucketList", { buckets });
493
+ const bucket = buckets == null ? void 0 : buckets.find((b) => b.Name === bucketName);
494
+ if (bucket == null ? void 0 : bucket.CreationDate) {
495
+ info.creationDate = bucket.CreationDate;
496
+ }
497
+ } catch (error) {
498
+ (_d = this.logger) == null ? void 0 : _d.warn(this.reqId, "Failed to get bucket creation date", { bucketName, error });
499
+ }
500
+ try {
501
+ const aclResponse = yield this.execute(
502
+ new GetBucketAclCommand({ Bucket: bucketName })
503
+ );
504
+ (_e = this.logger) == null ? void 0 : _e.debug("bucketInfo", "GetBucketAclCommandOutput", aclResponse);
505
+ info.acl = (_f = aclResponse.Grants) == null ? void 0 : _f.map((grant) => {
506
+ var _a3;
507
+ return {
508
+ grantee: (_a3 = grant.Grantee) == null ? void 0 : _a3.Type,
509
+ permission: grant.Permission
510
+ };
511
+ });
512
+ } catch (error) {
513
+ (_g = this.logger) == null ? void 0 : _g.warn(this.reqId, "Failed to get bucket ACL", { bucketName, error });
514
+ }
515
+ try {
516
+ const publicAccessResponse = yield this.execute(
517
+ new GetPublicAccessBlockCommand({ Bucket: bucketName })
518
+ );
519
+ (_h = this.logger) == null ? void 0 : _h.debug("bucketInfo", "GetPublicAccessBlockCommandOutput", publicAccessResponse);
520
+ info.publicAccessBlock = publicAccessResponse.PublicAccessBlockConfiguration;
521
+ } catch (error) {
522
+ if (error.name !== "NoSuchPublicAccessBlockConfiguration") {
523
+ (_i = this.logger) == null ? void 0 : _i.warn(this.reqId, "Failed to get public access block", { bucketName, error });
524
+ }
525
+ }
526
+ try {
527
+ const policyResponse = yield this.execute(
528
+ new GetBucketPolicyCommand({ Bucket: bucketName })
529
+ );
530
+ (_j = this.logger) == null ? void 0 : _j.debug("bucketInfo", "GetBucketPolicyCommandOutput", policyResponse);
531
+ if (policyResponse.Policy) {
532
+ info.policy = JSON.parse(policyResponse.Policy);
533
+ }
534
+ } catch (error) {
535
+ if (error.name !== "NoSuchBucketPolicy") {
536
+ (_k = this.logger) == null ? void 0 : _k.warn(this.reqId, "Failed to get bucket policy", { bucketName, error });
537
+ }
538
+ }
539
+ try {
540
+ const versioningResponse = yield this.execute(
541
+ new GetBucketVersioningCommand({ Bucket: bucketName })
542
+ );
543
+ (_l = this.logger) == null ? void 0 : _l.debug("bucketInfo", "GetBucketVersioningCommandOutput", versioningResponse);
544
+ info.versioning = versioningResponse.Status || "Disabled";
545
+ } catch (error) {
546
+ (_m = this.logger) == null ? void 0 : _m.warn(this.reqId, "Failed to get bucket versioning", { bucketName, error });
547
+ }
548
+ try {
549
+ const encryptionResponse = yield this.execute(
550
+ new GetBucketEncryptionCommand({ Bucket: bucketName })
551
+ );
552
+ (_n = this.logger) == null ? void 0 : _n.debug("bucketInfo", "GetBucketEncryptionCommandOutput", encryptionResponse);
553
+ info.encryption = {
554
+ enabled: true,
555
+ type: (_r = (_q = (_p = (_o = encryptionResponse.ServerSideEncryptionConfiguration) == null ? void 0 : _o.Rules) == null ? void 0 : _p[0]) == null ? void 0 : _q.ApplyServerSideEncryptionByDefault) == null ? void 0 : _r.SSEAlgorithm
556
+ };
557
+ } catch (error) {
558
+ if (error.name === "ServerSideEncryptionConfigurationNotFoundError") {
559
+ info.encryption = { enabled: false };
560
+ } else {
561
+ (_s = this.logger) == null ? void 0 : _s.warn(this.reqId, "Failed to get bucket encryption", { bucketName, error });
562
+ info.encryption = { enabled: false };
563
+ }
564
+ }
565
+ (_t = this.logger) == null ? void 0 : _t.debug("bucketInfo", "bucket info response", info);
566
+ return info;
567
+ } catch (error) {
568
+ (_u = this.logger) == null ? void 0 : _u.error(this.reqId, "Failed to get bucket info", { bucketName, error });
569
+ throw error;
570
+ }
571
+ });
572
+ }
573
+ destroyBucket(forceDeleteAllFilesBeforeDestroyBucket = false) {
574
+ return __async(this, null, function* () {
575
+ var _a2;
576
+ const bucketName = this.bucket;
577
+ const isExists = yield this.isBucketExists();
578
+ if (!isExists) {
579
+ (_a2 = this.logger) == null ? void 0 : _a2.debug(this.reqId, `Bucket not exists.`, { bucketName });
580
+ return;
581
+ }
582
+ if (forceDeleteAllFilesBeforeDestroyBucket) {
583
+ yield this.emptyBucket();
584
+ }
585
+ const createParams = { Bucket: bucketName };
586
+ const data = yield this.execute(new DeleteBucketCommand(createParams));
587
+ return data;
588
+ });
589
+ }
590
+ // ##### DIRECTORY BLOCK ##########################
591
+ createDirectory(directoryPath) {
592
+ return __async(this, null, function* () {
593
+ let normalizedPath = decodeURIComponent((directoryPath == null ? void 0 : directoryPath.replace(/^\//, "").replace(/\/$/, "")) || "");
594
+ if (!normalizedPath) throw new Error("No directory path provided");
595
+ if (normalizedPath === "/") normalizedPath = "";
596
+ const command = new PutObjectCommand({ Bucket: this.bucket, Key: `${normalizedPath}/` });
597
+ const result = yield this.execute(command);
598
+ return result;
599
+ });
600
+ }
601
+ deleteDirectory(directoryPath) {
602
+ return __async(this, null, function* () {
603
+ var _a2, _b, _c, _d, _e, _f, _g;
604
+ let normalizedPath = decodeURIComponent((directoryPath == null ? void 0 : directoryPath.replace(/^\//, "").replace(/\/$/, "")) || "");
605
+ if (!normalizedPath) {
606
+ throw new Error("No directory path provided");
607
+ }
608
+ if (normalizedPath === "/") normalizedPath = "";
609
+ let totalDeletedCount = 0;
610
+ let ContinuationToken = void 0;
611
+ do {
612
+ const listResp = yield this.execute(
613
+ new ListObjectsV2Command({
614
+ Bucket: this.bucket,
615
+ Prefix: normalizedPath,
616
+ ContinuationToken
617
+ })
618
+ );
619
+ if (listResp.Contents && listResp.Contents.length > 0) {
620
+ const deleteResult = yield this.execute(
621
+ new DeleteObjectsCommand({
622
+ Bucket: this.bucket,
623
+ Delete: {
624
+ Objects: listResp.Contents.map((obj) => ({ Key: obj.Key })),
625
+ Quiet: false
626
+ }
627
+ })
628
+ );
629
+ totalDeletedCount += (_b = (_a2 = deleteResult.Deleted) == null ? void 0 : _a2.length) != null ? _b : 0;
630
+ if (deleteResult.Errors && deleteResult.Errors.length > 0) {
631
+ (_c = this.logger) == null ? void 0 : _c.warn(this.reqId, `Some objects failed to delete`, {
632
+ directoryPath: normalizedPath,
633
+ errors: deleteResult.Errors
634
+ });
635
+ }
636
+ }
637
+ ContinuationToken = listResp.NextContinuationToken;
638
+ } while (ContinuationToken);
639
+ if (totalDeletedCount === 0) {
640
+ const directoryExists = yield this.fileExists(normalizedPath);
641
+ if (!directoryExists) {
642
+ (_d = this.logger) == null ? void 0 : _d.debug(this.reqId, `Directory not found`, { directoryPath: normalizedPath });
643
+ return null;
644
+ }
645
+ }
646
+ try {
647
+ yield this.execute(
648
+ new DeleteObjectCommand({
649
+ Bucket: this.bucket,
650
+ Key: normalizedPath
651
+ })
652
+ );
653
+ totalDeletedCount++;
654
+ } catch (error) {
655
+ if (error.name !== "NotFound" && ((_e = error.$metadata) == null ? void 0 : _e.httpStatusCode) !== 404) {
656
+ (_f = this.logger) == null ? void 0 : _f.warn(this.reqId, `Failed to delete directory marker`, {
657
+ directoryPath: normalizedPath,
658
+ error
659
+ });
660
+ }
661
+ }
662
+ (_g = this.logger) == null ? void 0 : _g.info(this.reqId, `Directory deleted successfully`, {
663
+ directoryPath: normalizedPath,
664
+ deletedCount: totalDeletedCount
665
+ });
666
+ return {
667
+ Deleted: [{ Key: normalizedPath }],
668
+ $metadata: {}
669
+ };
670
+ });
671
+ }
672
+ directoryList(directoryPath) {
673
+ return __async(this, null, function* () {
674
+ let normalizedPath = decodeURIComponent((directoryPath == null ? void 0 : directoryPath.replace(/^\//, "").replace(/\/$/, "")) || "");
675
+ if (directoryPath !== "/" && directoryPath !== "" && directoryPath !== void 0) normalizedPath += "/";
676
+ else normalizedPath = "";
677
+ const result = yield this.execute(
678
+ new ListObjectsV2Command({
679
+ Bucket: this.bucket,
680
+ Prefix: normalizedPath,
681
+ Delimiter: "/"
682
+ })
683
+ );
684
+ const directories = (result.CommonPrefixes || []).map((prefix) => prefix.Prefix).map((prefix) => {
685
+ const relativePath = prefix.replace(normalizedPath, "");
686
+ const dir = relativePath.replace(/\/$/, "");
687
+ return dir;
688
+ }).filter((dir) => dir);
689
+ const files = (result.Contents || []).filter((content) => {
690
+ var _a2;
691
+ return content.Key !== normalizedPath && !((_a2 = content.Key) == null ? void 0 : _a2.endsWith("/"));
692
+ }).map((content) => __spreadProps(__spreadValues({}, content), {
693
+ Name: content.Key.replace(normalizedPath, "") || content.Key,
694
+ LastModified: new Date(content.LastModified)
695
+ }));
696
+ return { directories, files };
697
+ });
698
+ }
699
+ /**
700
+ * Get all files recursively (example for search/indexing)
701
+ * @param directoryPath
702
+ */
703
+ directoryListRecursive(directoryPath) {
704
+ return __async(this, null, function* () {
705
+ let normalizedPath = decodeURIComponent((directoryPath == null ? void 0 : directoryPath.replace(/^\//, "").replace(/\/$/, "")) || "");
706
+ if (directoryPath !== "/" && directoryPath !== "" && directoryPath !== void 0) normalizedPath += "/";
707
+ else normalizedPath = "";
708
+ const allDirectories = [];
709
+ const allFiles = [];
710
+ let ContinuationToken = void 0;
711
+ do {
712
+ const command = new ListObjectsV2Command({
713
+ Bucket: this.bucket,
714
+ Prefix: normalizedPath,
715
+ ContinuationToken
716
+ });
717
+ const result = yield this.execute(command);
718
+ if (result.Contents) {
719
+ for (const content of result.Contents) {
720
+ const fullPath = content.Key;
721
+ const relativePath = fullPath.replace(normalizedPath, "");
722
+ const filename = fullPath.split("/").pop();
723
+ if (fullPath.endsWith("/")) {
724
+ allDirectories.push(relativePath.slice(0, -1));
725
+ } else {
726
+ allFiles.push(__spreadProps(__spreadValues({}, content), {
727
+ Name: filename,
728
+ Path: fullPath,
729
+ LastModified: new Date(content.LastModified)
730
+ }));
731
+ }
732
+ }
733
+ }
734
+ ContinuationToken = result.NextContinuationToken;
735
+ } while (ContinuationToken);
736
+ return { directories: allDirectories, files: allFiles };
737
+ });
738
+ }
739
+ /**
740
+ * Get tree files recursively (example for build file explorer UI)
741
+ * @param directoryPath - the directory start from
742
+ * @example
743
+ * const tree = await s3Util.getDirectoryTree('uploads');
744
+ * // {
745
+ * // name: 'uploads',
746
+ * // path: 'uploads/',
747
+ * // type: 'directory',
748
+ * // children: [
749
+ * // {
750
+ * // name: 'logo.png',
751
+ * // path: 'uploads/logo.png',
752
+ * // type: 'file',
753
+ * // size: 12345,
754
+ * // lastModified: Date
755
+ * // },
756
+ * // {
757
+ * // name: 'images',
758
+ * // path: 'uploads/images/',
759
+ * // type: 'directory',
760
+ * // children: [
761
+ * // { name: 'photo1.jpg', type: 'file', ... },
762
+ * // { name: 'photo2.jpg', type: 'file', ... }
763
+ * // ]
764
+ * // }
765
+ * // ]
766
+ * // }
767
+ */
768
+ directoryTree(directoryPath) {
769
+ return __async(this, null, function* () {
770
+ let normalizedPath = decodeURIComponent((directoryPath == null ? void 0 : directoryPath.replace(/^\//, "").replace(/\/$/, "")) || "");
771
+ const lastDirectory = directoryPath == null ? void 0 : directoryPath.split("/").pop();
772
+ const { directories, files } = yield this.directoryList(normalizedPath);
773
+ if (directoryPath !== "/" && directoryPath !== "" && directoryPath !== void 0) normalizedPath += "/";
774
+ else normalizedPath = "";
775
+ const treeNode = {
776
+ path: "/" + normalizedPath,
777
+ name: lastDirectory || this.bucket,
778
+ type: "directory",
779
+ children: []
780
+ };
781
+ for (const file of files) {
782
+ treeNode.children.push({
783
+ path: "/" + file.Key,
784
+ name: file.Name,
785
+ type: "file",
786
+ size: file.Size,
787
+ lastModified: file.LastModified
788
+ });
789
+ }
790
+ for (const dir of directories) {
791
+ const subPath = treeNode.path + dir;
792
+ const subTree = yield this.directoryTree(subPath);
793
+ treeNode.children.push(subTree);
794
+ }
795
+ return treeNode;
796
+ });
797
+ }
798
+ // ##### FILES BLOCK ##########################
799
+ fileInfo(filePath) {
800
+ return __async(this, null, function* () {
801
+ const normalizedKey = decodeURIComponent((filePath == null ? void 0 : filePath.replace(/^\//, "").replace(/\/$/, "")) || "");
802
+ if (!normalizedKey || normalizedKey === "/") {
803
+ throw new Error("No file key provided");
804
+ }
805
+ const command = new HeadObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
806
+ return yield this.execute(command);
807
+ });
808
+ }
809
+ fileListInfo(directoryPath, fileNamePrefix) {
810
+ return __async(this, null, function* () {
811
+ var _a2, _b;
812
+ let normalizedPath = decodeURIComponent((directoryPath == null ? void 0 : directoryPath.replace(/^\//, "").replace(/\/$/, "")) || "");
813
+ if (directoryPath !== "/" && directoryPath !== "" && directoryPath !== void 0) normalizedPath += "/";
814
+ else normalizedPath = "";
815
+ const prefix = normalizedPath + (fileNamePrefix || "");
816
+ const command = new ListObjectsCommand({
817
+ Bucket: this.bucket,
818
+ Prefix: prefix,
819
+ Delimiter: "/"
820
+ });
821
+ const result = yield this.execute(command);
822
+ const files = ((_a2 = result.Contents) != null ? _a2 : []).filter((v) => v).map(
823
+ (content) => {
824
+ var _a3, _b2;
825
+ return __spreadProps(__spreadValues({}, content), {
826
+ Name: (_b2 = (_a3 = content.Key) == null ? void 0 : _a3.replace(prefix, "")) != null ? _b2 : content.Key,
827
+ LastModified: content.LastModified ? new Date(content.LastModified) : null
828
+ });
829
+ }
830
+ ).filter((content) => content.Name);
831
+ (_b = this.logger) == null ? void 0 : _b.debug(null, "file list info", { prefix, files });
832
+ return files;
833
+ });
834
+ }
835
+ taggingFile(filePath, tagVersion = "1.0.0") {
836
+ return __async(this, null, function* () {
837
+ try {
838
+ const normalizedKey = decodeURIComponent((filePath == null ? void 0 : filePath.replace(/^\//, "").replace(/\/$/, "")) || "");
839
+ if (!normalizedKey || normalizedKey === "/") {
840
+ throw new Error("No file key provided");
841
+ }
842
+ const command = new PutObjectTaggingCommand({
843
+ Bucket: this.bucket,
844
+ Key: normalizedKey,
845
+ Tagging: { TagSet: [{ Key: "version", Value: tagVersion }] }
846
+ });
847
+ yield this.execute(command);
848
+ return true;
849
+ } catch (e) {
850
+ return false;
851
+ }
852
+ });
853
+ }
854
+ fileVersion(filePath) {
855
+ return __async(this, null, function* () {
856
+ var _a2, _b;
857
+ const normalizedKey = decodeURIComponent((filePath == null ? void 0 : filePath.replace(/^\//, "").replace(/\/$/, "")) || "");
858
+ if (!normalizedKey || normalizedKey === "/") {
859
+ throw new Error("No file key provided");
860
+ }
861
+ const command = new GetObjectTaggingCommand({ Bucket: this.bucket, Key: normalizedKey });
862
+ const result = yield this.execute(command);
863
+ const tag = (_a2 = result.TagSet) == null ? void 0 : _a2.find((tag2) => tag2.Key === "version");
864
+ return (_b = tag == null ? void 0 : tag.Value) != null ? _b : "";
865
+ });
866
+ }
867
+ fileUrl(filePath, expiresIn = "15m") {
868
+ return __async(this, null, function* () {
869
+ var _a2;
870
+ const normalizedKey = decodeURIComponent((filePath == null ? void 0 : filePath.replace(/^\//, "").replace(/\/$/, "")) || "");
871
+ if (!normalizedKey || normalizedKey === "/") {
872
+ throw new Error("No file key provided");
873
+ }
874
+ const expiresInSeconds = typeof expiresIn === "number" ? expiresIn : ms(expiresIn) / 1e3;
875
+ const command = new GetObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
876
+ const url = yield getSignedUrl(this.s3Client, command, {
877
+ expiresIn: expiresInSeconds
878
+ // is using 3600 it's will expire in 1 hour (default is 900 seconds = 15 minutes)
879
+ });
880
+ (_a2 = this.logger) == null ? void 0 : _a2.info(null, "generate signed file url", { url, filePath: normalizedKey, expiresIn });
881
+ return url;
882
+ });
883
+ }
884
+ sizeOf(filePath, unit = "bytes") {
885
+ return __async(this, null, function* () {
886
+ var _a2, _b, _c;
887
+ const normalizedKey = decodeURIComponent((filePath == null ? void 0 : filePath.replace(/^\//, "").replace(/\/$/, "")) || "");
888
+ if (!normalizedKey || normalizedKey === "/") {
889
+ throw new Error("No file key provided");
890
+ }
891
+ try {
892
+ const command = new HeadObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
893
+ const headObject = yield this.execute(command);
894
+ const bytes2 = (_a2 = headObject.ContentLength) != null ? _a2 : 0;
895
+ switch (unit) {
896
+ case "KB":
897
+ return bytes2 / 1024;
898
+ case "MB":
899
+ return bytes2 / (1024 * 1024);
900
+ case "GB":
901
+ return bytes2 / (1024 * 1024 * 1024);
902
+ default:
903
+ return bytes2;
904
+ }
905
+ } catch (error) {
906
+ if (error.name === "NotFound" || ((_b = error.$metadata) == null ? void 0 : _b.httpStatusCode) === 404) {
907
+ (_c = this.logger) == null ? void 0 : _c.warn(this.reqId, "File not found", { filePath: normalizedKey });
908
+ return 0;
909
+ }
910
+ throw error;
911
+ }
912
+ });
913
+ }
914
+ fileExists(filePath) {
915
+ return __async(this, null, function* () {
916
+ var _a2;
917
+ try {
918
+ const normalizedKey = decodeURIComponent((filePath == null ? void 0 : filePath.replace(/^\//, "").replace(/\/$/, "")) || "");
919
+ if (!normalizedKey || normalizedKey === "/") {
920
+ throw new Error("No file key provided");
921
+ }
922
+ const command = new HeadObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
923
+ yield this.execute(command);
924
+ return true;
925
+ } catch (error) {
926
+ if (error.name === "NotFound" || ((_a2 = error.$metadata) == null ? void 0 : _a2.httpStatusCode) === 404) {
927
+ return false;
928
+ }
929
+ throw error;
930
+ }
931
+ });
932
+ }
933
+ fileContent(filePath, format = "buffer") {
934
+ return __async(this, null, function* () {
935
+ const normalizedKey = decodeURIComponent((filePath == null ? void 0 : filePath.replace(/^\//, "").replace(/\/$/, "")) || "");
936
+ if (!normalizedKey || normalizedKey === "/") {
937
+ throw new Error("No file key provided");
938
+ }
939
+ const command = new GetObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
940
+ const result = yield this.execute(command);
941
+ if (!result.Body) {
942
+ throw new Error("File body is empty");
943
+ }
944
+ const stream = result.Body;
945
+ const chunks = [];
946
+ try {
947
+ for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield iter.next()).done; more = false) {
948
+ const chunk = temp.value;
949
+ chunks.push(chunk);
950
+ }
951
+ } catch (temp) {
952
+ error = [temp];
953
+ } finally {
954
+ try {
955
+ more && (temp = iter.return) && (yield temp.call(iter));
956
+ } finally {
957
+ if (error)
958
+ throw error[0];
959
+ }
960
+ }
961
+ const buffer = Buffer2.concat(chunks);
962
+ if (format === "base64" || format === "utf8") {
963
+ return buffer.toString(format);
964
+ }
965
+ return buffer;
966
+ });
967
+ }
968
+ uploadFile(_0, _1) {
969
+ return __async(this, arguments, function* (filePath, fileData, acl = "private" /* private */, version = "1.0.0") {
970
+ const normalizedKey = decodeURIComponent((filePath == null ? void 0 : filePath.replace(/^\//, "").replace(/\/$/, "")) || "");
971
+ if (!normalizedKey || normalizedKey === "/") {
972
+ throw new Error("No file key provided");
973
+ }
974
+ const upload = new Upload({
975
+ client: this.s3Client,
976
+ params: {
977
+ Bucket: this.bucket,
978
+ ACL: acl,
979
+ Key: normalizedKey,
980
+ Body: fileData,
981
+ Tagging: `version=${version}`
982
+ }
983
+ });
984
+ const result = yield upload.done();
985
+ return {
986
+ Bucket: this.bucket,
987
+ Key: normalizedKey,
988
+ Location: `https://${this.bucket}.s3.amazonaws.com/${normalizedKey}`,
989
+ test: `${this.link}/${normalizedKey}`,
990
+ ETag: result.ETag
991
+ };
992
+ });
993
+ }
994
+ deleteFile(filePath) {
995
+ return __async(this, null, function* () {
996
+ const normalizedKey = decodeURIComponent((filePath == null ? void 0 : filePath.replace(/^\//, "").replace(/\/$/, "")) || "");
997
+ if (!normalizedKey || normalizedKey === "/") {
998
+ throw new Error("No file key provided");
999
+ }
1000
+ const command = new DeleteObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
1001
+ return yield this.execute(command);
1002
+ });
1003
+ }
1004
+ // ##### STREAMING BLOCK ##########################
1005
+ streamObjectFile(_0) {
1006
+ return __async(this, arguments, function* (filePath, {
1007
+ Range,
1008
+ checkFileExists = true,
1009
+ abortSignal
1010
+ } = {}) {
1011
+ const normalizedKey = decodeURIComponent((filePath == null ? void 0 : filePath.replace(/^\//, "").replace(/\/$/, "")) || "");
1012
+ if (!normalizedKey || normalizedKey === "/") {
1013
+ throw new Error("No file key provided");
1014
+ }
1015
+ if (checkFileExists) {
1016
+ const isExists = yield this.fileExists(normalizedKey);
1017
+ if (!isExists) return null;
1018
+ }
1019
+ const command = new GetObjectCommand(__spreadValues({
1020
+ Bucket: this.bucket,
1021
+ Key: normalizedKey
1022
+ }, Range ? { Range } : {}));
1023
+ const response = yield this.execute(command, { abortSignal });
1024
+ if (!response.Body || !(response.Body instanceof Readable)) {
1025
+ throw new Error("Invalid response body: not a Readable stream");
1026
+ }
1027
+ return response.Body;
1028
+ });
1029
+ }
1030
+ streamVideoFile(_0) {
1031
+ return __async(this, arguments, function* ({
1032
+ filePath,
1033
+ Range,
1034
+ abortSignal
1035
+ }) {
1036
+ var _a2;
1037
+ const normalizedKey = decodeURIComponent((filePath == null ? void 0 : filePath.replace(/^\//, "").replace(/\/$/, "")) || "");
1038
+ if (!normalizedKey || normalizedKey === "/") {
1039
+ throw new Error("No file key provided");
1040
+ }
1041
+ try {
1042
+ const cmd = new GetObjectCommand(__spreadValues({
1043
+ Bucket: this.bucket,
1044
+ Key: normalizedKey
1045
+ }, Range ? { Range } : {}));
1046
+ const data = yield s3Limiter(() => this.execute(cmd, { abortSignal }));
1047
+ const body = data.Body;
1048
+ if (!body) return null;
1049
+ return {
1050
+ body,
1051
+ meta: {
1052
+ contentType: data.ContentType,
1053
+ contentLength: data.ContentLength,
1054
+ contentRange: data.ContentRange,
1055
+ acceptRanges: data.AcceptRanges,
1056
+ etag: data.ETag,
1057
+ lastModified: data.LastModified
1058
+ }
1059
+ };
1060
+ } catch (error) {
1061
+ (_a2 = this.logger) == null ? void 0 : _a2.warn(this.reqId, "getS3VideoStream error", {
1062
+ Bucket: this.bucket,
1063
+ filePath: normalizedKey,
1064
+ Range,
1065
+ error
1066
+ });
1067
+ return null;
1068
+ }
1069
+ });
1070
+ }
1071
+ getStreamZipFileCtr(_0) {
1072
+ return __async(this, arguments, function* ({
1073
+ filePath,
1074
+ filename: _filename,
1075
+ compressionLevel = 5
1076
+ }) {
1077
+ return (req, res, next) => __async(this, null, function* () {
1078
+ var _a2, _b, _c, _d, _e;
1079
+ const filePaths = [].concat(filePath).map((filePath2) => {
1080
+ return decodeURIComponent((filePath2 == null ? void 0 : filePath2.replace(/^\//, "").replace(/\/$/, "")) || "");
1081
+ }).filter((v) => v && v !== "/");
1082
+ if (!filePaths.length) {
1083
+ throw new Error("No file keys provided");
1084
+ }
1085
+ let filename = _filename || (/* @__PURE__ */ new Date()).toISOString();
1086
+ filename = filename.endsWith(".zip") ? filename : `${filename}.zip`;
1087
+ const abort = new AbortController();
1088
+ const onClose = () => {
1089
+ abort.abort();
1090
+ };
1091
+ req.once("close", onClose);
1092
+ try {
1093
+ (_a2 = this.logger) == null ? void 0 : _a2.info(this.reqId, "Starting parallel file download...", { fileCount: filePaths.length });
1094
+ const downloadPromises = filePaths.map((filePath2) => __async(this, null, function* () {
1095
+ var _a3, _b2, _c2;
1096
+ try {
1097
+ if (abort.signal.aborted) return null;
1098
+ const stream = yield this.streamObjectFile(filePath2, { abortSignal: abort.signal });
1099
+ if (!stream) {
1100
+ (_a3 = this.logger) == null ? void 0 : _a3.warn(this.reqId, "File not found", { filePath: filePath2 });
1101
+ return null;
1102
+ }
1103
+ const chunks = [];
1104
+ try {
1105
+ for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield iter.next()).done; more = false) {
1106
+ const chunk = temp.value;
1107
+ if (abort.signal.aborted) {
1108
+ stream.destroy();
1109
+ return null;
1110
+ }
1111
+ chunks.push(Buffer2.from(chunk));
1112
+ }
1113
+ } catch (temp) {
1114
+ error = [temp];
1115
+ } finally {
1116
+ try {
1117
+ more && (temp = iter.return) && (yield temp.call(iter));
1118
+ } finally {
1119
+ if (error)
1120
+ throw error[0];
1121
+ }
1122
+ }
1123
+ const buffer = Buffer2.concat(chunks);
1124
+ const fileName = filePath2.split("/").pop() || filePath2;
1125
+ (_b2 = this.logger) == null ? void 0 : _b2.debug(this.reqId, "File downloaded", {
1126
+ filePath: filePath2,
1127
+ sizeMB: (buffer.length / (1024 * 1024)).toFixed(2)
1128
+ });
1129
+ return { buffer, name: fileName, path: filePath2 };
1130
+ } catch (error2) {
1131
+ (_c2 = this.logger) == null ? void 0 : _c2.warn(this.reqId, "Failed to download file", { filePath: filePath2, error: error2 });
1132
+ return null;
1133
+ }
1134
+ }));
1135
+ const fileBuffers = (yield Promise.all(downloadPromises)).filter(Boolean);
1136
+ if (abort.signal.aborted || fileBuffers.length === 0) {
1137
+ req.off("close", onClose);
1138
+ if (fileBuffers.length === 0) {
1139
+ next(new Error("No files available to zip"));
1140
+ }
1141
+ return;
1142
+ }
1143
+ (_b = this.logger) == null ? void 0 : _b.info(this.reqId, "All files downloaded, measuring zip size...", {
1144
+ fileCount: fileBuffers.length,
1145
+ totalSizeMB: (fileBuffers.reduce((sum, f) => sum + f.buffer.length, 0) / (1024 * 1024)).toFixed(2)
1146
+ });
1147
+ const measureArchive = archiver("zip", { zlib: { level: compressionLevel } });
1148
+ let actualZipSize = 0;
1149
+ measureArchive.on("data", (chunk) => {
1150
+ actualZipSize += chunk.length;
1151
+ });
1152
+ for (const file of fileBuffers) {
1153
+ if (abort.signal.aborted) break;
1154
+ measureArchive.append(file.buffer, { name: file.name });
1155
+ }
1156
+ yield measureArchive.finalize();
1157
+ if (abort.signal.aborted) {
1158
+ req.off("close", onClose);
1159
+ return;
1160
+ }
1161
+ (_c = this.logger) == null ? void 0 : _c.info(this.reqId, "Zip size calculated", {
1162
+ actualZipSize,
1163
+ sizeMB: (actualZipSize / (1024 * 1024)).toFixed(2)
1164
+ });
1165
+ const actualArchive = archiver("zip", { zlib: { level: compressionLevel } });
1166
+ res.setHeader("Content-Type", "application/zip");
1167
+ res.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
1168
+ res.setHeader("Content-Length", String(actualZipSize));
1169
+ res.setHeader("Access-Control-Expose-Headers", "Content-Type, Content-Disposition, Content-Length");
1170
+ actualArchive.on("error", (err) => {
1171
+ var _a3;
1172
+ (_a3 = this.logger) == null ? void 0 : _a3.error(this.reqId, "Archive error", { error: err });
1173
+ abort.abort();
1174
+ if (!res.headersSent) {
1175
+ next(err);
1176
+ }
1177
+ });
1178
+ actualArchive.pipe(res);
1179
+ for (const file of fileBuffers) {
1180
+ if (abort.signal.aborted) break;
1181
+ actualArchive.append(file.buffer, { name: file.name });
1182
+ }
1183
+ yield actualArchive.finalize();
1184
+ (_d = this.logger) == null ? void 0 : _d.info(this.reqId, "Zip download completed", {
1185
+ fileCount: fileBuffers.length,
1186
+ totalSize: actualZipSize
1187
+ });
1188
+ req.off("close", onClose);
1189
+ } catch (error) {
1190
+ abort.abort();
1191
+ 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";
1192
+ if (isBenignError) {
1193
+ return;
1194
+ }
1195
+ if (!res.headersSent) {
1196
+ (_e = this.logger) == null ? void 0 : _e.error(this.reqId, "Failed to create zip archive", { error });
1197
+ next(error);
1198
+ } else if (!res.writableEnded) {
1199
+ try {
1200
+ res.end();
1201
+ } catch (e) {
1202
+ }
1203
+ }
1204
+ } finally {
1205
+ req.off("close", onClose);
1206
+ }
1207
+ });
1208
+ });
1209
+ }
1210
+ getStreamFileCtrl(_0) {
1211
+ return __async(this, arguments, function* ({ filePath, filename }) {
1212
+ return (req, res, next) => __async(this, null, function* () {
1213
+ var _a2, _b;
1214
+ const abort = new AbortController();
1215
+ let stream = null;
1216
+ const onClose = () => {
1217
+ var _a3;
1218
+ abort.abort();
1219
+ (_a3 = stream == null ? void 0 : stream.destroy) == null ? void 0 : _a3.call(stream);
1220
+ };
1221
+ req.once("close", onClose);
1222
+ const normalizedKey = decodeURIComponent((filePath == null ? void 0 : filePath.replace(/^\//, "").replace(/\/$/, "")) || "");
1223
+ if (!normalizedKey || normalizedKey === "/") {
1224
+ throw new Error("No file key provided");
1225
+ }
1226
+ try {
1227
+ const isExists = yield this.fileExists(normalizedKey);
1228
+ if (!isExists) {
1229
+ req.off("close", onClose);
1230
+ next(Error(`File not found: "${normalizedKey}"`));
1231
+ return;
1232
+ }
1233
+ stream = yield this.streamObjectFile(normalizedKey, {
1234
+ abortSignal: abort.signal,
1235
+ checkFileExists: false
1236
+ });
1237
+ if (!stream) {
1238
+ req.off("close", onClose);
1239
+ next(Error(`Failed to get file stream: "${normalizedKey}"`));
1240
+ return;
1241
+ }
1242
+ const fileInfo = yield this.fileInfo(normalizedKey);
1243
+ const fileName = filename || normalizedKey.split("/").pop() || "download";
1244
+ res.setHeader("Content-Type", fileInfo.ContentType || "application/octet-stream");
1245
+ res.setHeader("Content-Disposition", `attachment; filename="${fileName}"`);
1246
+ if (fileInfo.ContentLength) {
1247
+ res.setHeader("Content-Length", String(fileInfo.ContentLength));
1248
+ }
1249
+ stream.on("error", (err) => {
1250
+ var _a3, _b2;
1251
+ (_a3 = this.logger) == null ? void 0 : _a3.warn(this.reqId, "Stream error", { filePath: normalizedKey, error: err });
1252
+ abort.abort();
1253
+ (_b2 = stream == null ? void 0 : stream.destroy) == null ? void 0 : _b2.call(stream);
1254
+ });
1255
+ res.once("close", () => {
1256
+ var _a3;
1257
+ (_a3 = stream == null ? void 0 : stream.destroy) == null ? void 0 : _a3.call(stream);
1258
+ req.off("close", onClose);
1259
+ });
1260
+ yield pump(stream, res);
1261
+ req.off("close", onClose);
1262
+ } catch (error) {
1263
+ abort.abort();
1264
+ if (stream) {
1265
+ (_a2 = stream.destroy) == null ? void 0 : _a2.call(stream);
1266
+ }
1267
+ 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";
1268
+ if (isBenignStreamError) {
1269
+ return;
1270
+ }
1271
+ (_b = this.logger) == null ? void 0 : _b.error(this.reqId, "Failed to stream file", { filePath: normalizedKey, error });
1272
+ if (!res.headersSent) {
1273
+ next(error);
1274
+ } else if (!res.writableEnded) {
1275
+ try {
1276
+ res.end();
1277
+ } catch (e) {
1278
+ }
1279
+ }
1280
+ } finally {
1281
+ req.off("close", onClose);
1282
+ }
1283
+ });
1284
+ });
1285
+ }
1286
+ getStreamVideoFileCtrl(_0) {
1287
+ return __async(this, arguments, function* ({
1288
+ fileKey,
1289
+ allowedWhitelist,
1290
+ contentType = "video/mp4",
1291
+ streamTimeoutMS = 3e4,
1292
+ bufferMB = 5
1293
+ }) {
1294
+ return (req, res, next) => __async(this, null, function* () {
1295
+ var _a2, _b, _c, _d, _e, _f;
1296
+ const normalizedKey = decodeURIComponent((fileKey == null ? void 0 : fileKey.replace(/^\//, "").replace(/\/$/, "")) || "");
1297
+ if (!normalizedKey || normalizedKey === "/") {
1298
+ throw new Error("No file key provided");
1299
+ }
1300
+ const isExists = yield this.fileExists(normalizedKey);
1301
+ const fileSize = yield this.sizeOf(normalizedKey);
1302
+ let Range;
1303
+ if (!isExists) {
1304
+ next(Error(`File does not exist: "${normalizedKey}"`));
1305
+ return;
1306
+ }
1307
+ try {
1308
+ if (req.method === "HEAD") {
1309
+ res.setHeader("Content-Type", contentType);
1310
+ res.setHeader("Accept-Ranges", "bytes");
1311
+ if (fileSize) res.setHeader("Content-Length", String(fileSize));
1312
+ return res.status(200).end();
1313
+ }
1314
+ const bufferSize = bufferMB;
1315
+ const CHUNK_SIZE = __pow(10, 6) * bufferSize;
1316
+ const rangeValues = parseRangeHeader(req.headers.range, fileSize, CHUNK_SIZE);
1317
+ let [start, end] = rangeValues || [];
1318
+ if (!rangeValues || start < 0 || start >= fileSize || end < 0 || end >= fileSize || start > end) {
1319
+ res.status(416).send("Requested Range Not Satisfiable");
1320
+ return;
1321
+ }
1322
+ res.statusCode = 206;
1323
+ const chunkLength = end - start + 1;
1324
+ res.setHeader("Content-Length", chunkLength);
1325
+ res.setHeader("Content-Range", `bytes ${start}-${end}/${fileSize}`);
1326
+ res.setHeader("Accept-Ranges", "bytes");
1327
+ res.setHeader("Content-Type", "video/mp4");
1328
+ Range = `bytes=${start}-${end}`;
1329
+ } catch (error) {
1330
+ next(error);
1331
+ return;
1332
+ }
1333
+ const abort = new AbortController();
1334
+ const onClose = () => abort.abort();
1335
+ req.once("close", onClose);
1336
+ try {
1337
+ const result = yield this.streamVideoFile({
1338
+ filePath: normalizedKey,
1339
+ Range,
1340
+ abortSignal: abort.signal
1341
+ });
1342
+ const { body, meta } = result;
1343
+ const origin = Array.isArray(allowedWhitelist) ? allowedWhitelist.includes((_a2 = req.headers.origin) != null ? _a2 : "") ? req.headers.origin : void 0 : allowedWhitelist;
1344
+ if (origin) {
1345
+ res.setHeader("Access-Control-Allow-Origin", origin);
1346
+ res.setHeader("Vary", "Origin");
1347
+ }
1348
+ const finalContentType = contentType.startsWith("video/") ? contentType : `video/${contentType}`;
1349
+ res.setHeader("Content-Type", (_b = meta.contentType) != null ? _b : finalContentType);
1350
+ res.setHeader("Accept-Ranges", (_c = meta.acceptRanges) != null ? _c : "bytes");
1351
+ if (Range && meta.contentRange) {
1352
+ res.status(206);
1353
+ res.setHeader("Content-Range", meta.contentRange);
1354
+ if (typeof meta.contentLength === "number") {
1355
+ res.setHeader("Content-Length", String(meta.contentLength));
1356
+ }
1357
+ } else if (fileSize) {
1358
+ res.setHeader("Content-Length", String(fileSize));
1359
+ }
1360
+ if (meta.etag) res.setHeader("ETag", meta.etag);
1361
+ if (meta.lastModified) res.setHeader("Last-Modified", meta.lastModified.toUTCString());
1362
+ const timeout = setTimeout(() => {
1363
+ abort.abort();
1364
+ if (!res.headersSent) res.status(504);
1365
+ res.end();
1366
+ }, streamTimeoutMS);
1367
+ res.once("close", () => {
1368
+ var _a3;
1369
+ clearTimeout(timeout);
1370
+ (_a3 = body.destroy) == null ? void 0 : _a3.call(body);
1371
+ req.off("close", onClose);
1372
+ });
1373
+ yield pump(body, res);
1374
+ clearTimeout(timeout);
1375
+ } catch (error) {
1376
+ 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";
1377
+ if (isBenignStreamError) {
1378
+ return;
1379
+ }
1380
+ if (!res.headersSent) {
1381
+ (_f = this.logger) == null ? void 0 : _f.warn(req.id, "caught exception in stream controller", {
1382
+ error: (_d = error == null ? void 0 : error.message) != null ? _d : String(error),
1383
+ key: fileKey,
1384
+ url: req.originalUrl,
1385
+ userId: (_e = req.user) == null ? void 0 : _e._id
1386
+ });
1387
+ next(error);
1388
+ return;
1389
+ }
1390
+ if (!res.writableEnded) {
1391
+ try {
1392
+ res.end();
1393
+ } catch (e) {
1394
+ }
1395
+ }
1396
+ return;
1397
+ }
1398
+ });
1399
+ });
1400
+ }
1401
+ static fileFilter(types, fileExt) {
1402
+ const fileTypesChecker = (fileExt == null ? void 0 : fileExt.length) ? new RegExp(`\\.(${fileExt.join("|")})$`, "i") : void 0;
1403
+ return function(_req, file, cb) {
1404
+ const fileExtension = path.extname(file.originalname).substring(1);
1405
+ const extname = fileTypesChecker ? fileTypesChecker.test(`.${fileExtension}`) : true;
1406
+ const mimeType = (types == null ? void 0 : types.length) ? types.some((type) => file.mimetype.startsWith(`${type}/`)) : true;
1407
+ if (mimeType && extname) {
1408
+ return cb(null, true);
1409
+ }
1410
+ const errorMsg = !extname ? `Upload File Ext Error: Allowed extensions: [${fileExt == null ? void 0 : fileExt.join(", ")}]. Got: ${fileExtension}` : `Upload File Type Error: Allowed types: [${types == null ? void 0 : types.join(", ")}]. Got: ${file.mimetype}`;
1411
+ return cb(new Error(errorMsg));
1412
+ };
1413
+ }
1414
+ getFileSize(maxFileSize) {
1415
+ var _a2;
1416
+ const fileSizeUnitValue = maxFileSize != null ? maxFileSize : this.maxUploadFileSizeRestriction;
1417
+ const fileSize = typeof fileSizeUnitValue === "number" ? fileSizeUnitValue : bytes(fileSizeUnitValue);
1418
+ if (!fileSize) {
1419
+ (_a2 = this.logger) == null ? void 0 : _a2.warn(this.reqId, "Failed to convert fileSize restriction, proceeding without limit", {
1420
+ maxFileSize,
1421
+ maxUploadFileSizeRestriction: this.maxUploadFileSizeRestriction
1422
+ });
1423
+ return void 0;
1424
+ }
1425
+ return fileSize;
1426
+ }
1427
+ getUploadFileMW(directory, {
1428
+ acl = "private" /* private */,
1429
+ maxFileSize,
1430
+ filename: _filename,
1431
+ fileType = [],
1432
+ fileExt = [],
1433
+ metadata: customMetadata
1434
+ } = {}) {
1435
+ let normalizedPath = decodeURIComponent((directory == null ? void 0 : directory.replace(/^\//, "").replace(/\/$/, "")) || "");
1436
+ if (directory !== "/" && directory !== "" && directory !== void 0) normalizedPath += "/";
1437
+ else normalizedPath = "";
1438
+ const fileSize = this.getFileSize(maxFileSize);
1439
+ const fileTypes = [].concat(fileType);
1440
+ const fileExts = [].concat(fileExt);
1441
+ const fileFilter = (fileTypes == null ? void 0 : fileTypes.length) || (fileExts == null ? void 0 : fileExts.length) ? _S3BucketUtil.fileFilter(fileTypes, fileExts) : void 0;
1442
+ return multer({
1443
+ fileFilter,
1444
+ limits: __spreadValues({}, fileSize && { fileSize }),
1445
+ storage: multerS3({
1446
+ acl,
1447
+ s3: this.s3Client,
1448
+ bucket: this.bucket,
1449
+ contentType: multerS3.AUTO_CONTENT_TYPE,
1450
+ metadata: (req, file, cb) => __async(this, null, function* () {
1451
+ const baseMetadata = __spreadProps(__spreadValues({}, file), { directory: normalizedPath });
1452
+ if (customMetadata) {
1453
+ const additionalMetadata = typeof customMetadata === "function" ? yield customMetadata(req, file) : customMetadata;
1454
+ Object.assign(baseMetadata, additionalMetadata);
1455
+ }
1456
+ cb(null, baseMetadata);
1457
+ }),
1458
+ key: (req, file, cb) => __async(this, null, function* () {
1459
+ let filename;
1460
+ if (typeof _filename === "function") {
1461
+ filename = yield _filename(req, file);
1462
+ } else if (_filename) {
1463
+ filename = _filename;
1464
+ } else {
1465
+ filename = file.originalname;
1466
+ }
1467
+ filename = decodeURIComponent(filename);
1468
+ const key = `${normalizedPath}${filename}`;
1469
+ cb(null, key);
1470
+ })
1471
+ })
1472
+ });
1473
+ }
1474
+ /**
1475
+ * Middleware for uploading a single file
1476
+ * Adds the uploaded file info to req.s3File
1477
+ */
1478
+ uploadSingleFile(fieldName, directory, options = {}) {
1479
+ const upload = this.getUploadFileMW(directory, options);
1480
+ return (req, res, next) => {
1481
+ const mw = upload.single(fieldName);
1482
+ mw(req, res, (err) => {
1483
+ var _a2, _b;
1484
+ if (err) {
1485
+ (_a2 = this.logger) == null ? void 0 : _a2.error(this.reqId, "Single file upload error", { fieldName, error: err.message });
1486
+ return next(err);
1487
+ }
1488
+ if (req.file) {
1489
+ req.s3File = req.file;
1490
+ (_b = this.logger) == null ? void 0 : _b.info(this.reqId, "Single file uploaded successfully", {
1491
+ fieldName,
1492
+ key: req.s3File.key,
1493
+ location: req.s3File.location,
1494
+ size: req.s3File.size
1495
+ });
1496
+ }
1497
+ next();
1498
+ });
1499
+ };
1500
+ }
1501
+ /**
1502
+ * Middleware for uploading multiple files with the same field name
1503
+ * Adds the uploaded files info to req.s3Files
1504
+ */
1505
+ uploadMultipleFiles(fieldName, directory, options = {}) {
1506
+ const upload = this.getUploadFileMW(directory, options);
1507
+ return (req, res, next) => {
1508
+ const mw = upload.array(fieldName, options.maxFilesCount || void 0);
1509
+ mw(req, res, (err) => {
1510
+ var _a2, _b;
1511
+ if (err) {
1512
+ (_a2 = this.logger) == null ? void 0 : _a2.error(this.reqId, "Multiple files upload error", { fieldName, error: err.message });
1513
+ return next(err);
1514
+ }
1515
+ if (Array.isArray(req.files)) {
1516
+ req.s3Files = req.files;
1517
+ (_b = this.logger) == null ? void 0 : _b.info(this.reqId, "Multiple files uploaded successfully", {
1518
+ fieldName,
1519
+ count: req.s3Files.length,
1520
+ keys: req.s3Files.map((f) => f.key)
1521
+ });
1522
+ }
1523
+ next();
1524
+ });
1525
+ };
1526
+ }
1527
+ /**
1528
+ * Middleware for uploading multiple files with different field names
1529
+ * Adds the uploaded files info to req.s3FilesByField
1530
+ */
1531
+ uploadFieldsFiles(fields) {
1532
+ const fieldConfigs = fields.map((field) => {
1533
+ const upload = this.getUploadFileMW(field.directory, field.options || {});
1534
+ return {
1535
+ name: field.name,
1536
+ maxCount: field.maxCount || 1,
1537
+ upload,
1538
+ directory: field.directory
1539
+ };
1540
+ });
1541
+ return (req, res, next) => __async(this, null, function* () {
1542
+ const multerFields = fieldConfigs.map((f) => ({ name: f.name, maxCount: f.maxCount }));
1543
+ const upload = this.getUploadFileMW(fieldConfigs[0].directory);
1544
+ const mw = upload.fields(multerFields);
1545
+ mw(req, res, (err) => {
1546
+ var _a2, _b;
1547
+ if (err) {
1548
+ (_a2 = this.logger) == null ? void 0 : _a2.error(this.reqId, "Fields upload error", { error: err.message });
1549
+ return next(err);
1550
+ }
1551
+ if (req.files && typeof req.files === "object" && !Array.isArray(req.files)) {
1552
+ req.s3FilesByField = req.files;
1553
+ const uploadSummary = Object.entries(req.s3FilesByField).map(([field, files]) => ({
1554
+ field,
1555
+ count: files.length,
1556
+ keys: files.map((f) => f.key)
1557
+ }));
1558
+ (_b = this.logger) == null ? void 0 : _b.info(this.reqId, "Fields uploaded successfully", { uploadSummary });
1559
+ }
1560
+ next();
1561
+ });
1562
+ });
1563
+ }
1564
+ /**
1565
+ * Middleware for uploading any files (mixed field names)
1566
+ * Adds the uploaded files info to req.s3AllFiles
1567
+ */
1568
+ uploadAnyFiles(directory, maxCount, options = {}) {
1569
+ const upload = this.getUploadFileMW(directory, options);
1570
+ return (req, res, next) => {
1571
+ const anyUpload = maxCount ? upload.any() : upload.any();
1572
+ anyUpload(req, res, (err) => {
1573
+ var _a2, _b;
1574
+ if (err) {
1575
+ (_a2 = this.logger) == null ? void 0 : _a2.error(this.reqId, "Any files upload error", { error: err.message });
1576
+ return next(err);
1577
+ }
1578
+ if (req.files && Array.isArray(req.files)) {
1579
+ req.s3AllFiles = req.files;
1580
+ if (maxCount && req.s3AllFiles.length > maxCount) {
1581
+ return next(new Error(`Too many files uploaded. Maximum is ${maxCount}`));
1582
+ }
1583
+ (_b = this.logger) == null ? void 0 : _b.info(this.reqId, "Any files uploaded successfully", {
1584
+ count: req.s3AllFiles.length,
1585
+ keys: req.s3AllFiles.map((f) => f.key)
1586
+ });
1587
+ }
1588
+ next();
1589
+ });
1590
+ };
1591
+ }
1592
+ };
1593
+
1594
+ // src/aws/sns.ts
1595
+ import { SNS } from "@aws-sdk/client-sns";
1596
+ var SNSUtil = class {
1597
+ constructor({
1598
+ accessKeyId = AWSConfigSharingUtil.accessKeyId,
1599
+ secretAccessKey = AWSConfigSharingUtil.secretAccessKey,
1600
+ endpoint = AWSConfigSharingUtil.endpoint,
1601
+ region = AWSConfigSharingUtil.region,
1602
+ topicArn,
1603
+ debug = false
1604
+ }) {
1605
+ __publicField(this, "sns");
1606
+ __publicField(this, "topicArn");
1607
+ const credentials = { accessKeyId, secretAccessKey };
1608
+ const options = __spreadValues(__spreadValues(__spreadValues({}, accessKeyId && secretAccessKey && { credentials }), endpoint && { endpoint }), region && { region });
1609
+ if (debug) {
1610
+ console.log("LambdaUtil client options", options);
1611
+ }
1612
+ this.topicArn = topicArn;
1613
+ this.sns = new SNS(__spreadValues(__spreadValues(__spreadValues({}, credentials && { credentials }), endpoint && { endpoint }), region && { region }));
1614
+ }
1615
+ publishTopicMessage(message) {
1616
+ return __async(this, null, function* () {
1617
+ this.sns.publish({
1618
+ Message: typeof message === "string" ? message : JSON.stringify(message),
1619
+ TopicArn: this.topicArn
1620
+ });
1621
+ });
1622
+ }
1623
+ };
1624
+ export {
1625
+ ACLs,
1626
+ AWSConfigSharingUtil,
1627
+ IAMUtil,
1628
+ LambdaUtil,
1629
+ S3BucketUtil,
1630
+ SNSUtil
1631
+ };