@duvdu-v1/duvdu 1.1.212 → 1.1.214

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
1
  import multer from 'multer';
2
- interface uploadOptions {
2
+ interface UploadOptions {
3
3
  fileTypes?: string[];
4
4
  maxSize?: number;
5
5
  fileFilter?(req: Request, file: Express.Multer.File, callback: multer.FileFilterCallback): void;
6
6
  }
7
- export declare const globalUploadMiddleware: (folder: string, options?: uploadOptions) => multer.Multer;
7
+ export declare const globalUploadMiddleware: (folder: string, options?: UploadOptions) => multer.Multer;
8
8
  export {};
@@ -1,21 +1,45 @@
1
1
  "use strict";
2
+ // /* eslint-disable indent */
3
+ // import path from 'path';
2
4
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
5
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
6
  };
5
7
  Object.defineProperty(exports, "__esModule", { value: true });
6
8
  exports.globalUploadMiddleware = void 0;
7
- /* eslint-disable indent */
8
- const path_1 = __importDefault(require("path"));
9
+ // import multer from 'multer';
10
+ // import { v4 } from 'uuid';
11
+ // import { BadRequestError } from '../errors/bad-request-error';
12
+ // interface uploadOptions {
13
+ // fileTypes?: string[];
14
+ // maxSize?: number;
15
+ // fileFilter?(req: Request, file: Express.Multer.File, callback: multer.FileFilterCallback): void;
16
+ // }
17
+ // export const globalUploadMiddleware = (folder: string, options?: uploadOptions) =>
18
+ // multer({
19
+ // storage: multer.diskStorage({
20
+ // destination: path.resolve(`media/${folder}`),
21
+ // filename(req, file, callback) {
22
+ // callback(null, `${v4()}.${file.originalname.split('.').at(-1)}`);
23
+ // },
24
+ // }),
25
+ // limits: { fileSize: options?.maxSize || 3 * 1024 * 1024 }, // 3MB
26
+ // fileFilter: options?.fileFilter
27
+ // ? (options.fileFilter as any)
28
+ // : function fileFilter(req, file, callback) {
29
+ // if (!options?.fileTypes) {
30
+ // if (!file.mimetype.startsWith('image'))
31
+ // return callback(new BadRequestError('invalid file format'));
32
+ // return callback(null, true);
33
+ // }
34
+ // if (options?.fileTypes?.some((type) => file.mimetype.startsWith(type)))
35
+ // return callback(null, true);
36
+ // else return callback(new BadRequestError('invalid file format'));
37
+ // },
38
+ // });
9
39
  const multer_1 = __importDefault(require("multer"));
10
- const uuid_1 = require("uuid");
11
40
  const bad_request_error_1 = require("../errors/bad-request-error");
