node-server-engine 1.8.0 → 1.10.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.
Files changed (40) hide show
  1. package/.nycrc +8 -4
  2. package/README.md +712 -67
  3. package/dist/entities/AWSS3/AWSS3.d.ts +157 -0
  4. package/dist/entities/AWSS3/AWSS3.js +426 -0
  5. package/dist/entities/AWSS3/AWSS3.js.map +1 -0
  6. package/dist/entities/AWSS3/AWSS3.types.d.ts +78 -0
  7. package/dist/entities/AWSS3/AWSS3.types.js +2 -0
  8. package/dist/entities/AWSS3/AWSS3.types.js.map +1 -0
  9. package/dist/entities/AWSS3/index.d.ts +2 -0
  10. package/dist/entities/{Storage → AWSS3}/index.js +8 -8
  11. package/dist/entities/AWSS3/index.js.map +1 -0
  12. package/dist/entities/ElasticSearch/ElasticSearch.d.ts +5 -0
  13. package/dist/entities/ElasticSearch/ElasticSearch.js +137 -38
  14. package/dist/entities/ElasticSearch/ElasticSearch.js.map +1 -1
  15. package/dist/entities/GoogleCloudStorage/GoogleCloudStorage.d.ts +141 -0
  16. package/dist/entities/GoogleCloudStorage/GoogleCloudStorage.js +351 -0
  17. package/dist/entities/GoogleCloudStorage/GoogleCloudStorage.js.map +1 -0
  18. package/dist/entities/{Storage/Storage.types.d.ts → GoogleCloudStorage/GoogleCloudStorage.types.d.ts} +14 -0
  19. package/dist/entities/GoogleCloudStorage/GoogleCloudStorage.types.js +2 -0
  20. package/dist/entities/GoogleCloudStorage/GoogleCloudStorage.types.js.map +1 -0
  21. package/dist/entities/GoogleCloudStorage/index.d.ts +2 -0
  22. package/dist/entities/GoogleCloudStorage/index.js +28 -0
  23. package/dist/entities/GoogleCloudStorage/index.js.map +1 -0
  24. package/dist/entities/Localizator/Localizator.js +2 -2
  25. package/dist/entities/Localizator/Localizator.js.map +1 -1
  26. package/dist/entities/Sequelize/Sequelize.config.js +7 -5
  27. package/dist/entities/Sequelize/Sequelize.config.js.map +1 -1
  28. package/dist/entities/Sequelize/Sequelize.js +29 -4
  29. package/dist/entities/Sequelize/Sequelize.js.map +1 -1
  30. package/dist/entities/index.d.ts +2 -1
  31. package/dist/entities/index.js +15 -4
  32. package/dist/entities/index.js.map +1 -1
  33. package/package.json +14 -12
  34. package/dist/entities/Storage/Storage.d.ts +0 -38
  35. package/dist/entities/Storage/Storage.js +0 -214
  36. package/dist/entities/Storage/Storage.js.map +0 -1
  37. package/dist/entities/Storage/Storage.types.js +0 -2
  38. package/dist/entities/Storage/Storage.types.js.map +0 -1
  39. package/dist/entities/Storage/index.d.ts +0 -2
  40. package/dist/entities/Storage/index.js.map +0 -1
