@sanvika/cloudinary 0.2.0 → 0.2.2
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 +108 -10
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -110,14 +110,14 @@ var TRANSFORM_PRESETS = {
|
|
|
110
110
|
|
|
111
111
|
// src/gatewayClient.js
|
|
112
112
|
function isProxyMode() {
|
|
113
|
-
return Boolean(process.env.
|
|
113
|
+
return Boolean(process.env.CLOUDINARY_GATEWAY_URL && process.env.CLOUDINARY_TOKEN);
|
|
114
114
|
}
|
|
115
115
|
function requireProxyEnv() {
|
|
116
|
-
const base = process.env.
|
|
117
|
-
const token = process.env.
|
|
116
|
+
const base = process.env.CLOUDINARY_GATEWAY_URL;
|
|
117
|
+
const token = process.env.CLOUDINARY_TOKEN;
|
|
118
118
|
if (!base || !token) {
|
|
119
119
|
throw new CloudinaryError(
|
|
120
|
-
"Proxy gateway env missing. Set
|
|
120
|
+
"Proxy gateway env missing. Set CLOUDINARY_GATEWAY_URL and CLOUDINARY_TOKEN.",
|
|
121
121
|
"gateway_env"
|
|
122
122
|
);
|
|
123
123
|
}
|
|
@@ -192,7 +192,7 @@ async function loadLegacyCloudinary() {
|
|
|
192
192
|
return _cloudinary;
|
|
193
193
|
}
|
|
194
194
|
async function configureSanvikaCloudinary({ appName, cloudName, apiKey, apiSecret } = {}) {
|
|
195
|
-
const resolvedApp = appName || process.env.
|
|
195
|
+
const resolvedApp = appName || process.env.CLOUDINARY_APP_NAME;
|
|
196
196
|
if (!resolvedApp) throw new Error("appName is required for configureSanvikaCloudinary");
|
|
197
197
|
_appName = resolvedApp;
|
|
198
198
|
if (isProxyMode()) {
|
|
@@ -204,7 +204,7 @@ async function configureSanvikaCloudinary({ appName, cloudName, apiKey, apiSecre
|
|
|
204
204
|
const as = apiSecret || process.env.CLOUDINARY_API_SECRET;
|
|
205
205
|
if (!cn || !ak || !as) {
|
|
206
206
|
throw new Error(
|
|
207
|
-
"Cloudinary credentials missing. Either use proxy mode (
|
|
207
|
+
"Cloudinary credentials missing. Either use proxy mode (CLOUDINARY_GATEWAY_URL + CLOUDINARY_TOKEN) or legacy env (CLOUDINARY_CLOUD_NAME, CLOUDINARY_API_KEY, CLOUDINARY_API_SECRET)."
|
|
208
208
|
);
|
|
209
209
|
}
|
|
210
210
|
const cloudinary = await loadLegacyCloudinary();
|
|
@@ -214,12 +214,12 @@ async function configureSanvikaCloudinary({ appName, cloudName, apiKey, apiSecre
|
|
|
214
214
|
async function ensureConfigured() {
|
|
215
215
|
if (_configured) return;
|
|
216
216
|
if (isProxyMode()) {
|
|
217
|
-
_appName = _appName || process.env.
|
|
217
|
+
_appName = _appName || process.env.CLOUDINARY_APP_NAME || "sanvika-app";
|
|
218
218
|
_configured = true;
|
|
219
219
|
return;
|
|
220
220
|
}
|
|
221
|
-
if (process.env.
|
|
222
|
-
await configureSanvikaCloudinary({ appName: process.env.
|
|
221
|
+
if (process.env.CLOUDINARY_APP_NAME) {
|
|
222
|
+
await configureSanvikaCloudinary({ appName: process.env.CLOUDINARY_APP_NAME });
|
|
223
223
|
return;
|
|
224
224
|
}
|
|
225
225
|
throw new Error("Call configureSanvikaCloudinary({ appName }) before using SDK operations.");
|
|
@@ -496,7 +496,7 @@ function verifySanvikaWebhookSignature({ headers, rawBody, secret, toleranceSec
|
|
|
496
496
|
};
|
|
497
497
|
const sig = String(getHeader("x-sanvika-signature") || "").trim();
|
|
498
498
|
const ts = String(getHeader("x-sanvika-timestamp") || "").trim();
|
|
499
|
-
const token = secret || process.env.
|
|
499
|
+
const token = secret || process.env.CLOUDINARY_TOKEN;
|
|
500
500
|
if (!token) return { valid: false, reason: "missing_secret" };
|
|
501
501
|
if (!sig) return { valid: false, reason: "missing_signature" };
|
|
502
502
|
if (!ts) return { valid: false, reason: "missing_timestamp" };
|
|
@@ -512,10 +512,103 @@ function verifySanvikaWebhookSignature({ headers, rawBody, secret, toleranceSec
|
|
|
512
512
|
const ok = crypto2.timingSafeEqual(a, b);
|
|
513
513
|
return { valid: ok, reason: ok ? void 0 : "signature_mismatch" };
|
|
514
514
|
}
|
|
515
|
+
|
|
516
|
+
// src/cloudinaryValidation.js
|
|
517
|
+
var MAX_IMAGE_SIZE = 5 * 1024 * 1024;
|
|
518
|
+
var MAX_PROFILE_PIC_SIZE = 6 * 1024 * 1024;
|
|
519
|
+
var MAX_VIDEO_SIZE = 100 * 1024 * 1024;
|
|
520
|
+
var SUPPORTED_IMAGE_FORMATS = [".jpg", ".jpeg", ".png", ".webp"];
|
|
521
|
+
var SUPPORTED_PROFILE_MIME_TYPES = ["image/jpeg", "image/jpg", "image/png"];
|
|
522
|
+
var SUPPORTED_VIDEO_FORMATS = [".mp4", ".mov", ".avi", ".wmv", ".flv", ".mkv", ".webm", ".m4v"];
|
|
523
|
+
function splitFileName(name) {
|
|
524
|
+
const safe = typeof name === "string" && name.trim() ? name.trim() : "";
|
|
525
|
+
const dotIdx = safe.lastIndexOf(".");
|
|
526
|
+
if (dotIdx <= 0) return { ext: "", base: safe || "asset" };
|
|
527
|
+
return { ext: safe.slice(dotIdx).toLowerCase(), base: safe.slice(0, dotIdx) };
|
|
528
|
+
}
|
|
529
|
+
function normalizeFolderPath(value, fallback) {
|
|
530
|
+
if (typeof value !== "string") return fallback;
|
|
531
|
+
const sanitized = value.trim().replace(/\\/g, "/").split("/").filter(Boolean).join("/");
|
|
532
|
+
return sanitized || fallback;
|
|
533
|
+
}
|
|
534
|
+
function createScopedPublicId({ baseName, userPrefix, timestamp }) {
|
|
535
|
+
const safeBaseName = baseName && baseName.trim() ? baseName.trim() : "asset";
|
|
536
|
+
const safePrefix = userPrefix && userPrefix.trim() ? userPrefix.trim() : "anon";
|
|
537
|
+
const safeTimestamp = timestamp && timestamp.trim() ? timestamp.trim() : Date.now().toString().slice(-6);
|
|
538
|
+
return `${safePrefix}_${safeTimestamp}_${safeBaseName}`;
|
|
539
|
+
}
|
|
540
|
+
function validateImageFile(file) {
|
|
541
|
+
const { ext: extFromName } = splitFileName(file.name || "");
|
|
542
|
+
const mimeExt = file.type ? `.${file.type.split("/")[1]}`.toLowerCase() : "";
|
|
543
|
+
const finalExt = extFromName || mimeExt;
|
|
544
|
+
if (!SUPPORTED_IMAGE_FORMATS.includes(finalExt)) {
|
|
545
|
+
return {
|
|
546
|
+
valid: false,
|
|
547
|
+
status: 415,
|
|
548
|
+
error: "INVALID_FORMAT",
|
|
549
|
+
message: `Unsupported format: ${finalExt}. Allowed: ${SUPPORTED_IMAGE_FORMATS.join(", ")}`
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
if (file.size > MAX_IMAGE_SIZE) {
|
|
553
|
+
return {
|
|
554
|
+
valid: false,
|
|
555
|
+
status: 413,
|
|
556
|
+
error: "FILE_TOO_LARGE",
|
|
557
|
+
message: `File size (${(file.size / 1024 / 1024).toFixed(2)}MB) exceeds the ${MAX_IMAGE_SIZE / 1024 / 1024}MB limit`
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
return { valid: true };
|
|
561
|
+
}
|
|
562
|
+
function validateProfilePicFile(file) {
|
|
563
|
+
if (!SUPPORTED_PROFILE_MIME_TYPES.includes(file.type)) {
|
|
564
|
+
return {
|
|
565
|
+
valid: false,
|
|
566
|
+
status: 415,
|
|
567
|
+
error: "INVALID_FORMAT",
|
|
568
|
+
message: "Unsupported format. Only JPG, JPEG, and PNG are allowed."
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
if (file.size > MAX_PROFILE_PIC_SIZE) {
|
|
572
|
+
return {
|
|
573
|
+
valid: false,
|
|
574
|
+
status: 413,
|
|
575
|
+
error: "FILE_TOO_LARGE",
|
|
576
|
+
message: `File size too large. Maximum is ${MAX_PROFILE_PIC_SIZE / 1024 / 1024}MB.`
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
return { valid: true };
|
|
580
|
+
}
|
|
581
|
+
function validateVideoFile(file) {
|
|
582
|
+
const { ext } = splitFileName(file.name || "");
|
|
583
|
+
if (!SUPPORTED_VIDEO_FORMATS.includes(ext)) {
|
|
584
|
+
return {
|
|
585
|
+
valid: false,
|
|
586
|
+
status: 415,
|
|
587
|
+
error: "INVALID_FORMAT",
|
|
588
|
+
message: `Unsupported format: ${ext}. Allowed: ${SUPPORTED_VIDEO_FORMATS.join(", ")}`
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
if (file.size > MAX_VIDEO_SIZE) {
|
|
592
|
+
return {
|
|
593
|
+
valid: false,
|
|
594
|
+
status: 413,
|
|
595
|
+
error: "FILE_TOO_LARGE",
|
|
596
|
+
message: `File size (${(file.size / 1024 / 1024).toFixed(2)}MB) exceeds the ${MAX_VIDEO_SIZE / 1024 / 1024}MB limit`
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
return { valid: true };
|
|
600
|
+
}
|
|
515
601
|
export {
|
|
516
602
|
CloudinaryError,
|
|
603
|
+
MAX_IMAGE_SIZE,
|
|
604
|
+
MAX_PROFILE_PIC_SIZE,
|
|
605
|
+
MAX_VIDEO_SIZE,
|
|
606
|
+
SUPPORTED_IMAGE_FORMATS,
|
|
607
|
+
SUPPORTED_PROFILE_MIME_TYPES,
|
|
608
|
+
SUPPORTED_VIDEO_FORMATS,
|
|
517
609
|
TRANSFORM_PRESETS,
|
|
518
610
|
configureSanvikaCloudinary,
|
|
611
|
+
createScopedPublicId,
|
|
519
612
|
deleteImage,
|
|
520
613
|
deleteImages,
|
|
521
614
|
extractPublicId,
|
|
@@ -530,14 +623,19 @@ export {
|
|
|
530
623
|
isCloudinaryUrl,
|
|
531
624
|
isProxyMode,
|
|
532
625
|
isRetriableError,
|
|
626
|
+
normalizeFolderPath,
|
|
533
627
|
pingCloudinary,
|
|
534
628
|
runCloudinaryDiagnostics,
|
|
629
|
+
splitFileName,
|
|
535
630
|
testCloudinaryWebhookSignature,
|
|
536
631
|
uploadImage,
|
|
537
632
|
uploadImages,
|
|
538
633
|
uploadRawFile,
|
|
539
634
|
uploadVideo,
|
|
635
|
+
validateImageFile,
|
|
636
|
+
validateProfilePicFile,
|
|
540
637
|
validatePublicId,
|
|
638
|
+
validateVideoFile,
|
|
541
639
|
verifySanvikaWebhookSignature,
|
|
542
640
|
withRetry
|
|
543
641
|
};
|
package/package.json
CHANGED