12
41
  const globalUploadMiddleware = (folder, options) => (0, multer_1.default)({
13
- storage: multer_1.default.diskStorage({
14
- destination: path_1.default.resolve(`media/${folder}`),
15
- filename(req, file, callback) {
16
- callback(null, `${(0, uuid_1.v4)()}.${file.originalname.split('.').at(-1)}`);
17
- },
18
- }),
42
+ storage: multer_1.default.memoryStorage(),
19
43
  limits: { fileSize: (options === null || options === void 0 ? void 0 : options.maxSize) || 3 * 1024 * 1024 }, // 3MB
20
44
  fileFilter: (options === null || options === void 0 ? void 0 : options.fileFilter)
21
45
  ? options.fileFilter
@@ -8,19 +8,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- var __asyncValues = (this && this.__asyncValues) || function (o) {
12
- if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
13
- var m = o[Symbol.asyncIterator], i;
14
- return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
15
- function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
16
- function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
17
- };
18
11
  var __importDefault = (this && this.__importDefault) || function (mod) {
19
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
20
13
  };
21
14
  Object.defineProperty(exports, "__esModule", { value: true });
22
15
  exports.Bucket = void 0;
23
- const fs_1 = __importDefault(require("fs"));
24
16
  const path_1 = __importDefault(require("path"));
25
17
  const aws_sdk_1 = __importDefault(require("aws-sdk"));
26
18
  class Bucket {
@@ -39,143 +31,130 @@ class Bucket {
39
31
  }
40
32
  saveBucketFiles(folder, ...files) {
41
33
  return __awaiter(this, void 0, void 0, function* () {
42
- const CHUNK_SIZE = 20 * 1024 * 1024; // 20MB chunks for better performance
43
- const MAX_PARALLEL_CHUNKS = 4; // Control parallel uploads
44
- yield Promise.all(files.map((file) => __awaiter(this, void 0, void 0, function* () {
45
- var _a, e_1, _b, _c;
46
- const filePath = path_1.default.resolve(`media/${folder}/${file.filename}`);
47
- const fileSize = fs_1.default.statSync(filePath).size;
48
- const contentType = this.getContentType(file.filename);
49
- if (fileSize < CHUNK_SIZE) {
50
- return this.uploadSmallFile(folder, file, contentType);
51
- }
52
- let multipartUpload;
53
- try {
54
- // Initiate multipart upload
55
- multipartUpload = yield new Promise((resolve, reject) => {
56
- this.s3.createMultipartUpload({
57
- Bucket: this.bucketName,
58
- Key: `${folder}/${file.filename}`,
59
- ContentType: contentType,
60
- ContentDisposition: 'inline',
61
- ServerSideEncryption: 'AES256',
62
- }, (err, data) => {
63
- if (err)
64
- reject(err);
65
- else
66
- resolve(data);
67
- });
68
- });
69
- const uploadId = multipartUpload.UploadId;
70
- const parts = [];
71
- const fileStream = fs_1.default.createReadStream(filePath);
72
- // Prepare chunks for parallel upload
73
- const chunks = [];
74
- let currentChunk = Buffer.alloc(0);
75
- try {
76
- for (var _d = true, fileStream_1 = __asyncValues(fileStream), fileStream_1_1; fileStream_1_1 = yield fileStream_1.next(), _a = fileStream_1_1.done, !_a; _d = true) {
77
- _c = fileStream_1_1.value;
78
- _d = false;
79
- const chunk = _c;
80
- currentChunk = Buffer.concat([currentChunk, chunk]);
81
- if (currentChunk.length >= CHUNK_SIZE) {
82
- chunks.push(Buffer.from(currentChunk));
83
- currentChunk = Buffer.alloc(0);
84
- }
85
- }
34
+ const CHUNK_SIZE = 20 * 1024 * 1024;
35
+ const MAX_PARALLEL_FILES = 30;
36
+ const MAX_RETRIES = 3;
37
+ // Process files in parallel batches
38
+ for (let i = 0; i < files.length; i += MAX_PARALLEL_FILES) {
39
+ const fileBatch = files.slice(i, i + MAX_PARALLEL_FILES);
40
+ yield Promise.all(fileBatch.map((file) => __awaiter(this, void 0, void 0, function* () {
41
+ const contentType = this.getContentType(file.filename);
42
+ const fileSize = file.size;
43
+ if (fileSize < CHUNK_SIZE) {
44
+ return this.uploadSmallFile(folder, file, contentType);
86
45
  }
87
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
88
- finally {
89
- try {
90
- if (!_d && !_a && (_b = fileStream_1.return)) yield _b.call(fileStream_1);
46
+ let multipartUpload;
47
+ try {
48
+ // Initiate multipart upload
49
+ multipartUpload = yield new Promise((resolve, reject) => {
50
+ this.s3.createMultipartUpload({
51
+ Bucket: this.bucketName,
52
+ Key: `${folder}/${file.filename}`,
53
+ ContentType: contentType,
54
+ ContentDisposition: 'inline',
55
+ ServerSideEncryption: 'AES256',
56
+ }, (err, data) => {
57
+ if (err)
58
+ reject(err);
59
+ else
60
+ resolve(data);
61
+ });
62
+ });
63
+ const uploadId = multipartUpload.UploadId;
64
+ const parts = [];
65
+ // Split buffer into chunks
66
+ const buffer = file.buffer;
67
+ const chunks = [];
68
+ for (let i = 0; i < buffer.length; i += CHUNK_SIZE) {
69
+ chunks.push(buffer.slice(i, i + CHUNK_SIZE));
91
70
  }
92
- finally { if (e_1) throw e_1.error; }
93
- }
94
- if (currentChunk.length > 0) {
95
- chunks.push(Buffer.from(currentChunk));
96
- }
97
- // Upload chunks in parallel batches
98
- for (let i = 0; i < chunks.length; i += MAX_PARALLEL_CHUNKS) {
99
- const batch = chunks.slice(i, i + MAX_PARALLEL_CHUNKS);
100
- const partUploads = batch.map((chunkBuffer, index) => {
101
- const partNumber = i + index + 1;
102
- return new Promise((resolve, reject) => {
103
- this.s3.uploadPart({
104
- Bucket: this.bucketName,
105
- Key: `${folder}/${file.filename}`,
106
- PartNumber: partNumber,
107
- UploadId: uploadId,
108
- Body: chunkBuffer,
109
- }, (err, data) => {
110
- if (err)
111
- reject(err);
112
- else
113
- resolve({
71
+ // Upload chunks
72
+ const partUploads = chunks.map((chunk, index) => {
73
+ const partNumber = index + 1;
74
+ const uploadChunkWithRetry = (retryCount = 0) => __awaiter(this, void 0, void 0, function* () {
75
+ try {
76
+ const uploadPromise = new Promise((resolve, reject) => {
77
+ this.s3.uploadPart({
78
+ Bucket: this.bucketName,
79
+ Key: `${folder}/${file.filename}`,
114
80
  PartNumber: partNumber,
115
- ETag: data.ETag
81
+ UploadId: uploadId,
82
+ Body: chunk,
83
+ }, (err, data) => {
84
+ if (err)
85
+ reject(err);
86
+ else
87
+ resolve({
88
+ PartNumber: partNumber,
89
+ ETag: data.ETag
90
+ });
116
91
  });
117
- });
92
+ });
93
+ const timeoutPromise = new Promise((_, reject) => {
94
+ setTimeout(() => reject(new Error('Upload timeout')), 30000);
95
+ });
96
+ return yield Promise.race([uploadPromise, timeoutPromise]);
97
+ }
98
+ catch (error) {
99
+ if (retryCount < MAX_RETRIES) {
100
+ yield new Promise(resolve => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
101
+ return uploadChunkWithRetry(retryCount + 1);
102
+ }
103
+ throw error;
104
+ }
118
105
  });
106
+ return uploadChunkWithRetry();
119
107
  });
120
108
  const completedParts = yield Promise.all(partUploads);
121
109
  parts.push(...completedParts.sort((a, b) => { var _a, _b; return ((_a = a.PartNumber) !== null && _a !== void 0 ? _a : 0) - ((_b = b.PartNumber) !== null && _b !== void 0 ? _b : 0); }));
122
- }
123
- // Complete multipart upload
124
- yield new Promise((resolve, reject) => {
125
- this.s3.completeMultipartUpload({
126
- Bucket: this.bucketName,
127
- Key: `${folder}/${file.filename}`,
128
- UploadId: uploadId,
129
- MultipartUpload: { Parts: parts }
130
- }, (err, data) => {
131
- if (err)
132
- reject(err);
133
- else
134
- resolve(data);
135
- });
136
- });
137
- }
138
- catch (error) {
139
- // Attempt to abort multipart upload if it fails
140
- if (multipartUpload === null || multipartUpload === void 0 ? void 0 : multipartUpload.UploadId) {
141
- yield new Promise((resolve) => {
142
- this.s3.abortMultipartUpload({
110
+ // Complete multipart upload
111
+ yield new Promise((resolve, reject) => {
112
+ this.s3.completeMultipartUpload({
143
113
  Bucket: this.bucketName,
144
114
  Key: `${folder}/${file.filename}`,
145
- UploadId: multipartUpload.UploadId
146
- }, () => resolve(null));
115
+ UploadId: uploadId,
116
+ MultipartUpload: { Parts: parts }
117
+ }, (err, data) => {
118
+ if (err)
119
+ reject(err);
120
+ else
121
+ resolve(data);
122
+ });
147
123
  });
148
124
  }
149
- throw error;
150
- }
151
- })));
125
+ catch (error) {
126
+ if (multipartUpload === null || multipartUpload === void 0 ? void 0 : multipartUpload.UploadId) {
127
+ yield new Promise((resolve) => {
128
+ this.s3.abortMultipartUpload({
129
+ Bucket: this.bucketName,
130
+ Key: `${folder}/${file.filename}`,
131
+ UploadId: multipartUpload.UploadId
132
+ }, () => resolve(null));
133
+ });
134
+ }
135
+ throw error;
136
+ }
137
+ })));
138
+ }
152
139
  });
153
140
  }
154
141
  uploadSmallFile(folder, file, contentType) {
155
142
  return __awaiter(this, void 0, void 0, function* () {
156
- const fileStream = fs_1.default.createReadStream(path_1.default.resolve(`media/${folder}/${file.filename}`));
157
- try {
158
- yield new Promise((resolve, reject) => {
159
- this.s3.putObject({
160
- Bucket: this.bucketName,
161
- Key: `${folder}/${file.filename}`,
162
- Body: fileStream,
163
- ContentDisposition: 'inline',
164
- ContentType: contentType,
165
- ServerSideEncryption: 'AES256',
166
- }, (err, data) => {
167
- fileStream.destroy();
168
- if (err)
169
- reject(err);
170
- else
171
- resolve(data);
172
- });
143
+ yield new Promise((resolve, reject) => {
144
+ this.s3.putObject({
145
+ Bucket: this.bucketName,
146
+ Key: `${folder}/${file.filename}`,
147
+ Body: file.buffer,
148
+ ContentDisposition: 'inline',
149
+ ContentType: contentType,
150
+ ServerSideEncryption: 'AES256',
151
+ }, (err, data) => {
152
+ if (err)
153
+ reject(err);
154
+ else
155
+ resolve(data);
173
156
  });
174
- }
175
- catch (error) {
176
- fileStream.destroy();
177
- throw error;
178
- }
157
+ });
179
158
  });
180
159
  }
181
160
  removeBucketFiles(...filePaths) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duvdu-v1/duvdu",
3
- "version": "1.1.212",
3
+ "version": "1.1.214",
4
4
  "main": "./build/index.js",
5
5
  "types": "./build/index.d.ts",
6
6
  "files": [