@duvdu-v1/duvdu 1.1.213 → 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,74 +31,55 @@ class Bucket {
39
31
  }
40
32
  saveBucketFiles(folder, ...files) {
41
33
  return __awaiter(this, void 0, void 0, function* () {
42
- const CHUNK_SIZE = 50 * 1024 * 1024; // 50MB chunks
43
- const MAX_PARALLEL_CHUNKS = 8; // Increased parallel uploads
34
+ const CHUNK_SIZE = 20 * 1024 * 1024;
35
+ const MAX_PARALLEL_FILES = 30;
44
36
  const MAX_RETRIES = 3;
45
- yield Promise.all(files.map((file) => __awaiter(this, void 0, void 0, function* () {
46
- var _a, e_1, _b, _c;
47
- const filePath = path_1.default.resolve(`media/${folder}/${file.filename}`);
48
- const fileSize = fs_1.default.statSync(filePath).size;
49
- const contentType = this.getContentType(file.filename);
50
- if (fileSize < CHUNK_SIZE) {
51
- return this.uploadSmallFile(folder, file, contentType);
52
- }
53
- let multipartUpload;
54
- try {
55
- // Initiate multipart upload
56
- multipartUpload = yield new Promise((resolve, reject) => {
57
- this.s3.createMultipartUpload({
58
- Bucket: this.bucketName,
59
- Key: `${folder}/${file.filename}`,
60
- ContentType: contentType,
61
- ContentDisposition: 'inline',
62
- ServerSideEncryption: 'AES256',
63
- }, (err, data) => {
64
- if (err)
65
- reject(err);
66
- else
67
- resolve(data);
68
- });
69
- });
70
- const uploadId = multipartUpload.UploadId;
71
- const parts = [];
72
- // Prepare chunks for upload
73
- const chunks = [];
74
- let chunkIndex = 0;
75
- const fileStream = fs_1.default.createReadStream(filePath, {
76
- highWaterMark: CHUNK_SIZE // Optimize read buffer size
77
- });
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);
45
+ }
46
+ let multipartUpload;
78
47
  try {
79
- 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) {
80
- _c = fileStream_1_1.value;
81
- _d = false;
82
- const chunk = _c;
83
- chunks.push({
84
- buffer: Buffer.from(chunk),
85
- index: chunkIndex++
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);
86
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));
87
70
  }
88
- }
89
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
90
- finally {
91
- try {
92
- if (!_d && !_a && (_b = fileStream_1.return)) yield _b.call(fileStream_1);
93
- }
94
- finally { if (e_1) throw e_1.error; }
95
- }
96
- // Upload chunks in parallel batches with retry logic
97
- for (let i = 0; i < chunks.length; i += MAX_PARALLEL_CHUNKS) {
98
- const batch = chunks.slice(i, i + MAX_PARALLEL_CHUNKS);
99
- const partUploads = batch.map(({ buffer, index }) => {
71
+ // Upload chunks
72
+ const partUploads = chunks.map((chunk, index) => {
100
73
  const partNumber = index + 1;
101
74
  const uploadChunkWithRetry = (retryCount = 0) => __awaiter(this, void 0, void 0, function* () {
102
75
  try {
103
- return yield new Promise((resolve, reject) => {
76
+ const uploadPromise = new Promise((resolve, reject) => {
104
77
  this.s3.uploadPart({
105
78
  Bucket: this.bucketName,
106
79
  Key: `${folder}/${file.filename}`,
107
80
  PartNumber: partNumber,
108
81
  UploadId: uploadId,
109
- Body: buffer,
82
+ Body: chunk,
110
83
  }, (err, data) => {
111
84
  if (err)
112
85
  reject(err);
@@ -117,10 +90,13 @@ class Bucket {
117
90
  });
118
91
  });
119
92
  });
93
+ const timeoutPromise = new Promise((_, reject) => {
94
+ setTimeout(() => reject(new Error('Upload timeout')), 30000);
95
+ });
96
+ return yield Promise.race([uploadPromise, timeoutPromise]);
120
97
  }
121
98
  catch (error) {
122
99
  if (retryCount < MAX_RETRIES) {
123
- // Exponential backoff
124
100
  yield new Promise(resolve => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
125
101
  return uploadChunkWithRetry(retryCount + 1);
126
102
  }
@@ -131,63 +107,54 @@ class Bucket {
131
107
  });
132
108
  const completedParts = yield Promise.all(partUploads);
133
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); }));
134
- }
135
- // Complete multipart upload
136
- yield new Promise((resolve, reject) => {
137
- this.s3.completeMultipartUpload({
138
- Bucket: this.bucketName,
139
- Key: `${folder}/${file.filename}`,
140
- UploadId: uploadId,
141
- MultipartUpload: { Parts: parts }
142
- }, (err, data) => {
143
- if (err)
144
- reject(err);
145
- else
146
- resolve(data);
147
- });
148
- });
149
- }
150
- catch (error) {
151
- // Attempt to abort multipart upload if it fails
152
- if (multipartUpload === null || multipartUpload === void 0 ? void 0 : multipartUpload.UploadId) {
153
- yield new Promise((resolve) => {
154
- this.s3.abortMultipartUpload({
110
+ // Complete multipart upload
111
+ yield new Promise((resolve, reject) => {
112
+ this.s3.completeMultipartUpload({
155
113
  Bucket: this.bucketName,
156
114
  Key: `${folder}/${file.filename}`,
157
- UploadId: multipartUpload.UploadId
158
- }, () => resolve(null));
115
+ UploadId: uploadId,
116
+ MultipartUpload: { Parts: parts }
117
+ }, (err, data) => {
118
+ if (err)
119
+ reject(err);
120
+ else
121
+ resolve(data);
122
+ });
159
123
  });
160
124
  }
161
- throw error;
162
- }
163
- })));
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
+ }
164
139
  });
165
140
  }
166
141
  uploadSmallFile(folder, file, contentType) {
167
142
  return __awaiter(this, void 0, void 0, function* () {
168
- const fileStream = fs_1.default.createReadStream(path_1.default.resolve(`media/${folder}/${file.filename}`));
169
- try {
170
- yield new Promise((resolve, reject) => {
171
- this.s3.putObject({
172
- Bucket: this.bucketName,
173
- Key: `${folder}/${file.filename}`,
174
- Body: fileStream,
175
- ContentDisposition: 'inline',
176
- ContentType: contentType,
177
- ServerSideEncryption: 'AES256',
178
- }, (err, data) => {
179
- fileStream.destroy();
180
- if (err)
181
- reject(err);
182
- else
183
- resolve(data);
184
- });
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);
185
156
  });
186
- }
187
- catch (error) {
188
- fileStream.destroy();
189
- throw error;
190
- }
157
+ });
191
158
  });
192
159
  }
193
160
  removeBucketFiles(...filePaths) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duvdu-v1/duvdu",
3
- "version": "1.1.213",
3
+ "version": "1.1.214",
4
4
  "main": "./build/index.js",
5
5
  "types": "./build/index.d.ts",
6
6
  "files": [