@duvdu-v1/duvdu 1.1.205 → 1.1.207

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.
@@ -70,7 +70,8 @@ const userSchema = new mongoose_1.Schema({
70
70
  color: { type: String, default: null },
71
71
  },
72
72
  projectsView: { type: Number, default: 0 },
73
- haveInvitation: { type: Boolean, default: false }
73
+ haveInvitation: { type: Boolean, default: false },
74
+ faceRecognition: { type: String, default: null },
74
75
  }, {
75
76
  timestamps: true,
76
77
  collection: model_names_1.MODELS.user,
@@ -80,6 +81,8 @@ const userSchema = new mongoose_1.Schema({
80
81
  ret.coverImage = process.env.BUCKET_HOST + '/' + ret.coverImage;
81
82
  if (ret.profileImage)
82
83
  ret.profileImage = process.env.BUCKET_HOST + '/' + ret.profileImage;
84
+ if (ret.faceRecognition)
85
+ ret.faceRecognition = process.env.BUCKET_HOST + '/' + ret.faceRecognition;
83
86
  },
84
87
  },
85
88
  })
@@ -103,4 +103,5 @@ export interface Iuser {
103
103
  };
104
104
  projectsView: number;
105
105
  haveInvitation: boolean;
106
+ faceRecognition: string | null;
106
107
  }
@@ -39,17 +39,16 @@ class Bucket {
39
39
  }
