#!/usr/bin/env node const { execSync } = require("child_process"); const fs = require("fs"); const path = require("path"); const isWin = process.platform === "win32"; const isMac = process.platform === "darwin"; // 소스 디렉토리 (현재 프로젝트 루트) const projectRoot = path.resolve(__dirname, ".."); const projectName = "saju-web"; // NAS 경로 설정 const dstWin = `Z:\\docker\\webpage\\${projectName}\\`; const dstMac = `/Volumes/gahusb.synology.me/docker/webpage/${projectName}/`; const dst = isWin ? dstWin : dstMac; // SSH 설정 (환경 변수에서 읽기) const sshTarget = process.env.NAS_SSH_TARGET || "gahusb@gahusb.synology.me"; const sshPath = process.env.NAS_SSH_PATH || `/volume1/docker/webpage/${projectName}`; const sshPort = process.env.NAS_SSH_PORT; // 제외할 파일/폴더 const excludePatterns = [ "node_modules/", ".next/", ".git/", "dist/", "out/", ".env.local", ".env*.local", "*.log", ".DS_Store", "build_error.log", "robocopy.log", ]; console.log("🚀 Saju Web NAS Deployment Started"); console.log("====================================="); console.log(`Platform: ${process.platform}`); console.log(`Project: ${projectName}`); console.log(""); // SSH를 통한 배포 (추천) if (process.env.NAS_DEPLOY_METHOD === "ssh" || sshTarget) { console.log("📡 Deploying via SSH..."); console.log(`Target: ${sshTarget}:${sshPath}`); console.log(""); const sshCmd = sshPort ? `ssh -p ${sshPort}` : "ssh"; const excludeArgs = excludePatterns .map((pattern) => `--exclude='${pattern}'`) .join(" "); try { // 1. rsync로 파일 동기화 console.log("📦 Step 1: Syncing files to NAS..."); const rsyncCmd = `rsync -avz --delete ${excludeArgs} -e "${sshCmd}" ${projectRoot}/ ${sshTarget}:${sshPath}/`; console.log(`Command: ${rsyncCmd}`); execSync(rsyncCmd, { stdio: "inherit" }); console.log("✅ Files synced successfully"); console.log(""); // 2. .env.nas를 .env로 복사 console.log("📝 Step 2: Setting up environment variables..."); const envCmd = `${sshCmd} ${sshTarget} "cd ${sshPath} && cp -f .env.nas .env"`; execSync(envCmd, { stdio: "inherit" }); console.log("✅ Environment variables set"); console.log(""); // 3. Docker 빌드 및 배포 console.log("🐳 Step 3: Building and deploying Docker container..."); const deployCmd = `${sshCmd} ${sshTarget} "cd /volume1/docker/webpage && docker compose build ${projectName} && docker compose up -d ${projectName}"`; console.log(`Command: ${deployCmd}`); execSync(deployCmd, { stdio: "inherit" }); console.log("✅ Docker container deployed"); console.log(""); // 4. 헬스체크 console.log("🏥 Step 4: Health check..."); setTimeout(() => { try { const healthCmd = `${sshCmd} ${sshTarget} "docker exec ${projectName} wget -q -O- http://localhost:3000/api/health"`; const health = execSync(healthCmd, { encoding: "utf-8" }); console.log("Health check response:", health); console.log("✅ Service is healthy"); } catch (err) { console.warn("⚠️ Health check failed (service may still be starting)"); } console.log(""); console.log("🎉 Deployment completed!"); console.log("====================================="); console.log(`🌐 Access: http://gahusb.synology.me/saju`); console.log(""); console.log("📝 To check logs:"); console.log(` ssh ${sshTarget} "docker logs ${projectName} --tail 50 -f"`); console.log(""); }, 3000); } catch (err) { console.error("❌ Deployment failed:", err.message); process.exit(1); } return; } // SMB/CIFS 마운트를 통한 배포 console.log("📂 Deploying via SMB mount..."); console.log(`Destination: ${dst}`); console.log(""); if (!fs.existsSync(dst)) { console.error("❌ NAS path not found. Check mount: " + dst); console.log(""); console.log("💡 Tip: Use SSH deployment instead:"); console.log(" NAS_DEPLOY_METHOD=ssh npm run release:nas"); console.log(""); console.log(" Or set environment variables:"); console.log(" NAS_SSH_TARGET=user@gahusb.synology.me"); console.log(" NAS_SSH_PORT=22 (optional)"); process.exit(1); } if (isWin) { console.log("📦 Step 1: Copying files to NAS (Windows - Robocopy)..."); // Robocopy 제외 패턴 const excludeDirs = excludePatterns .filter((p) => p.endsWith("/")) .map((p) => p.slice(0, -1)) .join(" "); const excludeFiles = excludePatterns .filter((p) => !p.endsWith("/")) .join(" "); const excludeDirsArg = excludeDirs ? `/XD ${excludeDirs}` : ""; const excludeFilesArg = excludeFiles ? `/XF ${excludeFiles}` : ""; const cmd = `powershell -NoProfile -ExecutionPolicy Bypass -Command "$ErrorActionPreference=\\"Stop\\"; $src=\\"${projectRoot}\\"; $dst=\\"${dst}\\"; if(!(Test-Path $dst)){ throw \\"NAS drive not found. Check Z: mapping.\\" }; $log = Join-Path (Get-Location) \\"robocopy.log\\"; robocopy $src $dst /MIR ${excludeDirsArg} ${excludeFilesArg} /R:1 /W:1 /E /NFL /NDL /NP /V /TEE /LOG:$log; $rc = $LASTEXITCODE; if($rc -ge 8){ Write-Host \\"robocopy failed with code $rc. See $log\\"; exit $rc } else { exit 0 }"`; try { execSync(cmd, { stdio: "inherit" }); console.log("✅ Files copied successfully"); } catch (err) { console.error("❌ File copy failed:", err.message); process.exit(1); } } else if (isMac) { console.log("📦 Step 1: Copying files to NAS (macOS - ditto)..."); // 쓰기 테스트 const safety = dst.includes("docker/webpage/saju-web"); if (!safety) { console.error("❌ Safety check failed: unexpected dst path: " + dst); process.exit(1); } try { const testPath = `${dst}.deploy-write-test`; fs.writeFileSync(testPath, "ok"); fs.unlinkSync(testPath); } catch (err) { console.error( "❌ NAS write test failed. Files may be locked or permissions are read-only." ); console.error( "💡 Try using SSH deployment instead: NAS_DEPLOY_METHOD=ssh npm run release:nas" ); throw err; } try { execSync(`ditto ${projectRoot} ${dst}`, { stdio: "inherit" }); console.log("✅ Files copied successfully"); } catch (err) { console.error("❌ File copy failed:", err.message); process.exit(1); } } else { // Linux 등 기타 플랫폼 console.log("📦 Step 1: Copying files to NAS (rsync)..."); const excludeArgs = excludePatterns .map((pattern) => `--exclude='${pattern}'`) .join(" "); const cmd = `rsync -avz --delete ${excludeArgs} ${projectRoot}/ ${dst}`; try { execSync(cmd, { stdio: "inherit" }); console.log("✅ Files copied successfully"); } catch (err) { console.error("❌ File copy failed:", err.message); process.exit(1); } } console.log(""); console.log("⚠️ Files copied to NAS. Manual steps required:"); console.log(""); console.log("1. SSH into NAS:"); console.log(` ssh ${sshTarget}`); console.log(""); console.log("2. Setup environment:"); console.log(` cd ${sshPath}`); console.log(` cp -f .env.nas .env`); console.log(""); console.log("3. Deploy Docker container:"); console.log(` cd /volume1/docker/webpage`); console.log(` docker compose build ${projectName}`); console.log(` docker compose up -d ${projectName}`); console.log(""); console.log("💡 Tip: Use SSH deployment for automated process:"); console.log(" NAS_DEPLOY_METHOD=ssh npm run release:nas"); console.log("");