const { Client } = require('ssh2'); const SSH_CONFIG = { host: '119.45.10.34', port: 22, username: 'root', password: '#xyzh%CS#2512@28' }; function sshExec(conn, cmd) { return new Promise((resolve, reject) => { conn.exec(cmd, (err, s) => { if (err) return reject(err); let o = ''; s.on('data', d => o += d); s.stderr.on('data', d => o += d); s.on('close', () => resolve(o)); }); }); } const fs = require('fs'); const path = require('path'); function sftpUpload(conn, localPath, remotePath) { return new Promise((resolve, reject) => { conn.sftp((err, sftp) => { if (err) return reject(err); const rs = fs.createReadStream(localPath); const ws = sftp.createWriteStream(remotePath); ws.on('close', () => resolve()); ws.on('error', reject); rs.pipe(ws); }); }); } const REMOTE_BASE = '/www/wwwroot/demo.tensorgrove.com.cn/server'; const LOCAL_BASE = path.join(__dirname, '..'); async function main() { const conn = new Client(); await new Promise((resolve, reject) => { conn.on('ready', resolve).on('error', reject).connect(SSH_CONFIG); }); // 1. 上传文件 const files = [ { name: 'toolExecutor.js', sub: 'services' }, { name: 'kbRetriever.js', sub: 'services' }, ]; console.log('=== 上传 ==='); for (const f of files) { const localFile = path.join(LOCAL_BASE, f.sub, f.name); const remoteFile = `${REMOTE_BASE}/${f.sub}/${f.name}`; await sftpUpload(conn, localFile, remoteFile); console.log(` \u2705 ${f.name}`); const sc = await sshExec(conn, `node -c ${remoteFile} 2>&1`); if (sc.includes('SyntaxError')) { console.log('SYNTAX ERROR!'); conn.end(); process.exit(1); } } // 2. 刷 Redis KB 缓存 console.log('\n=== 刷 Redis KB 缓存 ==='); console.log(await sshExec(conn, `cd ${REMOTE_BASE} && node -e " require('dotenv').config(); const Redis = require('ioredis'); const r = new Redis(process.env.REDIS_URL || 'redis://127.0.0.1:6379', { password: process.env.REDIS_PASSWORD || undefined, db: parseInt(process.env.REDIS_DB) || 0, keyPrefix: process.env.REDIS_KEY_PREFIX || 'bigwo:', lazyConnect: true, maxRetriesPerRequest: 2, connectTimeout: 5000, }); r.connect().then(async () => { const keys = await r.keys('kb_cache:*'); if (keys.length > 0) { await r.del(...keys); console.log('Deleted ' + keys.length + ' keys'); } else { console.log('No keys'); } r.quit(); process.exit(0); }).catch(e => { console.log('Redis: ' + e.message); process.exit(0); }); " 2>&1`)); // 3. 重启 await sshExec(conn, '> /var/log/bigwo/server-error.log && > /var/log/bigwo/server-out.log'); await sshExec(conn, 'pm2 stop bigwo-server'); await new Promise(r => setTimeout(r, 1000)); await sshExec(conn, `cd ${REMOTE_BASE} && pm2 start bigwo-server --update-env`); console.log('\n=== PM2 重启,等待5s ==='); await new Promise(r => setTimeout(r, 5000)); console.log(await sshExec(conn, 'pm2 status bigwo-server')); const errLog = await sshExec(conn, 'cat /var/log/bigwo/server-error.log'); console.log('=== 错误 ==='); console.log(errLog || '(空 ✅)'); console.log('\n=== 健康 ==='); console.log(await sshExec(conn, 'curl -s --max-time 5 http://localhost:3012/api/health 2>&1')); conn.end(); } main().catch(e => { console.error('FAILED:', e.message); process.exit(1); });