payload-cloudinary
Version:
A Cloudinary storage plugin for Payload CMS
168 lines • 6.18 kB
JavaScript
import path from 'path';
import crypto from 'crypto';
import { getResourceType } from './utils';
/**
* Get upload options based on file type and versioning settings
*/
const getUploadOptions = (filename, versioning) => {
const ext = path.extname(filename).toLowerCase();
const resourceType = getResourceType(ext);
const baseOptions = {
resource_type: resourceType,
use_filename: true,
unique_filename: true,
...(versioning?.autoInvalidate && { invalidate: true }),
};
switch (resourceType) {
case 'video':
return {
...baseOptions,
chunk_size: 6000000,
eager: [{ format: ext.slice(1), quality: 'auto' }],
eager_async: true,
};
case 'image':
return {
...baseOptions,
eager: [{ quality: 'auto' }],
eager_async: true,
};
case 'raw':
// For PDFs, add a pages parameter to count the pages and create a thumbnail
if (ext === '.pdf') {
return {
...baseOptions,
resource_type: 'raw',
use_filename: true,
pages: true,
eager: [{ format: 'jpg', page: 1, quality: 'auto' }],
eager_async: true,
};
}
return {
...baseOptions,
resource_type: 'raw',
use_filename: true,
};
default:
return baseOptions;
}
};
/**
* Sanitize a string to be used as part of a public ID
*/
const sanitizeForPublicID = (str) => {
return str
.toLowerCase()
.replace(/[^a-z0-9]/g, '-')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '');
};
/**
* Generate a public ID based on the publicID options
*/
const generatePublicID = (filename, folderPath, publicIDOptions) => {
if (publicIDOptions?.generatePublicID) {
return publicIDOptions.generatePublicID(filename, path.dirname(folderPath), path.basename(folderPath));
}
const ext = path.extname(filename).toLowerCase();
const resourceType = getResourceType(ext);
const isRawFile = resourceType === 'raw';
if (publicIDOptions?.enabled === false) {
const filenameWithoutExt = path.basename(filename, path.extname(filename));
const sanitizedFilename = sanitizeForPublicID(filenameWithoutExt);
const finalFilename = isRawFile
? `${sanitizedFilename}${ext}`
: sanitizedFilename;
return path.posix.join(folderPath, finalFilename);
}
const useFilename = publicIDOptions?.useFilename !== false;
const uniqueFilename = publicIDOptions?.uniqueFilename !== false;
const timestamp = uniqueFilename ? `_${Date.now()}` : '';
if (useFilename) {
const filenameWithoutExt = path.basename(filename, path.extname(filename));
const sanitizedFilename = sanitizeForPublicID(filenameWithoutExt);
const finalFilename = isRawFile
? `${sanitizedFilename}${timestamp}${ext}`
: `${sanitizedFilename}${timestamp}`;
return path.posix.join(folderPath, finalFilename);
}
const finalFilename = isRawFile
? `media${timestamp}${ext}`
: `media${timestamp}`;
return path.posix.join(folderPath, finalFilename);
};
/**
* Generate a signature for client-side upload to Cloudinary
*/
export const getGenerateClientUploadSignature = ({ cloudinary, folder, versioning, publicID }) => async ({ filename, prefix = '', }) => {
// Construct the folder path with proper handling of prefix
const folderPath = prefix
? path.posix.join(folder, prefix)
: folder;
// Generate the public ID based on options
const publicIdValue = generatePublicID(filename, folderPath, publicID);
// Get upload options based on file type
const uploadOptions = {
...getUploadOptions(filename, versioning),
public_id: publicIdValue,
use_filename: publicID?.useFilename !== false,
unique_filename: publicID?.uniqueFilename !== false,
asset_folder: folderPath,
};
// Generate timestamp
const timestamp = Math.round(new Date().getTime() / 1000);
// Create the parameters to sign
const paramsToSign = {
timestamp,
...uploadOptions,
};
// Remove undefined values and non-signable parameters
const cleanedParams = Object.entries(paramsToSign).reduce((acc, [key, value]) => {
if (value !== undefined && value !== null) {
acc[key] = value;
}
return acc;
}, {});
// Generate signature using Cloudinary's signing method
const signature = generateSignature(cleanedParams, cloudinary.config().api_secret);
// Get the upload URL based on resource type
const resourceType = getResourceType(path.extname(filename).toLowerCase());
const cloudName = cloudinary.config().cloud_name;
const uploadUrl = `https://api.cloudinary.com/v1_1/${cloudName}/${resourceType}/upload`;
return {
signature,
timestamp,
api_key: cloudinary.config().api_key,
upload_url: uploadUrl,
public_id: publicIdValue,
upload_options: cleanedParams,
};
};
/**
* Generate a signature for Cloudinary upload
* This follows Cloudinary's signature generation algorithm
*/
function generateSignature(paramsToSign, apiSecret) {
// Sort parameters alphabetically
const sortedParams = Object.keys(paramsToSign)
.sort()
.map((key) => {
const value = paramsToSign[key];
// Handle arrays and objects
if (Array.isArray(value)) {
return `${key}=${JSON.stringify(value)}`;
}
if (typeof value === 'object') {
return `${key}=${JSON.stringify(value)}`;
}
return `${key}=${value}`;
})
.join('&');
// Create SHA256 hash
return crypto
.createHash('sha256')
.update(sortedParams + apiSecret)
.digest('hex');
}
//# sourceMappingURL=generateClientUploadSignature.js.map