@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
|
|
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?:
|
|
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
|
-
|
|
8
|
-
|
|
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.
|
|
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
|
package/build/utils/bucket.js
CHANGED
|
@@ -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;
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
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
|
-
|
|
124
|
-
|
|
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:
|
|
146
|
-
|
|
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
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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) {
|