#!/usr/bin/env node /** * R2 업로드 스크립트 * * 사용법: * node scripts/upload.js * * 예시: * node scripts/upload.js demo-site ./sample-site * * 환경변수: * R2_ENDPOINT - R2 엔드포인트 URL * R2_ACCESS_KEY - R2 접근 키 * R2_SECRET_KEY - R2 시크릿 키 * R2_BUCKET - 버킷 이름 (기본: multisite-bucket) */ const { S3Client, PutObjectCommand, ListObjectsV2Command, DeleteObjectsCommand } = require('@aws-sdk/client-s3'); const fs = require('fs'); const path = require('path'); // MIME 타입 매핑 const MIME_TYPES = { '.html': 'text/html; charset=utf-8', '.css': 'text/css', '.js': 'application/javascript', '.json': 'application/json', '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.svg': 'image/svg+xml', '.ico': 'image/x-icon', '.woff': 'font/woff', '.woff2': 'font/woff2', '.ttf': 'font/ttf', '.pdf': 'application/pdf', '.xml': 'application/xml', '.txt': 'text/plain', '.md': 'text/markdown', }; // 재귀적으로 모든 파일 찾기 function getAllFiles(dir, files = []) { const entries = fs.readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { getAllFiles(fullPath, files); } else { files.push(fullPath); } } return files; } async function main() { const [,, customerId, sourceDir] = process.argv; if (!customerId || !sourceDir) { console.error('Usage: node scripts/upload.js '); console.error('Example: node scripts/upload.js demo-site ./sample-site'); process.exit(1); } // 환경변수 체크 const endpoint = process.env.R2_ENDPOINT; const accessKeyId = process.env.R2_ACCESS_KEY; const secretAccessKey = process.env.R2_SECRET_KEY; const bucket = process.env.R2_BUCKET || 'multisite-bucket'; if (!endpoint || !accessKeyId || !secretAccessKey) { console.error('Missing environment variables:'); console.error(' R2_ENDPOINT, R2_ACCESS_KEY, R2_SECRET_KEY'); console.error(''); console.error('R2 API 토큰 생성: Cloudflare Dashboard > R2 > Manage R2 API Tokens'); process.exit(1); } // S3 클라이언트 생성 (R2는 S3 호환) const s3 = new S3Client({ region: 'auto', endpoint, credentials: { accessKeyId, secretAccessKey }, }); const sourcePath = path.resolve(sourceDir); if (!fs.existsSync(sourcePath)) { console.error(`Source directory not found: ${sourcePath}`); process.exit(1); } console.log(`Uploading to R2...`); console.log(` Customer: ${customerId}`); console.log(` Source: ${sourcePath}`); console.log(` Bucket: ${bucket}`); console.log(''); // 모든 파일 수집 const files = getAllFiles(sourcePath); const prefix = `sites/${customerId}`; // 업로드 let uploaded = 0; let failed = 0; for (const file of files) { const relativePath = path.relative(sourcePath, file); const key = `${prefix}/${relativePath}`.replace(/\\/g, '/'); const ext = path.extname(file).toLowerCase(); const contentType = MIME_TYPES[ext] || 'application/octet-stream'; try { const body = fs.readFileSync(file); await s3.send(new PutObjectCommand({ Bucket: bucket, Key: key, Body: body, ContentType: contentType, })); console.log(` ✓ ${key}`); uploaded++; } catch (error) { console.error(` ✗ ${key}: ${error.message}`); failed++; } } console.log(''); console.log(`Done! Uploaded: ${uploaded}, Failed: ${failed}`); if (failed > 0) { process.exit(1); } } main().catch(error => { console.error('Fatal error:', error); process.exit(1); });