refactor: trocar SDK MinIO pelo AWS S3 client
This commit is contained in:
1941
frontend/package-lock.json
generated
1941
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -13,11 +13,11 @@
|
||||
"seed": "node prisma/seed.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.940.0",
|
||||
"@prisma/client": "^5.22.0",
|
||||
"bcryptjs": "^3.0.3",
|
||||
"date-fns": "^4.1.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"minio": "^7.1.3",
|
||||
"next": "15.1.0",
|
||||
"next-themes": "^0.4.6",
|
||||
"pg": "^8.16.3",
|
||||
|
||||
@@ -1,30 +1,94 @@
|
||||
import { Client } from 'minio';
|
||||
import { S3Client, HeadBucketCommand, CreateBucketCommand, PutBucketPolicyCommand, PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3';
|
||||
|
||||
const endpoint = process.env.MINIO_ENDPOINT || 'localhost';
|
||||
const port = parseInt(process.env.MINIO_PORT || '9000');
|
||||
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: ${endpoint}:${port} (SSL: ${useSSL})`);
|
||||
|
||||
export const minioClient = new Client({
|
||||
endPoint: endpoint,
|
||||
port: port,
|
||||
useSSL: useSSL,
|
||||
accessKey: process.env.MINIO_ACCESS_KEY || 'admin',
|
||||
secretKey: process.env.MINIO_SECRET_KEY || 'adminpassword',
|
||||
region: 'us-east-1',
|
||||
pathStyle: true, // IMPORTANTE: força path-style (endpoint/bucket) ao invés de virtual-hosted (bucket.endpoint)
|
||||
});
|
||||
console.log(`[MinIO] Configurando cliente: ${endpointHost}:${port} (SSL: ${useSSL})`);
|
||||
|
||||
export const bucketName = process.env.MINIO_BUCKET_NAME || 'occto-images';
|
||||
|
||||
// Ensure bucket exists
|
||||
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 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, 'us-east-1');
|
||||
// Set policy to public read
|
||||
await minioClient.makeBucket(bucketName);
|
||||
|
||||
const policy = {
|
||||
Version: '2012-10-17',
|
||||
Statement: [
|
||||
@@ -36,10 +100,11 @@ export async function ensureBucketExists() {
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
await minioClient.setBucketPolicy(bucketName, JSON.stringify(policy));
|
||||
console.log(`Bucket ${bucketName} created and policy set to public read.`);
|
||||
console.log(`Bucket ${bucketName} criado e política pública aplicada.`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error ensuring bucket exists:', error);
|
||||
console.error('Erro ao garantir existência do bucket MinIO:', error);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user