Files
Socialflow/server/services/cloudinary.ts
T
michaelschal df3e2be733 Improve media display and image aspect ratio handling for social posts
Update Cloudinary transformations to use 'pad' crop instead of 'fill' for better aspect ratio preservation and add click functionality to media items in the upload component.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: ae4037a0-2a6f-4530-9bac-79b543286bda
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/397bca8c-984f-43ff-841a-10897aeb8140/ae4037a0-2a6f-4530-9bac-79b543286bda/4KAFpKS
2025-10-02 07:43:31 +00:00

107 lines
2.9 KiB
TypeScript

import { v2 as cloudinary } from 'cloudinary';
import { storage } from '../storage';
class CloudinaryService {
async uploadMedia(
buffer: Buffer,
fileName: string,
userId: string,
mimeType: string
): Promise<{
publicId: string;
originalUrl: string;
facebookFeedUrl: string | null;
instagramFeedUrl: string | null;
instagramStoryUrl: string | null;
}> {
// Get user's Cloudinary config
const config = await storage.getCloudinaryConfig(userId);
if (!config) {
throw new Error('Cloudinary configuration not found. Please configure Cloudinary in Settings.');
}
// Configure Cloudinary with user's credentials
cloudinary.config({
cloud_name: config.cloudName,
api_key: config.apiKey,
api_secret: config.apiSecret,
secure: true,
});
const isVideo = mimeType.startsWith('video/');
// Upload to Cloudinary
const uploadResult = await new Promise<any>((resolve, reject) => {
const uploadStream = cloudinary.uploader.upload_stream(
{
folder: 'social-flow',
public_id: `${Date.now()}-${fileName.replace(/\.[^/.]+$/, '')}`,
resource_type: isVideo ? 'video' : 'image',
},
(error, result) => {
if (error) reject(error);
else resolve(result);
}
);
uploadStream.end(buffer);
});
const publicId = uploadResult.public_id;
const originalUrl = uploadResult.secure_url;
// Generate transformation URLs only for images
let facebookFeedUrl = null;
let instagramFeedUrl = null;
let instagramStoryUrl = null;
if (!isVideo) {
facebookFeedUrl = cloudinary.url(publicId, {
transformation: [
{ width: 1200, height: 630, crop: 'pad', gravity: 'auto', background: 'black' }
]
});
instagramFeedUrl = cloudinary.url(publicId, {
transformation: [
{ width: 1080, height: 1080, crop: 'pad', gravity: 'auto', background: 'black' }
]
});
instagramStoryUrl = cloudinary.url(publicId, {
transformation: [
{ width: 1080, height: 1920, crop: 'pad', gravity: 'auto', background: 'black' }
]
});
}
return {
publicId,
originalUrl,
facebookFeedUrl,
instagramFeedUrl,
instagramStoryUrl,
};
}
async deleteMedia(publicId: string, userId: string, mediaType: 'image' | 'video'): Promise<void> {
const config = await storage.getCloudinaryConfig(userId);
if (!config) {
throw new Error('Cloudinary configuration not found');
}
cloudinary.config({
cloud_name: config.cloudName,
api_key: config.apiKey,
api_secret: config.apiSecret,
secure: true,
});
await cloudinary.uploader.destroy(publicId, {
resource_type: mediaType,
});
}
}
export const cloudinaryService = new CloudinaryService();