feat: replace Cloudinary with MinIO for all media storage

- Create server/services/minio.ts: S3-compatible service (same interface as
  cloudinaryService) using @aws-sdk/client-s3 and MINIO_ENDPOINT env var
- Replace all cloudinaryService imports with minioService across routes.ts,
  routes/reels.ts, routes/remotion.ts, services/media-purge.ts
- Replace Cloudinary logo URL pattern (res.cloudinary.com/...) with
  buildMinioUrl() helper throughout all server routes
- GET /api/cloudinary/config now returns a logoUrl field (full MinIO URL)
  so clients never need to build the URL themselves
- Update settings pages (desktop + mobile): labels changed to MinIO
  (Bucket Name, Access Key, Secret Key)
- Update image-editor.tsx: remove Cloudinary URL transformation, use logoUrl
- Update media-utils.ts: simplify getVideoThumbnailUrl (no URL transforms)
- Add MINIO_ENDPOINT and MINIO_PUBLIC_URL to docker-compose.yml + .env.example
- DB table reuse: cloudinary_config.cloud_name = bucket, api_key = access key,
  api_secret = secret key — no migration needed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
MichaelandClaude Sonnet 4.6 committed 2026-03-24 21:05:37 +01:00
1 parent b0e11cb8ab
commit e39fa1026d
13 files changed
+1832 -118

No files matched your search

+4 -16
View File
@@ -1,20 +1,8 @@
/**
* Derives a Cloudinary video thumbnail URL from the original video URL.
*
* Cloudinary automatically generates image frames for uploaded videos.
* By inserting transformation parameters into the URL, we can extract
* a static JPEG thumbnail without any backend change or DB migration.
*
* @param originalUrl - The Cloudinary video URL (e.g. .../video/upload/v123/file.mp4)
* @returns A JPEG thumbnail URL with fill-crop at 400x400
* Returns a thumbnail URL for a video.
* With MinIO there are no automatic URL-based transformations,
* so we return the original URL and let the browser render the video.
*/
export function getVideoThumbnailUrl(originalUrl: string): string {
// Only transform Cloudinary video URLs
if (!originalUrl.includes('/video/upload/')) {
return originalUrl;
}
return originalUrl
.replace('/video/upload/', '/video/upload/so_0,w_400,h_400,c_fill/')
.replace(/\.[^.]+$/, '.jpg');
return originalUrl;
}