40
40
  saveBucketFiles(folder, ...files) {
41
41
  return __awaiter(this, void 0, void 0, function* () {
42
- const CHUNK_SIZE = 5 * 1024 * 1024; // 5MB chunks (AWS minimum)
42
+ const CHUNK_SIZE = 20 * 1024 * 1024; // 20MB chunks for better performance
43
+ const MAX_PARALLEL_CHUNKS = 4; // Control parallel uploads
43
44
  yield Promise.all(files.map((file) => __awaiter(this, void 0, void 0, function* () {
44
45
  var _a, e_1, _b, _c;
45
46
  const filePath = path_1.default.resolve(`media/${folder}/${file.filename}`);
46
47
  const fileSize = fs_1.default.statSync(filePath).size;
47
48
  const contentType = this.getContentType(file.filename);
48
- // Use regular upload for small files (less than 15MB)
49
- if (fileSize < CHUNK_SIZE * 3) {
49
+ if (fileSize < CHUNK_SIZE) {
50
50
  return this.uploadSmallFile(folder, file, contentType);
51
51
  }
52
- // Use multipart upload for large files
53
52
  let multipartUpload;
54
53
  try {
55
54
  // Initiate multipart upload
@@ -69,37 +68,19 @@ class Bucket {
69
68
  });
70
69
  const uploadId = multipartUpload.UploadId;
71
70
  const parts = [];
72
- const fileStream = fs_1.default.createReadStream(filePath, { highWaterMark: CHUNK_SIZE });
73
- let partNumber = 1;
74
- let buffer = Buffer.alloc(0);
71
+ const fileStream = fs_1.default.createReadStream(filePath);
72
+ // Prepare chunks for parallel upload
73
+ const chunks = [];
74
+ let currentChunk = Buffer.alloc(0);
75
75
  try {
76
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
77
  _c = fileStream_1_1.value;
78
78
  _d = false;
79
79
  const chunk = _c;
80
- buffer = Buffer.concat([buffer, chunk]);
81
- // Upload when buffer reaches CHUNK_SIZE or it's the last chunk
82
- if (buffer.length >= CHUNK_SIZE || partNumber * CHUNK_SIZE >= fileSize) {
83
- const partData = yield new Promise((resolve, reject) => {
84
- this.s3.uploadPart({
85
- Bucket: this.bucketName,
86
- Key: `${folder}/${file.filename}`,
87
- PartNumber: partNumber,
88
- UploadId: uploadId,
89
- Body: buffer,
90
- }, (err, data) => {
91
- if (err)
92
- reject(err);
93
- else
94
- resolve(data);
95
- });
96
- });
97
- parts.push({
98
- PartNumber: partNumber,
99
- ETag: partData.ETag
100
- });
101
- buffer = Buffer.alloc(0);
102
- partNumber++;
80
+ currentChunk = Buffer.concat([currentChunk, chunk]);
81
+ if (currentChunk.length >= CHUNK_SIZE) {
82
+ chunks.push(Buffer.from(currentChunk));
83
+ currentChunk = Buffer.alloc(0);
103
84
  }
104
85
  }
105
86
  }
@@ -110,6 +91,35 @@ class Bucket {
110
91
  }
111
92
  finally { if (e_1) throw e_1.error; }
112
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({
114
+ PartNumber: partNumber,
115
+ ETag: data.ETag
116
+ });
117
+ });
118
+ });
119
+ });
120
+ const completedParts = yield Promise.all(partUploads);
121
+ 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
+ }
113
123
  // Complete multipart upload
114
124
  yield new Promise((resolve, reject) => {
115
125
  this.s3.completeMultipartUpload({
@@ -132,7 +142,7 @@ class Bucket {
132
142
  this.s3.abortMultipartUpload({
133
143
  Bucket: this.bucketName,
134
144
  Key: `${folder}/${file.filename}`,
135
- UploadId: multipartUpload.UploadId // Add non-null assertion here
145
+ UploadId: multipartUpload.UploadId
136
146
  }, () => resolve(null));
137
147
  });
138
148
  }
@@ -170,7 +180,7 @@ class Bucket {
170
180
  }
171
181
  removeBucketFiles(...filePaths) {
172
182
  return __awaiter(this, void 0, void 0, function* () {
173
- const BATCH_SIZE = 1000; // AWS limit is 1000 objects per delete operation
183
+ const BATCH_SIZE = 1000;
174
184
  for (let i = 0; i < filePaths.length; i += BATCH_SIZE) {
175
185
  const batch = filePaths.slice(i, i + BATCH_SIZE);
176
186
  yield new Promise((resolve, reject) => {
@@ -189,61 +199,12 @@ class Bucket {
189
199
  }
190
200
  getContentType(filename) {
191
201
  const ext = path_1.default.extname(filename).toLowerCase();
192
- switch (ext) {
193
- // Video Types
194
- case '.mp4':
195
- return 'video/mp4';
196
- case '.webm':
197
- return 'video/webm';
198
- case '.ogg':
199
- return 'video/ogg';
200
- case '.avi':
201
- return 'video/x-msvideo';
202
- case '.mov':
203
- return 'video/quicktime';
204
- case '.wmv':
205
- return 'video/x-ms-wmv';
206
- case '.mkv':
207
- return 'video/x-matroska';
208
- case '.flv':
209
- return 'video/x-flv';
210
- // Audio Types
211
- case '.mp3':
212
- return 'audio/mpeg';
213
- case '.wav':
214
- return 'audio/wav';
215
- case '.aac':
216
- return 'audio/aac';
217
- case '.flac':
218
- return 'audio/flac';
219
- case '.m4a':
220
- return 'audio/x-m4a';
221
- case '.wma':
222
- return 'audio/x-ms-wma';
223
- // Image Types
224
- case '.jpg':
225
- case '.jpeg':
226
- return 'image/jpeg';
227
- case '.png':
228
- return 'image/png';
229
- case '.gif':
230
- return 'image/gif';
231
- case '.bmp':
232
- return 'image/bmp';
233
- case '.webp':
234
- return 'image/webp';
235
- case '.svg':
236
- return 'image/svg+xml';
237
- case '.tiff':
238
- case '.tif':
239
- return 'image/tiff';
240
- // PDF and other document types
241
- case '.pdf':
242
- return 'application/pdf';
243
- // Default fallback
244
- default:
245
- return 'application/octet-stream'; // Fallback for unknown types
202
+ for (const category of Object.values(MIME_TYPES)) {
203
+ if (ext in category) {
204
+ return category[ext];
205
+ }
246
206
  }
207
+ return 'application/octet-stream';
247
208
  }
248
209
  validateFace(imageKey) {
249
210
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
@@ -259,7 +220,6 @@ class Bucket {
259
220
  Attributes: ['ALL']
260
221
  };
261
222
  const result = yield this.rekognition.detectFaces(params).promise();
262
- // Check if exactly one face is detected
263
223
  if (!result.FaceDetails || result.FaceDetails.length === 0) {
264
224
  return {
265
225
  isValid: false,
@@ -269,25 +229,22 @@ class Bucket {
269
229
  if (result.FaceDetails.length > 1) {
270
230
  return {
271
231
  isValid: false,
272
- error: { en: 'Multiple faces detected in the image', ar: 'تم الكشف عن أكثر من وجه ف�� الصورة' }
232
+ error: { en: 'Multiple faces detected in the image', ar: 'تم الكشف عن أكثر من وجه في الصورة' }
273
233
  };
274
234
  }
275
235
  const face = result.FaceDetails[0];
276
- // Stricter confidence threshold (95%)
277
236
  if (!face.Confidence || face.Confidence < 95) {
278
237
  return {
279
238
  isValid: false,
280
239
  error: { en: 'Image quality is not sufficient', ar: 'جودة الصورة غير كافية' }
281
240
  };
282
241
  }
283
- // Check for sunglasses or eyeglasses
284
242
  if (((_a = face.Sunglasses) === null || _a === void 0 ? void 0 : _a.Value) || ((_b = face.Eyeglasses) === null || _b === void 0 ? void 0 : _b.Value)) {
285
243
  return {
286
244
  isValid: false,
287
245
  error: { en: 'Please remove glasses or sunglasses', ar: 'يرجى إزالة النظارات أو النظارات الشمسية' }
288
246
  };
289
247
  }
290
- // Check if eyes are open with higher confidence
291
248
  const leftEyeOpen = ((_c = face.EyesOpen) === null || _c === void 0 ? void 0 : _c.Value) && ((_e = (_d = face.EyesOpen) === null || _d === void 0 ? void 0 : _d.Confidence) !== null && _e !== void 0 ? _e : 0) > 95;
292
249
  const rightEyeOpen = ((_f = face.EyesOpen) === null || _f === void 0 ? void 0 : _f.Value) && ((_h = (_g = face.EyesOpen) === null || _g === void 0 ? void 0 : _g.Confidence) !== null && _h !== void 0 ? _h : 0) > 95;
293
250
  if (!leftEyeOpen || !rightEyeOpen) {
@@ -296,16 +253,14 @@ class Bucket {
296
253
  error: { en: 'Eyes must be fully open and clearly visible', ar: 'يجب أن تكون العينين مفتوحتين بالكامل وواضحتين' }
297
254
  };
298
255
  }
299
- // Check for mouth open
300
256
  if ((_j = face.MouthOpen) === null || _j === void 0 ? void 0 : _j.Value) {
301
257
  return {
302
258
  isValid: false,
303
259
  error: { en: 'Mouth should be closed', ar: 'يجب أن يكون الفم مغلقاً' }
304
260
  };
305
261
  }
306
- // Stricter face orientation check
307
262
  const pose = face.Pose;
308
- const poseThreshold = 15; // Stricter threshold (15 degrees)
263
+ const poseThreshold = 15;
309
264
  if (Math.abs((pose === null || pose === void 0 ? void 0 : pose.Pitch) || 0) > poseThreshold ||
310
265
  Math.abs((pose === null || pose === void 0 ? void 0 : pose.Roll) || 0) > poseThreshold ||
311
266
  Math.abs((pose === null || pose === void 0 ? void 0 : pose.Yaw) || 0) > poseThreshold) {
@@ -314,14 +269,12 @@ class Bucket {
314
269
  error: { en: 'Face must be directly facing the camera', ar: 'يجب أن يكون الوجه مواجهاً للكاميرا مباشرة' }
315
270
  };
316
271
  }
317
- // Check for facial occlusions
318
272
  if ((_k = face.FaceOccluded) === null || _k === void 0 ? void 0 : _k.Value) {
319
273
  return {
320
274
  isValid: false,
321
275
  error: { en: 'Face must be fully visible without any coverings', ar: 'يجب أن يكون الوجه مرئياً بالكامل بدون أي أغطية' }
322
276
  };
323
277
  }
324
- // Check for neutral expression
325
278
  if ((_l = face.Smile) === null || _l === void 0 ? void 0 : _l.Value) {
326
279
  return {
327
280
  isValid: false,
@@ -341,3 +294,37 @@ class Bucket {
341
294
  }
342
295
  }
343
296
  exports.Bucket = Bucket;
297
+ const MIME_TYPES = {
298
+ video: {
299
+ '.mp4': 'video/mp4',
300
+ '.webm': 'video/webm',
301
+ '.ogg': 'video/ogg',
302
+ '.avi': 'video/x-msvideo',
303
+ '.mov': 'video/quicktime',
304
+ '.wmv': 'video/x-ms-wmv',
305
+ '.mkv': 'video/x-matroska',
306
+ '.flv': 'video/x-flv'
307
+ },
308
+ audio: {
309
+ '.mp3': 'audio/mpeg',
310
+ '.wav': 'audio/wav',
311
+ '.aac': 'audio/aac',
312
+ '.flac': 'audio/flac',
313
+ '.m4a': 'audio/x-m4a',
314
+ '.wma': 'audio/x-ms-wma'
315
+ },
316
+ image: {
317
+ '.jpg': 'image/jpeg',
318
+ '.jpeg': 'image/jpeg',
319
+ '.png': 'image/png',
320
+ '.gif': 'image/gif',
321
+ '.bmp': 'image/bmp',
322
+ '.webp': 'image/webp',
323
+ '.svg': 'image/svg+xml',
324
+ '.tiff': 'image/tiff',
325
+ '.tif': 'image/tiff'
326
+ },
327
+ document: {
328
+ '.pdf': 'application/pdf'
329
+ }
330
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duvdu-v1/duvdu",
3
- "version": "1.1.205",
3
+ "version": "1.1.207",
4
4
  "main": "./build/index.js",
5
5
  "types": "./build/index.d.ts",
6
6
  "files": [