2025-12-23 21:58:45 +09:00
|
|
|
import {
|
|
|
|
|
S3Client,
|
|
|
|
|
PutObjectCommand,
|
|
|
|
|
GetObjectCommand,
|
|
|
|
|
HeadObjectCommand,
|
2025-12-26 20:14:43 +09:00
|
|
|
DeleteObjectCommand,
|
|
|
|
|
ListObjectsV2Command,
|
2025-12-23 21:58:45 +09:00
|
|
|
} from "@aws-sdk/client-s3";
|
2025-12-16 08:40:32 +09:00
|
|
|
|
|
|
|
|
// 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",
|
|
|
|
|
};
|
|
|
|
|
|
2025-12-23 21:58:45 +09:00
|
|
|
// S3 클라이언트 생성
|
|
|
|
|
const s3Client = new S3Client({
|
|
|
|
|
endpoint: s3Config.endpoint,
|
|
|
|
|
region: "us-east-1",
|
|
|
|
|
credentials: {
|
|
|
|
|
accessKeyId: s3Config.accessKey,
|
|
|
|
|
secretAccessKey: s3Config.secretKey,
|
|
|
|
|
},
|
|
|
|
|
forcePathStyle: true, // RustFS/MinIO용
|
|
|
|
|
});
|
2025-12-16 08:40:32 +09:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* S3(RustFS)에 파일 업로드
|
|
|
|
|
*/
|
2025-12-23 21:58:45 +09:00
|
|
|
async function uploadToS3(
|
2025-12-23 10:07:34 +09:00
|
|
|
bucket,
|
|
|
|
|
key,
|
|
|
|
|
data,
|
|
|
|
|
contentType = "application/octet-stream"
|
|
|
|
|
) {
|
2025-12-23 21:58:45 +09:00
|
|
|
const command = new PutObjectCommand({
|
|
|
|
|
Bucket: bucket,
|
|
|
|
|
Key: key,
|
|
|
|
|
Body: data,
|
|
|
|
|
ContentType: contentType,
|
2025-12-16 08:40:32 +09:00
|
|
|
});
|
2025-12-23 21:58:45 +09:00
|
|
|
|
|
|
|
|
await s3Client.send(command);
|
|
|
|
|
// 공개 URL 반환 (key는 인코딩)
|
|
|
|
|
const encodedKey = key
|
|
|
|
|
.split("/")
|
|
|
|
|
.map((s) => encodeURIComponent(s))
|
|
|
|
|
.join("/");
|
|
|
|
|
return `${s3Config.publicUrl}/${bucket}/${encodedKey}`;
|
2025-12-16 08:40:32 +09:00
|
|
|
}
|
|
|
|
|
|
2025-12-23 10:07:34 +09:00
|
|
|
/**
|
|
|
|
|
* S3(RustFS)에서 파일 다운로드
|
|
|
|
|
*/
|
2025-12-23 21:58:45 +09:00
|
|
|
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);
|
|
|
|
|
}
|
2025-12-23 10:07:34 +09:00
|
|
|
|
2025-12-23 21:58:45 +09:00
|
|
|
/**
|
|
|
|
|
* S3(RustFS)에 파일이 존재하는지 확인
|
|
|
|
|
*/
|
|
|
|
|
async function checkS3Exists(bucket, key) {
|
|
|
|
|
try {
|
|
|
|
|
const command = new HeadObjectCommand({
|
|
|
|
|
Bucket: bucket,
|
|
|
|
|
Key: key,
|
2025-12-23 10:07:34 +09:00
|
|
|
});
|
2025-12-23 21:58:45 +09:00
|
|
|
await s3Client.send(command);
|
|
|
|
|
return true;
|
|
|
|
|
} catch (err) {
|
|
|
|
|
if (err.name === "NotFound" || err.$metadata?.httpStatusCode === 404) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
throw err;
|
|
|
|
|
}
|
2025-12-23 10:07:34 +09:00
|
|
|
}
|
|
|
|
|
|
2025-12-26 20:14:43 +09:00
|
|
|
/**
|
|
|
|
|
* 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,
|
|
|
|
|
};
|