/** * S3 서비스 (Garage/MinIO 호환) * 첨부파일 업로드 및 다운로드 */ const { S3Client, PutObjectCommand, GetObjectCommand, } = require("@aws-sdk/client-s3"); const { getSignedUrl } = require("@aws-sdk/s3-request-presigner"); const crypto = require("crypto"); // S3 클라이언트 설정 (Garage/MinIO 호환 모드) const s3Client = new S3Client({ region: process.env.AWS_REGION || "garage", endpoint: process.env.MINIO_ENDPOINT || "http://garage:3900", forcePathStyle: true, credentials: { accessKeyId: process.env.MINIO_ACCESS_KEY, secretAccessKey: process.env.MINIO_SECRET_KEY, }, }); const BUCKET_NAME = process.env.MINIO_BUCKET || "emails"; /** * 첨부파일 업로드 * @param {Buffer} buffer - 파일 내용 * @param {string} filename - 원본 파일명 * @param {string} mimeType - MIME 타입 * @returns {Promise} S3 객체 키 */ exports.uploadAttachment = async (buffer, filename, mimeType) => { // 고유 키 생성: 타임스탬프_랜덤해시_파일명 const key = `${Date.now()}_${crypto .randomBytes(8) .toString("hex")}_${filename}`; await s3Client.send( new PutObjectCommand({ Bucket: BUCKET_NAME, Key: key, Body: buffer, ContentType: mimeType, }) ); return key; }; /** * 서명된 다운로드 URL 생성 (1시간 유효) * @param {string} key - S3 객체 키 * @returns {Promise} 서명된 URL */ exports.getAttachmentUrl = async (key) => { const command = new GetObjectCommand({ Bucket: BUCKET_NAME, Key: key, }); return getSignedUrl(s3Client, command, { expiresIn: 3600 }); }; /** * 객체 스트림 가져오기 (직접 다운로드용) * @param {string} key - S3 객체 키 * @returns {Promise<{stream: ReadableStream, contentType: string, contentLength: number}>} */ exports.getObjectStream = async (key) => { const command = new GetObjectCommand({ Bucket: BUCKET_NAME, Key: key, }); const response = await s3Client.send(command); return { stream: response.Body, contentType: response.ContentType, contentLength: response.ContentLength, }; };