const { Client } = require('ssh2'); const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); const privateKey = fs.readFileSync('D:\\Trae\\huishou用户密钥id_ed25519', 'utf8'); const keyConfig = { host: 'hs.yifenbao.cn', port: 22, username: 'root', privateKey: privateKey }; const pwConfig = { host: 'hs.yifenbao.cn', port: 22, username: 'root', password: 'Torch1112' }; function createConnection(config) { return new Promise((resolve, reject) => { const conn = new Client(); conn.on('ready', () => resolve(conn)); conn.on('error', (err) => { conn.end(); reject(err); }); conn.connect(config); }); } async function executeCommand(conn, cmd) { return new Promise((resolve, reject) => { conn.exec(cmd, (err, stream) => { if (err) { reject(err); return; } stream.stdout.on('data', (data) => process.stdout.write(data)); stream.stderr.on('data', (data) => process.stdout.write(data)); stream.on('close', (code) => { if (code === 0) { resolve(); } else { reject(new Error(`命令执行失败 (退出码: ${code})`)); } }); }); }); } async function uploadFileViaSFTP(conn, localPath, remotePath) { return new Promise((resolve, reject) => { conn.sftp((err, sftp) => { if (err) { reject(err); return; } const dir = path.dirname(remotePath); sftp.mkdir(dir, { recursive: true }, (err) => { if (err && err.code !== 4) { reject(err); return; } const readStream = fs.createReadStream(localPath); const writeStream = sftp.createWriteStream(remotePath); writeStream.on('close', () => { console.log(`✓ ${localPath}`); resolve(); }); writeStream.on('error', (err) => { reject(err); }); readStream.pipe(writeStream); }); }); }); } function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function run() { let conn; try { console.log('尝试使用密钥登录...'); conn = await createConnection(keyConfig); console.log('SSH连接成功(密钥登录)'); } catch (err) { console.log('密钥登录失败:', err.message); console.log('尝试密码登录...'); conn = await createConnection(pwConfig); console.log('SSH连接成功(密码登录)'); } try { console.log('\n=== 上传核心文件 ==='); const coreFiles = [ { local: './server/server.js', remote: '/www/huishou/server/server.js' }, { local: './server/package.json', remote: '/www/huishou/server/package.json' }, { local: './admin/index.html', remote: '/www/huishou/admin/index.html' }, { local: './admin/nginx.conf', remote: '/www/huishou/admin/nginx.conf' }, { local: './docker-compose.yml', remote: '/www/huishou/docker-compose.yml' }, ]; for (const file of coreFiles) { await uploadFileViaSFTP(conn, file.local, file.remote); } console.log('\n=== 打包H5文件 ==='); const tarPath = './h5-dist.tar.gz'; execSync(`cd dist/build/h5 && tar -czf ../../../${tarPath} .`, { stdio: 'ignore' }); console.log('✓ 使用系统tar命令打包完成'); console.log('\n=== 上传H5打包文件 ==='); await uploadFileViaSFTP(conn, tarPath, '/www/huishou/h5-dist.tar.gz'); console.log('✓ H5打包文件上传完成'); console.log('\n=== 解压H5文件 ==='); await executeCommand(conn, 'cd /www/huishou && mkdir -p dist/build/h5 && rm -rf dist/build/h5/* && tar -xzf h5-dist.tar.gz -C dist/build/h5'); console.log('✓ H5文件解压完成'); console.log('\n=== 清理临时文件 ==='); fs.unlinkSync(tarPath); await executeCommand(conn, 'rm -f /www/huishou/h5-dist.tar.gz'); console.log('\n=== 开始部署 ==='); await executeCommand(conn, 'cd /www/huishou && docker-compose down'); console.log('\n=== 构建容器 ==='); await executeCommand(conn, 'cd /www/huishou && docker-compose build --no-cache 2>&1'); console.log('\n=== 启动服务 ==='); await executeCommand(conn, 'cd /www/huishou && docker-compose up -d'); console.log('\n=== 等待服务启动 ==='); await sleep(30000); console.log('\n=== 检查容器状态 ==='); await executeCommand(conn, 'docker ps'); console.log('\n=== 检查API ==='); await executeCommand(conn, 'curl -s http://localhost:4000/api/statistics'); console.log('\n=== 部署完成 ==='); } catch (error) { console.error('部署失败:', error.message); process.exit(1); } finally { if (conn) conn.end(); } } run();