import { Client } from "ssh2"; import { readFileSync } from "fs"; import { join } from "path"; const SSH_CONFIG = { host: "119.45.10.34", port: 22, username: "root", password: "#xyzh%CS#2512@28", readyTimeout: 10000, }; function sshExec(command, timeout = 30000) { return new Promise((resolve, reject) => { const conn = new Client(); let stdout = ""; let stderr = ""; let timer = setTimeout(() => { conn.end(); resolve({ stdout, stderr: stderr + "\n[TIMEOUT]", code: -1 }); }, timeout); conn.on("ready", () => { conn.exec(command, (err, stream) => { if (err) { clearTimeout(timer); conn.end(); return reject(err); } stream.on("close", (code) => { clearTimeout(timer); conn.end(); resolve({ stdout, stderr, code }); }); stream.on("data", (d) => { stdout += d.toString(); }); stream.stderr.on("data", (d) => { stderr += d.toString(); }); }); }).on("error", (err) => { clearTimeout(timer); reject(err); }).connect(SSH_CONFIG); }); } function sshUpload(localPath, remotePath) { return new Promise((resolve, reject) => { const conn = new Client(); conn.on("ready", () => { conn.sftp((err, sftp) => { if (err) { conn.end(); return reject(err); } const content = readFileSync(localPath); const ws = sftp.createWriteStream(remotePath); ws.on("close", () => { conn.end(); resolve(); }); ws.on("error", (e) => { conn.end(); reject(e); }); ws.write(content); ws.end(); }); }).on("error", (err) => reject(err)).connect(SSH_CONFIG); }); } const PROJECT = "/www/wwwroot/demo.tensorgrove.com.cn"; const LOCAL_BASE = "C:\\Users\\UI\\Desktop\\bigwo\\test2"; async function main() { const filesToDeploy = [ { local: "server\\routes\\voice.js", remote: `${PROJECT}/server/routes/voice.js` }, { local: "server\\services\\toolExecutor.js", remote: `${PROJECT}/server/services/toolExecutor.js` }, { local: "server\\config\\voiceChatConfig.js", remote: `${PROJECT}/server/config/voiceChatConfig.js` }, { local: "server\\config\\tools.js", remote: `${PROJECT}/server/config/tools.js` }, { local: "server\\app.js", remote: `${PROJECT}/server/app.js` }, ]; // 1. 备份 console.log("=== 1. 备份服务器文件 ==="); const backupDir = `${PROJECT}/server/_backup_${Date.now()}`; const backupResult = await sshExec(`mkdir -p ${backupDir} && cp ${PROJECT}/server/routes/voice.js ${PROJECT}/server/services/toolExecutor.js ${PROJECT}/server/config/voiceChatConfig.js ${PROJECT}/server/app.js ${backupDir}/ 2>&1`); console.log(backupResult.stdout || "(done)"); console.log(`备份目录: ${backupDir}`); // 2. 上传文件 console.log("\n=== 2. 上传文件 ==="); for (const { local, remote } of filesToDeploy) { const localPath = join(LOCAL_BASE, local); try { await sshUpload(localPath, remote); console.log(`✅ ${local} → ${remote}`); } catch (e) { console.error(`❌ ${local}: ${e.message}`); } } // 3. 重启 PM2 console.log("\n=== 3. 重启 PM2 ==="); const restart = await sshExec("pm2 restart bigwo-server 2>&1"); console.log(restart.stdout); // 4. 等待 3 秒检查状态 console.log("=== 4. 等待 3 秒后检查 ==="); await new Promise(r => setTimeout(r, 3000)); const health = await sshExec(`curl -s http://127.0.0.1:3012/api/health 2>&1`); console.log("Health:", health.stdout); const pm2 = await sshExec("pm2 list 2>&1"); console.log(pm2.stdout); // 5. 检查最新日志 console.log("=== 5. 启动日志 ==="); const logs = await sshExec("pm2 logs bigwo-server --nostream --lines 15 2>&1"); console.log(logs.stdout); if (logs.stderr) console.log(logs.stderr); // 6. 验证文件行数 console.log("=== 6. 验证文件 ==="); const wc = await sshExec(`wc -l ${PROJECT}/server/routes/voice.js ${PROJECT}/server/services/toolExecutor.js ${PROJECT}/server/config/voiceChatConfig.js ${PROJECT}/server/app.js 2>&1`); console.log(wc.stdout); console.log("\n=== 部署完成 ==="); } main().catch(e => { console.error("Fatal:", e.message); process.exit(1); });