@@ -0,0 +1,157 @@
1
+ import { Readable } from 'stream';
2
+ import { S3Client } from '@aws-sdk/client-s3';
3
+ import { S3UploadDestinationOptions, S3UploadedFile, S3UploaderOptions, S3GetResponse, S3DownloadResponse, AWSS3Config, S3FileMetadata } from './AWSS3.types';
4
+ /**
5
+ * Generic AWS S3 wrapper for easy file operations
6
+ * Provides methods for uploading, downloading, streaming, and deleting files
7
+ * Compatible with AWS S3 and S3-compatible services (MinIO, LocalStack, etc.)
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * // Initialize with custom configuration
12
+ * AWSS3.init({
13
+ * region: 'us-east-1',
14
+ * accessKeyId: 'YOUR_ACCESS_KEY',
15
+ * secretAccessKey: 'YOUR_SECRET_KEY'
16
+ * });
17
+ *
18
+ * // Upload a file
19
+ * const result = await AWSS3.upload(
20
+ * fileStream,
21
+ * 'my-bucket',
22
+ * { directory: 'uploads', mime: 'image/png' }
23
+ * );
24
+ *
25
+ * // Download a file
26
+ * const { data, metadata } = await AWSS3.get('my-bucket', 'uploads/file.png');
27
+ *
28
+ * // Delete a file
29
+ * await AWSS3.delete('my-bucket', 'uploads/file.png');
30
+ * ```
31
+ */
32
+ export declare const AWSS3: {
33
+ s3Client: S3Client | null;
34
+ /**
35
+ * Initialize AWS S3 with configuration
36
+ * Call this method before using any other methods
37
+ * @param config - AWS S3 configuration options
38
+ */
39
+ init(config?: AWSS3Config): void;
40
+ /**
41
+ * Get the S3 client instance (auto-initializes if needed)
42
+ * @private
43
+ */
44
+ getS3Client(): S3Client;
45
+ /**
46
+ * Upload a file to AWS S3
47
+ * @param readStream - Readable stream of the file content
48
+ * @param bucket - Bucket name where the file should be stored
49
+ * @param destinationOptions - Options for file destination (directory, fileName, etc.)
50
+ * @param uploaderOptions - Additional options like maxSize
51
+ * @param metadata - Optional metadata to attach to the file
52
+ * @returns Promise with file upload result
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * const fileStream = fs.createReadStream('photo.jpg');
57
+ * const result = await AWSS3.upload(
58
+ * fileStream,
59
+ * 'my-bucket',
60
+ * { directory: 'photos', mime: 'image/jpeg' },
61
+ * { maxSize: '5MB' },
62
+ * { userId: '123', uploadedBy: 'user@example.com' }
63
+ * );
64
+ * console.log(result.Key); // photos/uuid.jpeg
65
+ * ```
66
+ */
67
+ upload(readStream: Readable, bucket: string, destinationOptions?: S3UploadDestinationOptions, uploaderOptions?: S3UploaderOptions, metadata?: Record<string, string>): Promise<S3UploadedFile>;
68
+ /**
69
+ * Download a file from AWS S3 and return its content as Buffer
70
+ * @param bucket - Bucket name
71
+ * @param key - File key (path) in the bucket
72
+ * @returns Promise with file data and metadata
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * const { data, metadata } = await AWSS3.get('my-bucket', 'photos/image.jpg');
77
+ * console.log(metadata.ContentLength); // File size in bytes
78
+ * console.log(data); // Buffer with file content
79
+ * ```
80
+ */
81
+ get(bucket: string, key: string): Promise<S3GetResponse>;
82
+ /**
83
+ * Get a readable stream for a file from AWS S3
84
+ * Use this for large files or when you need to stream the content
85
+ * @param bucket - Bucket name
86
+ * @param key - File key (path) in the bucket
87
+ * @returns Promise with readable stream and metadata
88
+ *
89
+ * @example
90
+ * ```typescript
91
+ * const { stream, metadata } = await AWSS3.download('my-bucket', 'videos/video.mp4');
92
+ * stream.pipe(res); // Stream to HTTP response
93
+ * ```
94
+ */
95
+ download(bucket: string, key: string): Promise<S3DownloadResponse>;
96
+ /**
97
+ * Get a readable stream for a file (without fetching all metadata first)
98
+ * Use this when you don't need metadata and want faster streaming
99
+ * @param bucket - Bucket name
100
+ * @param key - File key (path) in the bucket
101
+ * @returns Promise with readable stream
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * const stream = await AWSS3.getFileStream('my-bucket', 'audio/song.mp3');
106
+ * stream.pipe(res); // Direct streaming
107
+ * ```
108
+ */
109
+ getFileStream(bucket: string, key: string): Promise<Readable>;
110
+ /**
111
+ * Get file metadata without downloading the file
112
+ * @param bucket - Bucket name
113
+ * @param key - File key (path) in the bucket
114
+ * @returns Promise with file metadata
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * const metadata = await AWSS3.getMetadata('my-bucket', 'photos/image.jpg');
119
+ * console.log(metadata.ContentLength); // File size
120
+ * console.log(metadata.ContentType); // MIME type
121
+ * ```
122
+ */
123
+ getMetadata(bucket: string, key: string): Promise<S3FileMetadata>;
124
+ /**
125
+ * Delete a file from AWS S3
126
+ * @param bucket - Bucket name
127
+ * @param key - File key (path) in the bucket
128
+ * @returns Promise that resolves when deletion is complete
129
+ *
130
+ * @example
131
+ * ```typescript
132
+ * await AWSS3.delete('my-bucket', 'temp/old-file.txt');
133
+ * ```
134
+ */
135
+ delete(bucket: string, key: string): Promise<void>;
136
+ /**
137
+ * Generate a random file path with optional directory and extension
138
+ * Useful for creating unique file names
139
+ * @param options - Options for file destination generation
140
+ * @returns Generated file path
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * // Generate UUID only
145
+ * const path1 = AWSS3.generateFileDestination();
146
+ * // 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
147
+ *
148
+ * // With directory and MIME type
149
+ * const path2 = AWSS3.generateFileDestination({
150
+ * directory: 'uploads/images',
151
+ * mime: 'image/jpeg'
152
+ * });
153
+ * // 'uploads/images/a1b2c3d4-e5f6-7890-abcd-ef1234567890.jpeg'
154
+ * ```
155
+ */
156
+ generateFileDestination(options?: S3UploadDestinationOptions): string;
157
+ };
@@ -0,0 +1,426 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.AWSS3 = void 0;
8
+ var _crypto = require("crypto");
9
+ var _clientS = require("@aws-sdk/client-s3");
10
+ var _filesizeParser = _interopRequireDefault(require("filesize-parser"));
11
+ var _mimeTypes = _interopRequireDefault(require("mime-types"));
12
+ var _WebError = require("../WebError");
13
+ var _report = require("../../utils/report");
14
+ var _trimPathSlash = require("../../utils/trimPathSlash");
15
+ const namespace = 'engine:aws-s3';
16
+
17
+ /**
18
+ * Generic AWS S3 wrapper for easy file operations
19
+ * Provides methods for uploading, downloading, streaming, and deleting files
20
+ * Compatible with AWS S3 and S3-compatible services (MinIO, LocalStack, etc.)
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * // Initialize with custom configuration
25
+ * AWSS3.init({
26
+ * region: 'us-east-1',
27
+ * accessKeyId: 'YOUR_ACCESS_KEY',
28
+ * secretAccessKey: 'YOUR_SECRET_KEY'
29
+ * });
30
+ *
31
+ * // Upload a file
32
+ * const result = await AWSS3.upload(
33
+ * fileStream,
34
+ * 'my-bucket',
35
+ * { directory: 'uploads', mime: 'image/png' }
36
+ * );
37
+ *
38
+ * // Download a file
39
+ * const { data, metadata } = await AWSS3.get('my-bucket', 'uploads/file.png');
40
+ *
41
+ * // Delete a file
42
+ * await AWSS3.delete('my-bucket', 'uploads/file.png');
43
+ * ```
44
+ */
45
+ const AWSS3 = exports.AWSS3 = {
46
+ s3Client: null,
47
+ /**
48
+ * Initialize AWS S3 with configuration
49
+ * Call this method before using any other methods
50
+ * @param config - AWS S3 configuration options
51
+ */
52
+ init(config) {
53
+ const s3Config = {};
54
+ if (config) {
55
+ if (config.region) s3Config.region = config.region;
56
+ if (config.endpoint) s3Config.endpoint = config.endpoint;
57
+ if (config.forcePathStyle !== undefined) s3Config.forcePathStyle = config.forcePathStyle;
58
+ if (config.accessKeyId && config.secretAccessKey) {
59
+ s3Config.credentials = {
60
+ accessKeyId: config.accessKeyId,
61
+ secretAccessKey: config.secretAccessKey,
62
+ ...(config.sessionToken && {
63
+ sessionToken: config.sessionToken
64
+ })
65
+ };
66
+ }
67
+ } else {
68
+ // Use environment variables as fallback
69
+ if (process.env.AWS_REGION) s3Config.region = process.env.AWS_REGION;
70
+ if (process.env.AWS_S3_ENDPOINT) s3Config.endpoint = process.env.AWS_S3_ENDPOINT;
71
+ if (process.env.AWS_S3_FORCE_PATH_STYLE === 'true') s3Config.forcePathStyle = true;
72
+ if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) {
73
+ s3Config.credentials = {
74
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
75
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
76
+ ...(process.env.AWS_SESSION_TOKEN && {
77
+ sessionToken: process.env.AWS_SESSION_TOKEN
78
+ })
79
+ };
80
+ }
81
+ }
82
+ this.s3Client = new _clientS.S3Client(s3Config);
83
+ (0, _report.reportDebug)({
84
+ namespace,
85
+ message: 'AWS S3 initialized',
86
+ data: {
87
+ region: s3Config.region,
88
+ endpoint: s3Config.endpoint
89
+ }
90
+ });
91
+ },
92
+ /**
93
+ * Get the S3 client instance (auto-initializes if needed)
94
+ * @private
95
+ */
96
+ getS3Client() {
97
+ if (!this.s3Client) {
98
+ this.init();
99
+ }
100
+ return this.s3Client;
101
+ },
102
+ /**
103
+ * Upload a file to AWS S3
104
+ * @param readStream - Readable stream of the file content
105
+ * @param bucket - Bucket name where the file should be stored
106
+ * @param destinationOptions - Options for file destination (directory, fileName, etc.)
107
+ * @param uploaderOptions - Additional options like maxSize
108
+ * @param metadata - Optional metadata to attach to the file
109
+ * @returns Promise with file upload result
110
+ *
111
+ * @example
112
+ * ```typescript
113
+ * const fileStream = fs.createReadStream('photo.jpg');
114
+ * const result = await AWSS3.upload(
115
+ * fileStream,
116
+ * 'my-bucket',
117
+ * { directory: 'photos', mime: 'image/jpeg' },
118
+ * { maxSize: '5MB' },
119
+ * { userId: '123', uploadedBy: 'user@example.com' }
120
+ * );
121
+ * console.log(result.Key); // photos/uuid.jpeg
122
+ * ```
123
+ */
124
+ async upload(readStream, bucket, destinationOptions = {}, uploaderOptions = {}, metadata) {
125
+ const client = this.getS3Client();
126
+ const {
127
+ maxSize
128
+ } = uploaderOptions;
129
+ (0, _report.reportDebug)({
130
+ namespace,
131
+ message: 'Uploading file to S3',
132
+ data: {
133
+ bucket,
134
+ destinationOptions,
135
+ uploaderOptions
136
+ }
137
+ });
138
+
139
+ // Generate the file path
140
+ const {
141
+ fileName,
142
+ directory
143
+ } = destinationOptions;
144
+ let key;
145
+ if (fileName) {
146
+ const trimmedDirectory = (0, _trimPathSlash.trimPathSlash)(directory ?? '/');
147
+ key = trimmedDirectory ? `${trimmedDirectory}/${fileName}` : fileName;
148
+ } else {
149
+ key = this.generateFileDestination(destinationOptions);
150
+ }
151
+
152
+ // Collect stream data with size validation
153
+ const chunks = [];
154
+ let receivedSize = 0;
155
+ const intMaxSize = maxSize && (0, _filesizeParser.default)(maxSize);
156
+ for await (const chunk of readStream) {
157
+ receivedSize += chunk.length;
158
+ if (intMaxSize && receivedSize > intMaxSize) {
159
+ throw new _WebError.WebError({
160
+ statusCode: 413,
161
+ errorCode: 'file-too-large',
162
+ message: 'File size exceeds the maximum allowed limit',
163
+ hint: {
164
+ maxSize
165
+ },
166
+ data: {
167
+ bucket,
168
+ destinationOptions,
169
+ receivedSize,
170
+ maxSize
171
+ }
172
+ });
173
+ }
174
+ chunks.push(chunk);
175
+ }
176
+ const body = Buffer.concat(chunks);
177
+
178
+ // Determine content type
179
+ const contentType = destinationOptions.mime || 'application/octet-stream';
180
+ const command = new _clientS.PutObjectCommand({
181
+ Bucket: bucket,
182
+ Key: key,
183
+ Body: body,
184
+ ContentType: contentType,
185
+ ...(metadata && {
186
+ Metadata: metadata
187
+ })
188
+ });
189
+ const response = await client.send(command);
190
+ (0, _report.reportDebug)({
191
+ namespace,
192
+ message: 'File uploaded to S3',
193
+ data: {
194
+ bucket,
195
+ key,
196
+ etag: response.ETag
197
+ }
198
+ });
199
+ return {
200
+ Bucket: bucket,
201
+ Key: key,
202
+ ETag: response.ETag,
203
+ Location: `https://${bucket}.s3.amazonaws.com/${key}`,
204
+ ServerSideEncryption: response.ServerSideEncryption,
205
+ VersionId: response.VersionId
206
+ };
207
+ },
208
+ /**
209
+ * Download a file from AWS S3 and return its content as Buffer
210
+ * @param bucket - Bucket name
211
+ * @param key - File key (path) in the bucket
212
+ * @returns Promise with file data and metadata
213
+ *
214
+ * @example
215
+ * ```typescript
216
+ * const { data, metadata } = await AWSS3.get('my-bucket', 'photos/image.jpg');
217
+ * console.log(metadata.ContentLength); // File size in bytes
218
+ * console.log(data); // Buffer with file content
219
+ * ```
220
+ */
221
+ async get(bucket, key) {
222
+ const client = this.getS3Client();
223
+ const command = new _clientS.GetObjectCommand({
224
+ Bucket: bucket,
225
+ Key: key
226
+ });
227
+ const response = await client.send(command);
228
+
229
+ // Convert stream to buffer
230
+ const chunks = [];
231
+ for await (const chunk of response.Body) {
232
+ chunks.push(chunk);
233
+ }
234
+ const data = Buffer.concat(chunks);
235
+ const metadata = {
236
+ ContentType: response.ContentType,
237
+ ContentLength: response.ContentLength,
238
+ LastModified: response.LastModified,
239
+ ETag: response.ETag,
240
+ Metadata: response.Metadata,
241
+ ServerSideEncryption: response.ServerSideEncryption,
242
+ VersionId: response.VersionId
243
+ };
244
+ (0, _report.reportDebug)({
245
+ namespace,
246
+ message: 'Downloaded file from S3',
247
+ data: {
248
+ bucket,
249
+ key,
250
+ size: data.length
251
+ }
252
+ });
253
+ return {
254
+ data,
255
+ metadata
256
+ };
257
+ },
258
+ /**
259
+ * Get a readable stream for a file from AWS S3
260
+ * Use this for large files or when you need to stream the content
261
+ * @param bucket - Bucket name
262
+ * @param key - File key (path) in the bucket
263
+ * @returns Promise with readable stream and metadata
264
+ *
265
+ * @example
266
+ * ```typescript
267
+ * const { stream, metadata } = await AWSS3.download('my-bucket', 'videos/video.mp4');
268
+ * stream.pipe(res); // Stream to HTTP response
269
+ * ```
270
+ */
271
+ async download(bucket, key) {
272
+ const client = this.getS3Client();
273
+ const command = new _clientS.GetObjectCommand({
274
+ Bucket: bucket,
275
+ Key: key
276
+ });
277
+ const response = await client.send(command);
278
+ const metadata = {
279
+ ContentType: response.ContentType,
280
+ ContentLength: response.ContentLength,
281
+ LastModified: response.LastModified,
282
+ ETag: response.ETag,
283
+ Metadata: response.Metadata,
284
+ ServerSideEncryption: response.ServerSideEncryption,
285
+ VersionId: response.VersionId
286
+ };
287
+ (0, _report.reportDebug)({
288
+ namespace,
289
+ message: 'Fetched file stream from S3',
290
+ data: {
291
+ bucket,
292
+ key,
293
+ metadata
294
+ }
295
+ });
296
+ return {
297
+ stream: response.Body,
298
+ metadata
299
+ };
300
+ },
301
+ /**
302
+ * Get a readable stream for a file (without fetching all metadata first)
303
+ * Use this when you don't need metadata and want faster streaming
304
+ * @param bucket - Bucket name
305
+ * @param key - File key (path) in the bucket
306
+ * @returns Promise with readable stream
307
+ *
308
+ * @example
309
+ * ```typescript
310
+ * const stream = await AWSS3.getFileStream('my-bucket', 'audio/song.mp3');
311
+ * stream.pipe(res); // Direct streaming
312
+ * ```
313
+ */
314
+ async getFileStream(bucket, key) {
315
+ const client = this.getS3Client();
316
+ const command = new _clientS.GetObjectCommand({
317
+ Bucket: bucket,
318
+ Key: key
319
+ });
320
+ const response = await client.send(command);
321
+ (0, _report.reportDebug)({
322
+ namespace,
323
+ message: "Fetched file stream from S3's bucket",
324
+ data: {
325
+ bucket,
326
+ key
327
+ }
328
+ });
329
+ return response.Body;
330
+ },
331
+ /**
332
+ * Get file metadata without downloading the file
333
+ * @param bucket - Bucket name
334
+ * @param key - File key (path) in the bucket
335
+ * @returns Promise with file metadata
336
+ *
337
+ * @example
338
+ * ```typescript
339
+ * const metadata = await AWSS3.getMetadata('my-bucket', 'photos/image.jpg');
340
+ * console.log(metadata.ContentLength); // File size
341
+ * console.log(metadata.ContentType); // MIME type
342
+ * ```
343
+ */
344
+ async getMetadata(bucket, key) {
345
+ const client = this.getS3Client();
346
+ const command = new _clientS.HeadObjectCommand({
347
+ Bucket: bucket,
348
+ Key: key
349
+ });
350
+ const response = await client.send(command);
351
+ return {
352
+ ContentType: response.ContentType,
353
+ ContentLength: response.ContentLength,
354
+ LastModified: response.LastModified,
355
+ ETag: response.ETag,
356
+ Metadata: response.Metadata,
357
+ ServerSideEncryption: response.ServerSideEncryption,
358
+ VersionId: response.VersionId
359
+ };
360
+ },
361
+ /**
362
+ * Delete a file from AWS S3
363
+ * @param bucket - Bucket name
364
+ * @param key - File key (path) in the bucket
365
+ * @returns Promise that resolves when deletion is complete
366
+ *
367
+ * @example
368
+ * ```typescript
369
+ * await AWSS3.delete('my-bucket', 'temp/old-file.txt');
370
+ * ```
371
+ */
372
+ async delete(bucket, key) {
373
+ const client = this.getS3Client();
374
+ (0, _report.reportDebug)({
375
+ namespace,
376
+ message: 'Deleting file from S3',
377
+ data: {
378
+ bucket,
379
+ key
380
+ }
381
+ });
382
+ const command = new _clientS.DeleteObjectCommand({
383
+ Bucket: bucket,
384
+ Key: key
385
+ });
386
+ await client.send(command);
387
+ },
388
+ /**
389
+ * Generate a random file path with optional directory and extension
390
+ * Useful for creating unique file names
391
+ * @param options - Options for file destination generation
392
+ * @returns Generated file path
393
+ *
394
+ * @example
395
+ * ```typescript
396
+ * // Generate UUID only
397
+ * const path1 = AWSS3.generateFileDestination();
398
+ * // 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
399
+ *
400
+ * // With directory and MIME type
401
+ * const path2 = AWSS3.generateFileDestination({
402
+ * directory: 'uploads/images',
403
+ * mime: 'image/jpeg'
404
+ * });
405
+ * // 'uploads/images/a1b2c3d4-e5f6-7890-abcd-ef1234567890.jpeg'
406
+ * ```
407
+ */
408
+ generateFileDestination(options = {}) {
409
+ const {
410
+ directory = '/',
411
+ mime,
412
+ noExtension = false
413
+ } = options;
414
+
415
+ // Determine file extension from MIME type
416
+ const extension = mime && _mimeTypes.default.extension(mime) ? _mimeTypes.default.extension(mime) : undefined;
417
+
418
+ // Build the full name with or without extension
419
+ const fullName = extension && !noExtension ? `${(0, _crypto.randomUUID)()}.${extension}` : (0, _crypto.randomUUID)();
420
+
421
+ // Clean up directory path
422
+ const trimmedDirectory = (0, _trimPathSlash.trimPathSlash)(directory);
423
+ return `${trimmedDirectory ? `${trimmedDirectory}/` : ''}${fullName}`;
424
+ }
425
+ };
426
+ //# sourceMappingURL=AWSS3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AWSS3.js","names":["_crypto","require","_clientS","_filesizeParser","_interopRequireDefault","_mimeTypes","_WebError","_report","_trimPathSlash","namespace","AWSS3","exports","s3Client","init","config","s3Config","region","endpoint","forcePathStyle","undefined","accessKeyId","secretAccessKey","credentials","sessionToken","process","env","AWS_REGION","AWS_S3_ENDPOINT","AWS_S3_FORCE_PATH_STYLE","AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_SESSION_TOKEN","S3Client","reportDebug","message","data","getS3Client","upload","readStream","bucket","destinationOptions","uploaderOptions","metadata","client","maxSize","fileName","directory","key","trimmedDirectory","trimPathSlash","generateFileDestination","chunks","receivedSize","intMaxSize","fileSizeParser","chunk","length","WebError","statusCode","errorCode","hint","push","body","Buffer","concat","contentType","mime","command","PutObjectCommand","Bucket","Key","Body","ContentType","Metadata","response","send","etag","ETag","Location","ServerSideEncryption","VersionId","get","GetObjectCommand","ContentLength","LastModified","size","download","stream","getFileStream","getMetadata","HeadObjectCommand","delete","DeleteObjectCommand","options","noExtension","extension","mimeTypes","fullName","randomUUID"],"sources":["../../../src/entities/AWSS3/AWSS3.ts"],"sourcesContent":["import { randomUUID } from 'crypto';\nimport { Readable } from 'stream';\nimport {\n S3Client,\n PutObjectCommand,\n GetObjectCommand,\n DeleteObjectCommand,\n HeadObjectCommand,\n S3ClientConfig\n} from '@aws-sdk/client-s3';\nimport fileSizeParser from 'filesize-parser';\nimport mimeTypes from 'mime-types';\nimport {\n S3UploadDestinationOptions,\n S3UploadedFile,\n S3UploaderOptions,\n S3GetResponse,\n S3DownloadResponse,\n AWSS3Config,\n S3FileMetadata\n} from './AWSS3.types';\nimport { WebError } from 'entities/WebError';\nimport { reportDebug } from 'utils/report';\nimport { trimPathSlash } from 'utils/trimPathSlash';\n\nconst namespace = 'engine:aws-s3';\n\n/**\n * Generic AWS S3 wrapper for easy file operations\n * Provides methods for uploading, downloading, streaming, and deleting files\n * Compatible with AWS S3 and S3-compatible services (MinIO, LocalStack, etc.)\n * \n * @example\n * ```typescript\n * // Initialize with custom configuration\n * AWSS3.init({\n * region: 'us-east-1',\n * accessKeyId: 'YOUR_ACCESS_KEY',\n * secretAccessKey: 'YOUR_SECRET_KEY'\n * });\n * \n * // Upload a file\n * const result = await AWSS3.upload(\n * fileStream,\n * 'my-bucket',\n * { directory: 'uploads', mime: 'image/png' }\n * );\n * \n * // Download a file\n * const { data, metadata } = await AWSS3.get('my-bucket', 'uploads/file.png');\n * \n * // Delete a file\n * await AWSS3.delete('my-bucket', 'uploads/file.png');\n * ```\n */\nexport const AWSS3 = {\n s3Client: null as S3Client | null,\n\n /**\n * Initialize AWS S3 with configuration\n * Call this method before using any other methods\n * @param config - AWS S3 configuration options\n */\n init(config?: AWSS3Config): void {\n const s3Config: S3ClientConfig = {};\n\n if (config) {\n if (config.region) s3Config.region = config.region;\n if (config.endpoint) s3Config.endpoint = config.endpoint;\n if (config.forcePathStyle !== undefined) s3Config.forcePathStyle = config.forcePathStyle;\n \n if (config.accessKeyId && config.secretAccessKey) {\n s3Config.credentials = {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n ...(config.sessionToken && { sessionToken: config.sessionToken })\n };\n }\n } else {\n // Use environment variables as fallback\n if (process.env.AWS_REGION) s3Config.region = process.env.AWS_REGION;\n if (process.env.AWS_S3_ENDPOINT) s3Config.endpoint = process.env.AWS_S3_ENDPOINT;\n if (process.env.AWS_S3_FORCE_PATH_STYLE === 'true') s3Config.forcePathStyle = true;\n \n if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) {\n s3Config.credentials = {\n accessKeyId: process.env.AWS_ACCESS_KEY_ID,\n secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,\n ...(process.env.AWS_SESSION_TOKEN && { sessionToken: process.env.AWS_SESSION_TOKEN })\n };\n }\n }\n\n this.s3Client = new S3Client(s3Config);\n reportDebug({\n namespace,\n message: 'AWS S3 initialized',\n data: { region: s3Config.region, endpoint: s3Config.endpoint }\n });\n },\n\n /**\n * Get the S3 client instance (auto-initializes if needed)\n * @private\n */\n getS3Client(): S3Client {\n if (!this.s3Client) {\n this.init();\n }\n return this.s3Client!;\n },\n\n /**\n * Upload a file to AWS S3\n * @param readStream - Readable stream of the file content\n * @param bucket - Bucket name where the file should be stored\n * @param destinationOptions - Options for file destination (directory, fileName, etc.)\n * @param uploaderOptions - Additional options like maxSize\n * @param metadata - Optional metadata to attach to the file\n * @returns Promise with file upload result\n * \n * @example\n * ```typescript\n * const fileStream = fs.createReadStream('photo.jpg');\n * const result = await AWSS3.upload(\n * fileStream,\n * 'my-bucket',\n * { directory: 'photos', mime: 'image/jpeg' },\n * { maxSize: '5MB' },\n * { userId: '123', uploadedBy: 'user@example.com' }\n * );\n * console.log(result.Key); // photos/uuid.jpeg\n * ```\n */\n async upload(\n readStream: Readable,\n bucket: string,\n destinationOptions: S3UploadDestinationOptions = {},\n uploaderOptions: S3UploaderOptions = {},\n metadata?: Record<string, string>\n ): Promise<S3UploadedFile> {\n const client = this.getS3Client();\n const { maxSize } = uploaderOptions;\n\n reportDebug({\n namespace,\n message: 'Uploading file to S3',\n data: { bucket, destinationOptions, uploaderOptions }\n });\n\n // Generate the file path\n const { fileName, directory } = destinationOptions;\n\n let key: string;\n if (fileName) {\n const trimmedDirectory = trimPathSlash(directory ?? '/');\n key = trimmedDirectory ? `${trimmedDirectory}/${fileName}` : fileName;\n } else {\n key = this.generateFileDestination(destinationOptions);\n }\n\n // Collect stream data with size validation\n const chunks: Buffer[] = [];\n let receivedSize = 0;\n const intMaxSize = maxSize && fileSizeParser(maxSize);\n\n for await (const chunk of readStream) {\n receivedSize += chunk.length;\n \n if (intMaxSize && receivedSize > intMaxSize) {\n throw new WebError({\n statusCode: 413,\n errorCode: 'file-too-large',\n message: 'File size exceeds the maximum allowed limit',\n hint: { maxSize },\n data: {\n bucket,\n destinationOptions,\n receivedSize,\n maxSize\n }\n });\n }\n \n chunks.push(chunk);\n }\n\n const body = Buffer.concat(chunks);\n\n // Determine content type\n const contentType = destinationOptions.mime || 'application/octet-stream';\n\n const command = new PutObjectCommand({\n Bucket: bucket,\n Key: key,\n Body: body,\n ContentType: contentType,\n ...(metadata && { Metadata: metadata })\n });\n\n const response = await client.send(command);\n\n reportDebug({\n namespace,\n message: 'File uploaded to S3',\n data: { bucket, key, etag: response.ETag }\n });\n\n return {\n Bucket: bucket,\n Key: key,\n ETag: response.ETag,\n Location: `https://${bucket}.s3.amazonaws.com/${key}`,\n ServerSideEncryption: response.ServerSideEncryption,\n VersionId: response.VersionId\n };\n },\n\n /**\n * Download a file from AWS S3 and return its content as Buffer\n * @param bucket - Bucket name\n * @param key - File key (path) in the bucket\n * @returns Promise with file data and metadata\n * \n * @example\n * ```typescript\n * const { data, metadata } = await AWSS3.get('my-bucket', 'photos/image.jpg');\n * console.log(metadata.ContentLength); // File size in bytes\n * console.log(data); // Buffer with file content\n * ```\n */\n async get(bucket: string, key: string): Promise<S3GetResponse> {\n const client = this.getS3Client();\n\n const command = new GetObjectCommand({\n Bucket: bucket,\n Key: key\n });\n\n const response = await client.send(command);\n\n // Convert stream to buffer\n const chunks: Buffer[] = [];\n for await (const chunk of response.Body as Readable) {\n chunks.push(chunk);\n }\n const data = Buffer.concat(chunks);\n\n const metadata: S3FileMetadata = {\n ContentType: response.ContentType,\n ContentLength: response.ContentLength,\n LastModified: response.LastModified,\n ETag: response.ETag,\n Metadata: response.Metadata,\n ServerSideEncryption: response.ServerSideEncryption,\n VersionId: response.VersionId\n };\n\n reportDebug({\n namespace,\n message: 'Downloaded file from S3',\n data: { bucket, key, size: data.length }\n });\n\n return { data, metadata };\n },\n\n /**\n * Get a readable stream for a file from AWS S3\n * Use this for large files or when you need to stream the content\n * @param bucket - Bucket name\n * @param key - File key (path) in the bucket\n * @returns Promise with readable stream and metadata\n * \n * @example\n * ```typescript\n * const { stream, metadata } = await AWSS3.download('my-bucket', 'videos/video.mp4');\n * stream.pipe(res); // Stream to HTTP response\n * ```\n */\n async download(bucket: string, key: string): Promise<S3DownloadResponse> {\n const client = this.getS3Client();\n\n const command = new GetObjectCommand({\n Bucket: bucket,\n Key: key\n });\n\n const response = await client.send(command);\n\n const metadata: S3FileMetadata = {\n ContentType: response.ContentType,\n ContentLength: response.ContentLength,\n LastModified: response.LastModified,\n ETag: response.ETag,\n Metadata: response.Metadata,\n ServerSideEncryption: response.ServerSideEncryption,\n VersionId: response.VersionId\n };\n\n reportDebug({\n namespace,\n message: 'Fetched file stream from S3',\n data: { bucket, key, metadata }\n });\n\n return {\n stream: response.Body as Readable,\n metadata\n };\n },\n\n /**\n * Get a readable stream for a file (without fetching all metadata first)\n * Use this when you don't need metadata and want faster streaming\n * @param bucket - Bucket name\n * @param key - File key (path) in the bucket\n * @returns Promise with readable stream\n * \n * @example\n * ```typescript\n * const stream = await AWSS3.getFileStream('my-bucket', 'audio/song.mp3');\n * stream.pipe(res); // Direct streaming\n * ```\n */\n async getFileStream(bucket: string, key: string): Promise<Readable> {\n const client = this.getS3Client();\n\n const command = new GetObjectCommand({\n Bucket: bucket,\n Key: key\n });\n\n const response = await client.send(command);\n\n reportDebug({\n namespace,\n message: \"Fetched file stream from S3's bucket\",\n data: { bucket, key }\n });\n\n return response.Body as Readable;\n },\n\n /**\n * Get file metadata without downloading the file\n * @param bucket - Bucket name\n * @param key - File key (path) in the bucket\n * @returns Promise with file metadata\n * \n * @example\n * ```typescript\n * const metadata = await AWSS3.getMetadata('my-bucket', 'photos/image.jpg');\n * console.log(metadata.ContentLength); // File size\n * console.log(metadata.ContentType); // MIME type\n * ```\n */\n async getMetadata(bucket: string, key: string): Promise<S3FileMetadata> {\n const client = this.getS3Client();\n\n const command = new HeadObjectCommand({\n Bucket: bucket,\n Key: key\n });\n\n const response = await client.send(command);\n\n return {\n ContentType: response.ContentType,\n ContentLength: response.ContentLength,\n LastModified: response.LastModified,\n ETag: response.ETag,\n Metadata: response.Metadata,\n ServerSideEncryption: response.ServerSideEncryption,\n VersionId: response.VersionId\n };\n },\n\n /**\n * Delete a file from AWS S3\n * @param bucket - Bucket name\n * @param key - File key (path) in the bucket\n * @returns Promise that resolves when deletion is complete\n * \n * @example\n * ```typescript\n * await AWSS3.delete('my-bucket', 'temp/old-file.txt');\n * ```\n */\n async delete(bucket: string, key: string): Promise<void> {\n const client = this.getS3Client();\n\n reportDebug({\n namespace,\n message: 'Deleting file from S3',\n data: { bucket, key }\n });\n\n const command = new DeleteObjectCommand({\n Bucket: bucket,\n Key: key\n });\n\n await client.send(command);\n },\n\n /**\n * Generate a random file path with optional directory and extension\n * Useful for creating unique file names\n * @param options - Options for file destination generation\n * @returns Generated file path\n * \n * @example\n * ```typescript\n * // Generate UUID only\n * const path1 = AWSS3.generateFileDestination();\n * // 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'\n * \n * // With directory and MIME type\n * const path2 = AWSS3.generateFileDestination({\n * directory: 'uploads/images',\n * mime: 'image/jpeg'\n * });\n * // 'uploads/images/a1b2c3d4-e5f6-7890-abcd-ef1234567890.jpeg'\n * ```\n */\n generateFileDestination(options: S3UploadDestinationOptions = {}): string {\n const { directory = '/', mime, noExtension = false } = options;\n\n // Determine file extension from MIME type\n const extension =\n mime && mimeTypes.extension(mime) ? mimeTypes.extension(mime) : undefined;\n \n // Build the full name with or without extension\n const fullName =\n extension && !noExtension ? `${randomUUID()}.${extension}` : randomUUID();\n \n // Clean up directory path\n const trimmedDirectory = trimPathSlash(directory);\n return `${trimmedDirectory ? `${trimmedDirectory}/` : ''}${fullName}`;\n }\n};\n"],"mappings":"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","ignoreList":[]}