@sanvika/cloudinary 0.2.1 → 0.2.3
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.
- package/dist/index.js +111 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -228,6 +228,13 @@ async function getAppName() {
|
|
|
228
228
|
await ensureConfigured();
|
|
229
229
|
return _appName;
|
|
230
230
|
}
|
|
231
|
+
function stripAppPrefix(folder, appName) {
|
|
232
|
+
if (!folder || !appName) return folder || "";
|
|
233
|
+
const prefix = `${appName}/`;
|
|
234
|
+
if (folder === appName) return "";
|
|
235
|
+
if (folder.startsWith(prefix)) return folder.slice(prefix.length);
|
|
236
|
+
return folder;
|
|
237
|
+
}
|
|
231
238
|
async function uploadImage(fileOrBuffer, options = {}) {
|
|
232
239
|
await ensureConfigured();
|
|
233
240
|
const {
|
|
@@ -247,8 +254,9 @@ async function uploadImage(fileOrBuffer, options = {}) {
|
|
|
247
254
|
maxAttempts = 3
|
|
248
255
|
} = options;
|
|
249
256
|
const eagerValue = eager || transforms;
|
|
257
|
+
const normalizedFolder = stripAppPrefix(folder || "", _appName);
|
|
250
258
|
const uploadOptions = {
|
|
251
|
-
folder:
|
|
259
|
+
folder: normalizedFolder,
|
|
252
260
|
resourceType,
|
|
253
261
|
tags: [_appName, ...tags],
|
|
254
262
|
eager: eagerValue,
|
|
@@ -268,7 +276,7 @@ async function uploadImage(fileOrBuffer, options = {}) {
|
|
|
268
276
|
});
|
|
269
277
|
}
|
|
270
278
|
const cloudinary = await loadLegacyCloudinary();
|
|
271
|
-
const uploadFolder = getFolderPath(_appName,
|
|
279
|
+
const uploadFolder = getFolderPath(_appName, normalizedFolder);
|
|
272
280
|
const uploadOpts = {
|
|
273
281
|
folder: uploadFolder,
|
|
274
282
|
resource_type: resourceType,
|
|
@@ -321,14 +329,15 @@ async function uploadRawFile(buffer, options = {}) {
|
|
|
321
329
|
const { folder, filename, tags = [] } = options;
|
|
322
330
|
if (!buffer || !Buffer.isBuffer(buffer)) throw new Error("Buffer is required for uploadRawFile");
|
|
323
331
|
const baseName = filename ? filename.replace(/\.[^.]+$/, "") : "file";
|
|
332
|
+
const normalizedFolder = stripAppPrefix(folder || "", _appName);
|
|
324
333
|
if (isProxyMode()) {
|
|
325
334
|
return withRetry(
|
|
326
|
-
() => gatewayUpload(buffer, { folder:
|
|
335
|
+
() => gatewayUpload(buffer, { folder: normalizedFolder, resourceType: "raw", tags: ["raw", _appName, ...tags], publicId: baseName, overwrite: true, filename }),
|
|
327
336
|
{ operationName: "uploadRawFile", maxAttempts: 3 }
|
|
328
337
|
);
|
|
329
338
|
}
|
|
330
339
|
const cloudinary = await loadLegacyCloudinary();
|
|
331
|
-
const uploadFolder = getFolderPath(_appName,
|
|
340
|
+
const uploadFolder = getFolderPath(_appName, normalizedFolder);
|
|
332
341
|
return withRetry(
|
|
333
342
|
() => new Promise((resolve, reject) => {
|
|
334
343
|
const stream = cloudinary.uploader.upload_stream(
|
|
@@ -512,10 +521,103 @@ function verifySanvikaWebhookSignature({ headers, rawBody, secret, toleranceSec
|
|
|
512
521
|
const ok = crypto2.timingSafeEqual(a, b);
|
|
513
522
|
return { valid: ok, reason: ok ? void 0 : "signature_mismatch" };
|
|
514
523
|
}
|
|
524
|
+
|
|
525
|
+
// src/cloudinaryValidation.js
|
|
526
|
+
var MAX_IMAGE_SIZE = 5 * 1024 * 1024;
|
|
527
|
+
var MAX_PROFILE_PIC_SIZE = 6 * 1024 * 1024;
|
|
528
|
+
var MAX_VIDEO_SIZE = 100 * 1024 * 1024;
|
|
529
|
+
var SUPPORTED_IMAGE_FORMATS = [".jpg", ".jpeg", ".png", ".webp"];
|
|
530
|
+
var SUPPORTED_PROFILE_MIME_TYPES = ["image/jpeg", "image/jpg", "image/png"];
|
|
531
|
+
var SUPPORTED_VIDEO_FORMATS = [".mp4", ".mov", ".avi", ".wmv", ".flv", ".mkv", ".webm", ".m4v"];
|
|
532
|
+
function splitFileName(name) {
|
|
533
|
+
const safe = typeof name === "string" && name.trim() ? name.trim() : "";
|
|
534
|
+
const dotIdx = safe.lastIndexOf(".");
|
|
535
|
+
if (dotIdx <= 0) return { ext: "", base: safe || "asset" };
|
|
536
|
+
return { ext: safe.slice(dotIdx).toLowerCase(), base: safe.slice(0, dotIdx) };
|
|
537
|
+
}
|
|
538
|
+
function normalizeFolderPath(value, fallback) {
|
|
539
|
+
if (typeof value !== "string") return fallback;
|
|
540
|
+
const sanitized = value.trim().replace(/\\/g, "/").split("/").filter(Boolean).join("/");
|
|
541
|
+
return sanitized || fallback;
|
|
542
|
+
}
|
|
543
|
+
function createScopedPublicId({ baseName, userPrefix, timestamp }) {
|
|
544
|
+
const safeBaseName = baseName && baseName.trim() ? baseName.trim() : "asset";
|
|
545
|
+
const safePrefix = userPrefix && userPrefix.trim() ? userPrefix.trim() : "anon";
|
|
546
|
+
const safeTimestamp = timestamp && timestamp.trim() ? timestamp.trim() : Date.now().toString().slice(-6);
|
|
547
|
+
return `${safePrefix}_${safeTimestamp}_${safeBaseName}`;
|
|
548
|
+
}
|
|
549
|
+
function validateImageFile(file) {
|
|
550
|
+
const { ext: extFromName } = splitFileName(file.name || "");
|
|
551
|
+
const mimeExt = file.type ? `.${file.type.split("/")[1]}`.toLowerCase() : "";
|
|
552
|
+
const finalExt = extFromName || mimeExt;
|
|
553
|
+
if (!SUPPORTED_IMAGE_FORMATS.includes(finalExt)) {
|
|
554
|
+
return {
|
|
555
|
+
valid: false,
|
|
556
|
+
status: 415,
|
|
557
|
+
error: "INVALID_FORMAT",
|
|
558
|
+
message: `Unsupported format: ${finalExt}. Allowed: ${SUPPORTED_IMAGE_FORMATS.join(", ")}`
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
if (file.size > MAX_IMAGE_SIZE) {
|
|
562
|
+
return {
|
|
563
|
+
valid: false,
|
|
564
|
+
status: 413,
|
|
565
|
+
error: "FILE_TOO_LARGE",
|
|
566
|
+
message: `File size (${(file.size / 1024 / 1024).toFixed(2)}MB) exceeds the ${MAX_IMAGE_SIZE / 1024 / 1024}MB limit`
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
return { valid: true };
|
|
570
|
+
}
|
|
571
|
+
function validateProfilePicFile(file) {
|
|
572
|
+
if (!SUPPORTED_PROFILE_MIME_TYPES.includes(file.type)) {
|
|
573
|
+
return {
|
|
574
|
+
valid: false,
|
|
575
|
+
status: 415,
|
|
576
|
+
error: "INVALID_FORMAT",
|
|
577
|
+
message: "Unsupported format. Only JPG, JPEG, and PNG are allowed."
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
if (file.size > MAX_PROFILE_PIC_SIZE) {
|
|
581
|
+
return {
|
|
582
|
+
valid: false,
|
|
583
|
+
status: 413,
|
|
584
|
+
error: "FILE_TOO_LARGE",
|
|
585
|
+
message: `File size too large. Maximum is ${MAX_PROFILE_PIC_SIZE / 1024 / 1024}MB.`
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
return { valid: true };
|
|
589
|
+
}
|
|
590
|
+
function validateVideoFile(file) {
|
|
591
|
+
const { ext } = splitFileName(file.name || "");
|
|
592
|
+
if (!SUPPORTED_VIDEO_FORMATS.includes(ext)) {
|
|
593
|
+
return {
|
|
594
|
+
valid: false,
|
|
595
|
+
status: 415,
|
|
596
|
+
error: "INVALID_FORMAT",
|
|
597
|
+
message: `Unsupported format: ${ext}. Allowed: ${SUPPORTED_VIDEO_FORMATS.join(", ")}`
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
if (file.size > MAX_VIDEO_SIZE) {
|
|
601
|
+
return {
|
|
602
|
+
valid: false,
|
|
603
|
+
status: 413,
|
|
604
|
+
error: "FILE_TOO_LARGE",
|
|
605
|
+
message: `File size (${(file.size / 1024 / 1024).toFixed(2)}MB) exceeds the ${MAX_VIDEO_SIZE / 1024 / 1024}MB limit`
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
return { valid: true };
|
|
609
|
+
}
|
|
515
610
|
export {
|
|
516
611
|
CloudinaryError,
|
|
612
|
+
MAX_IMAGE_SIZE,
|
|
613
|
+
MAX_PROFILE_PIC_SIZE,
|
|
614
|
+
MAX_VIDEO_SIZE,
|
|
615
|
+
SUPPORTED_IMAGE_FORMATS,
|
|
616
|
+
SUPPORTED_PROFILE_MIME_TYPES,
|
|
617
|
+
SUPPORTED_VIDEO_FORMATS,
|
|
517
618
|
TRANSFORM_PRESETS,
|
|
518
619
|
configureSanvikaCloudinary,
|
|
620
|
+
createScopedPublicId,
|
|
519
621
|
deleteImage,
|
|
520
622
|
deleteImages,
|
|
521
623
|
extractPublicId,
|
|
@@ -530,14 +632,19 @@ export {
|
|
|
530
632
|
isCloudinaryUrl,
|
|
531
633
|
isProxyMode,
|
|
532
634
|
isRetriableError,
|
|
635
|
+
normalizeFolderPath,
|
|
533
636
|
pingCloudinary,
|
|
534
637
|
runCloudinaryDiagnostics,
|
|
638
|
+
splitFileName,
|
|
535
639
|
testCloudinaryWebhookSignature,
|
|
536
640
|
uploadImage,
|
|
537
641
|
uploadImages,
|
|
538
642
|
uploadRawFile,
|
|
539
643
|
uploadVideo,
|
|
644
|
+
validateImageFile,
|
|
645
|
+
validateProfilePicFile,
|
|
540
646
|
validatePublicId,
|
|
647
|
+
validateVideoFile,
|
|
541
648
|
verifySanvikaWebhookSignature,
|
|
542
649
|
withRetry
|
|
543
650
|
};
|
package/package.json
CHANGED