mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-15 19:29:03 +02:00
Store endpointUrl and publicUrl in cloudinary_config DB table so MinIO connection can be fully configured from the app settings without needing environment variables. - DB migration: adds endpoint_url and public_url columns - Schema: adds endpointUrl/publicUrl to cloudinaryConfig table - MinIO service: reads endpoint from DB (falls back to MINIO_ENDPOINT env) - buildMinioUrl: accepts optional publicUrl override from DB config - All routes updated to pass config.publicUrl to buildMinioUrl - Settings UI (desktop + mobile): adds Endpoint URL and Public URL fields Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
113 lines
4.3 KiB
TypeScript
113 lines
4.3 KiB
TypeScript
import { S3Client, PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3';
|
|
import { storage } from '../storage';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
function getS3Client(accessKey: string, secretKey: string, endpointUrl?: string | null): S3Client {
|
|
const endpoint = endpointUrl || process.env.MINIO_ENDPOINT || 'http://localhost:9000';
|
|
return new S3Client({
|
|
endpoint,
|
|
region: 'us-east-1',
|
|
credentials: { accessKeyId: accessKey, secretAccessKey: secretKey },
|
|
forcePathStyle: true,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Builds the public URL for a stored object.
|
|
* Uses DB publicUrl > MINIO_PUBLIC_URL env > MINIO_ENDPOINT env > localhost fallback.
|
|
*/
|
|
export function buildMinioUrl(bucketName: string, objectKey: string, publicUrlOverride?: string | null): string {
|
|
const base = (publicUrlOverride || process.env.MINIO_PUBLIC_URL || process.env.MINIO_ENDPOINT || 'http://localhost:9000').replace(/\/$/, '');
|
|
return `${base}/${bucketName}/${objectKey}`;
|
|
}
|
|
|
|
class MinioService {
|
|
async uploadMedia(
|
|
file: Buffer | string,
|
|
fileName: string,
|
|
_userId: string,
|
|
mimeType: string
|
|
): Promise<{
|
|
publicId: string;
|
|
originalUrl: string;
|
|
facebookFeedUrl: string | null;
|
|
instagramFeedUrl: string | null;
|
|
instagramStoryUrl: string | null;
|
|
}> {
|
|
const config = await storage.getCloudinaryConfig();
|
|
if (!config) throw new Error('MinIO non configuré. Veuillez configurer le stockage dans les Paramètres.');
|
|
|
|
const client = getS3Client(config.apiKey, config.apiSecret, config.endpointUrl);
|
|
const bucket = config.cloudName;
|
|
const safeName = path.basename(fileName).replace(/\s+/g, '-');
|
|
const objectKey = `social-flow/${Date.now()}-${safeName}`;
|
|
|
|
const body: Buffer = typeof file === 'string' ? fs.readFileSync(file) : file;
|
|
|
|
await client.send(new PutObjectCommand({
|
|
Bucket: bucket,
|
|
Key: objectKey,
|
|
Body: body,
|
|
ContentType: mimeType,
|
|
}));
|
|
|
|
const url = buildMinioUrl(bucket, objectKey, config.publicUrl);
|
|
return { publicId: objectKey, originalUrl: url, facebookFeedUrl: url, instagramFeedUrl: url, instagramStoryUrl: url };
|
|
}
|
|
|
|
async deleteMedia(publicId: string, _userId: string, _mediaType: 'image' | 'video'): Promise<void> {
|
|
const config = await storage.getCloudinaryConfig();
|
|
if (!config) throw new Error('MinIO non configuré.');
|
|
|
|
const client = getS3Client(config.apiKey, config.apiSecret, config.endpointUrl);
|
|
await client.send(new DeleteObjectCommand({ Bucket: config.cloudName, Key: publicId }));
|
|
}
|
|
|
|
async uploadStoryImageWithText(buffer: Buffer, originalFileName: string): Promise<string> {
|
|
const config = await storage.getCloudinaryConfig();
|
|
if (!config) throw new Error('MinIO non configuré.');
|
|
|
|
const client = getS3Client(config.apiKey, config.apiSecret, config.endpointUrl);
|
|
const safeName = path.basename(originalFileName).replace(/\s+/g, '-');
|
|
const objectKey = `social-flow/stories/story-${Date.now()}-${safeName}`;
|
|
|
|
await client.send(new PutObjectCommand({
|
|
Bucket: config.cloudName,
|
|
Key: objectKey,
|
|
Body: buffer,
|
|
ContentType: 'image/jpeg',
|
|
}));
|
|
|
|
return buildMinioUrl(config.cloudName, objectKey, config.publicUrl);
|
|
}
|
|
|
|
async uploadLogo(buffer: Buffer, fileName: string): Promise<{ publicId: string; url: string }> {
|
|
const config = await storage.getCloudinaryConfig();
|
|
if (!config) throw new Error('MinIO non configuré.');
|
|
|
|
const client = getS3Client(config.apiKey, config.apiSecret, config.endpointUrl);
|
|
const ext = path.extname(fileName) || '.png';
|
|
const objectKey = `social-flow/logos/logo-${Date.now()}${ext}`;
|
|
|
|
await client.send(new PutObjectCommand({
|
|
Bucket: config.cloudName,
|
|
Key: objectKey,
|
|
Body: buffer,
|
|
ContentType: fileName.match(/\.png$/i) ? 'image/png' : 'image/jpeg',
|
|
}));
|
|
|
|
return { publicId: objectKey, url: buildMinioUrl(config.cloudName, objectKey, config.publicUrl) };
|
|
}
|
|
|
|
async deleteLogo(publicId: string): Promise<void> {
|
|
const config = await storage.getCloudinaryConfig();
|
|
if (!config) throw new Error('MinIO non configuré.');
|
|
|
|
const client = getS3Client(config.apiKey, config.apiSecret, config.endpointUrl);
|
|
await client.send(new DeleteObjectCommand({ Bucket: config.cloudName, Key: publicId }));
|
|
}
|
|
}
|
|
|
|
export const minioService = new MinioService();
|