Files
octto-engenharia/frontend/src/lib/minio.ts

115 lines
3.8 KiB
TypeScript

import { S3Client, HeadBucketCommand, CreateBucketCommand, PutBucketPolicyCommand, PutObjectCommand, DeleteObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
const endpointHost = process.env.MINIO_ENDPOINT || 'localhost';
const port = Number.parseInt(process.env.MINIO_PORT || '9000', 10);
const useSSL = process.env.MINIO_USE_SSL === 'true';
const protocol = useSSL ? 'https' : 'http';
const endpointUrl = `${protocol}://${endpointHost}:${port}`;
console.log(`[MinIO] Configurando cliente: ${endpointHost}:${port} (SSL: ${useSSL})`);
export const bucketName = process.env.MINIO_BUCKET_NAME || 'occto-images';
const s3Client = new S3Client({
region: 'us-east-1',
endpoint: endpointUrl,
forcePathStyle: true,
credentials: {
accessKeyId: process.env.MINIO_ACCESS_KEY || 'admin',
secretAccessKey: process.env.MINIO_SECRET_KEY || 'adminpassword',
},
});
type MetadataMap = Record<string, string> | undefined;
function extractContentType(metadata: MetadataMap) {
if (!metadata) return undefined;
const contentTypeKey = Object.keys(metadata).find(key => key.toLowerCase() === 'content-type');
return contentTypeKey ? metadata[contentTypeKey] : undefined;
}
function sanitizeMetadata(metadata: MetadataMap) {
if (!metadata) return undefined;
const entries = Object.entries(metadata).filter(([key]) => key.toLowerCase() !== 'content-type');
return entries.length ? Object.fromEntries(entries) : undefined;
}
export const minioClient = {
async bucketExists(bucket: string) {
try {
await s3Client.send(new HeadBucketCommand({ Bucket: bucket }));
return true;
} catch (error: unknown) {
const err = error as { $metadata?: { httpStatusCode?: number } };
if (err?.$metadata?.httpStatusCode === 404) return false;
if (err?.$metadata?.httpStatusCode === 301) return true; // Bucket existe mas outra região
throw error;
}
},
async makeBucket(bucket: string) {
try {
await s3Client.send(new CreateBucketCommand({ Bucket: bucket }));
} catch (error: unknown) {
const err = error as { $metadata?: { httpStatusCode?: number } };
if (err?.$metadata?.httpStatusCode === 409) {
return; // bucket já existe
}
throw error;
}
},
async setBucketPolicy(bucket: string, policy: string) {
await s3Client.send(new PutBucketPolicyCommand({ Bucket: bucket, Policy: policy }));
},
async putObject(bucket: string, key: string, body: Buffer | Uint8Array | string, size?: number, metadata?: MetadataMap) {
const ContentType = extractContentType(metadata);
const remainingMetadata = sanitizeMetadata(metadata);
await s3Client.send(new PutObjectCommand({
Bucket: bucket,
Key: key,
Body: body,
ContentLength: size,
ContentType,
Metadata: remainingMetadata,
}));
},
async getObject(bucket: string, key: string) {
return s3Client.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
},
async removeObject(bucket: string, key: string) {
await s3Client.send(new DeleteObjectCommand({ Bucket: bucket, Key: key }));
},
};
// Garante que o bucket exista antes de qualquer upload
export async function ensureBucketExists() {
try {
const exists = await minioClient.bucketExists(bucketName);
if (!exists) {
await minioClient.makeBucket(bucketName);
const policy = {
Version: '2012-10-17',
Statement: [
{
Effect: 'Allow',
Principal: { AWS: ['*'] },
Action: ['s3:GetObject'],
Resource: [`arn:aws:s3:::${bucketName}/*`],
},
],
};
await minioClient.setBucketPolicy(bucketName, JSON.stringify(policy));
console.log(`Bucket ${bucketName} criado e política pública aplicada.`);
}
} catch (error) {
console.error('Erro ao garantir existência do bucket MinIO:', error);
}
}