import { S3Client, PutObjectCommand, GetObjectCommand, HeadObjectCommand, DeleteObjectCommand, ListObjectsV2Command, } from "@aws-sdk/client-s3"; // S3 설정 (RustFS) - 환경변수에서 로드 const s3Config = { endpoint: process.env.S3_ENDPOINT, accessKey: process.env.S3_ACCESS_KEY, secretKey: process.env.S3_SECRET_KEY, bucket: "minecraft", publicUrl: "https://s3.caadiq.co.kr", }; // S3 클라이언트 생성 const s3Client = new S3Client({ endpoint: s3Config.endpoint, region: "us-east-1", credentials: { accessKeyId: s3Config.accessKey, secretAccessKey: s3Config.secretKey, }, forcePathStyle: true, // RustFS/MinIO용 }); /** * S3(RustFS)에 파일 업로드 */ async function uploadToS3( bucket, key, data, contentType = "application/octet-stream" ) { const command = new PutObjectCommand({ Bucket: bucket, Key: key, Body: data, ContentType: contentType, }); await s3Client.send(command); // 공개 URL 반환 (key는 인코딩) const encodedKey = key .split("/") .map((s) => encodeURIComponent(s)) .join("/"); return `${s3Config.publicUrl}/${bucket}/${encodedKey}`; } /** * S3(RustFS)에서 파일 다운로드 */ async function downloadFromS3(bucket, key) { const command = new GetObjectCommand({ Bucket: bucket, Key: key, }); const response = await s3Client.send(command); // ReadableStream을 Buffer로 변환 const chunks = []; for await (const chunk of response.Body) { chunks.push(chunk); } return Buffer.concat(chunks); } /** * S3(RustFS)에 파일이 존재하는지 확인 */ async function checkS3Exists(bucket, key) { try { const command = new HeadObjectCommand({ Bucket: bucket, Key: key, }); await s3Client.send(command); return true; } catch (err) { if (err.name === "NotFound" || err.$metadata?.httpStatusCode === 404) { return false; } throw err; } } /** * S3(RustFS)에서 파일 삭제 */ async function deleteFromS3(bucket, key) { const command = new DeleteObjectCommand({ Bucket: bucket, Key: key, }); await s3Client.send(command); } /** * S3(RustFS)에서 prefix로 시작하는 모든 파일 삭제 */ async function deleteByPrefix(bucket, prefix) { const listCommand = new ListObjectsV2Command({ Bucket: bucket, Prefix: prefix, }); const listResult = await s3Client.send(listCommand); const objects = listResult.Contents || []; let deletedCount = 0; for (const obj of objects) { try { await deleteFromS3(bucket, obj.Key); deletedCount++; } catch (err) { console.error(`[S3] 파일 삭제 실패: ${obj.Key} - ${err.message}`); } } return deletedCount; } export { s3Config, uploadToS3, downloadFromS3, checkS3Exists, deleteFromS3, deleteByPrefix, };