Greenlens/server/lib/storage.js

73 lines
2.3 KiB
JavaScript

const Minio = require('minio');
const crypto = require('crypto');
const getTrimmedEnv = (name, fallback = '') => String(process.env[name] ?? fallback).trim();
const MINIO_ENDPOINT = getTrimmedEnv('MINIO_ENDPOINT');
const MINIO_PORT = Number(process.env.MINIO_PORT || 9000);
const MINIO_USE_SSL = process.env.MINIO_USE_SSL === 'true';
const MINIO_ACCESS_KEY = getTrimmedEnv('MINIO_ACCESS_KEY');
const MINIO_SECRET_KEY = getTrimmedEnv('MINIO_SECRET_KEY');
const MINIO_BUCKET = getTrimmedEnv('MINIO_BUCKET', 'plant-images') || 'plant-images';
const isStorageConfigured = () => Boolean(MINIO_ENDPOINT && MINIO_ACCESS_KEY && MINIO_SECRET_KEY);
const getMinioPublicUrl = () =>
getTrimmedEnv('MINIO_PUBLIC_URL', `http://${MINIO_ENDPOINT}:${MINIO_PORT}`).replace(/\/$/, '');
const getClient = () => {
if (!isStorageConfigured()) {
throw new Error('Image storage is not configured.');
}
return new Minio.Client({
endPoint: MINIO_ENDPOINT,
port: MINIO_PORT,
useSSL: MINIO_USE_SSL,
accessKey: MINIO_ACCESS_KEY,
secretKey: MINIO_SECRET_KEY,
});
};
const ensureStorageBucket = async () => {
const client = getClient();
const exists = await client.bucketExists(MINIO_BUCKET);
if (!exists) {
await client.makeBucket(MINIO_BUCKET);
const policy = JSON.stringify({
Version: '2012-10-17',
Statement: [
{
Effect: 'Allow',
Principal: { AWS: ['*'] },
Action: ['s3:GetObject'],
Resource: [`arn:aws:s3:::${MINIO_BUCKET}/*`],
},
],
});
await client.setBucketPolicy(MINIO_BUCKET, policy);
console.log(`MinIO bucket '${MINIO_BUCKET}' created with public read policy.`);
}
};
const uploadImage = async (base64Data, contentType = 'image/jpeg') => {
const client = getClient();
const rawExtension = contentType.split('/')[1] || 'jpg';
const extension = rawExtension === 'jpeg' ? 'jpg' : rawExtension;
const filename = `${Date.now()}-${crypto.randomBytes(8).toString('hex')}.${extension}`;
const buffer = Buffer.from(base64Data, 'base64');
await client.putObject(MINIO_BUCKET, filename, buffer, buffer.length, {
'Content-Type': contentType,
});
const url = `${getMinioPublicUrl()}/${MINIO_BUCKET}/${filename}`;
return { url, filename };
};
module.exports = {
ensureStorageBucket,
uploadImage,
isStorageConfigured,
};