/** * MinIO Storage Service * S3-compatible object storage for media files * * MinIO Console: https://minio-console.eluxnetworks.net * S3 Endpoint: https://minio.eluxnetworks.net */ const { S3Client, PutObjectCommand, DeleteObjectCommand } = require('@aws-sdk/client-s3'); const crypto = require('crypto'); // MinIO Configuration const MINIO_ENDPOINT = process.env.MINIO_ENDPOINT || 'https://minio.eluxnetworks.net'; const MINIO_ACCESS_KEY = process.env.MINIO_ACCESS_KEY; const MINIO_SECRET_KEY = process.env.MINIO_SECRET_KEY; const MINIO_BUCKET = process.env.MINIO_BUCKET || 'wellnuo'; // Public URL for accessing files const PUBLIC_URL = process.env.MINIO_PUBLIC_URL || MINIO_ENDPOINT; // Create S3 client for MinIO const s3Client = new S3Client({ endpoint: MINIO_ENDPOINT, region: 'us-east-1', // MinIO ignores this but requires it credentials: { accessKeyId: MINIO_ACCESS_KEY, secretAccessKey: MINIO_SECRET_KEY, }, forcePathStyle: true, // Required for MinIO }); /** * Upload a base64 image to MinIO * @param {string} base64Data - Base64 data URI (data:image/png;base64,...) * @param {string} folder - Folder path (e.g., 'avatars/beneficiaries') * @param {string} [filename] - Optional filename, generated if not provided * @returns {Promise<{url: string, key: string}>} */ async function uploadBase64Image(base64Data, folder, filename = null) { if (!MINIO_ACCESS_KEY || !MINIO_SECRET_KEY) { throw new Error('MinIO credentials not configured'); } // Parse base64 data URI const matches = base64Data.match(/^data:image\/(\w+);base64,(.+)$/); if (!matches) { throw new Error('Invalid base64 image format'); } const [, extension, base64Content] = matches; const buffer = Buffer.from(base64Content, 'base64'); // Generate filename if not provided const finalFilename = filename || `${crypto.randomUUID()}.${extension}`; const key = `${folder}/${finalFilename}`; // Determine content type const contentType = `image/${extension === 'jpg' ? 'jpeg' : extension}`; // Upload to MinIO const command = new PutObjectCommand({ Bucket: MINIO_BUCKET, Key: key, Body: buffer, ContentType: contentType, ACL: 'public-read', // Make file publicly accessible }); await s3Client.send(command); // Return public URL const url = `${PUBLIC_URL}/${MINIO_BUCKET}/${key}`; return { url, key }; } /** * Delete a file from MinIO * @param {string} key - Object key (path in bucket) */ async function deleteFile(key) { if (!MINIO_ACCESS_KEY || !MINIO_SECRET_KEY) { throw new Error('MinIO credentials not configured'); } const command = new DeleteObjectCommand({ Bucket: MINIO_BUCKET, Key: key, }); await s3Client.send(command); } /** * Extract key from MinIO URL * @param {string} url - Full MinIO URL * @returns {string|null} - Object key or null */ function extractKeyFromUrl(url) { if (!url) return null; // URL format: https://minio.eluxnetworks.net/wellnuo/avatars/xxx.png const match = url.match(/\/wellnuo\/(.+)$/); return match ? match[1] : null; } /** * Check if storage is configured */ function isConfigured() { return !!(MINIO_ACCESS_KEY && MINIO_SECRET_KEY); } module.exports = { uploadBase64Image, deleteFile, extractKeyFromUrl, isConfigured, MINIO_BUCKET, PUBLIC_URL, };