/** * Deploy script - Upload files to remote server using SSH2 * 使用 ssh2-sftp-client 避免每次输入密钥密码 */ import * as fs from 'fs'; import * as path from 'path'; import SftpClient from 'ssh2-sftp-client'; import * as dotenv from 'dotenv'; // 加载 .env 文件 dotenv.config(); // Configuration const config = { host: '127.0.0.1', port: 1122, username: 'cubie', privateKey: fs.readFileSync('d:\\163'), passphrase: process.env.SSH_PASSPHRASE || '', }; const destinations = { server: '/home/cubie/down/document/bidding/publish/server', frontend: '/home/cubie/down/document/bidding/publish/frontend', src: '/home/cubie/down/document/bidding/', }; async function uploadDirectory( sftp: SftpClient, localPath: string, remotePath: string, ): Promise { const startTime = Date.now(); console.log(`\n[上传] ${localPath} -> ${remotePath}`); // 检查本地目录 if (!fs.existsSync(localPath)) { throw new Error(`本地目录不存在: ${localPath}`); } // 统计文件数量 const countFiles = (dir: string): number => { let count = 0; const items = fs.readdirSync(dir); for (const item of items) { const fullPath = path.join(dir, item); if (fs.statSync(fullPath).isDirectory()) { count += countFiles(fullPath); } else { count++; } } return count; }; const fileCount = countFiles(localPath); console.log(` 文件数量: ${fileCount}`); await sftp.uploadDir(localPath, remotePath); const duration = ((Date.now() - startTime) / 1000).toFixed(2); console.log(` 完成! 耗时: ${duration}s`); } async function deploy(): Promise { // 检查必要目录 const requiredDirs = ['dist', 'frontend', 'src']; for (const dir of requiredDirs) { if (!fs.existsSync(dir)) { console.error(`${dir} 目录不存在`); process.exit(1); } } // 检查私钥文件 if (!fs.existsSync('d:\\163')) { console.error('私钥文件不存在: d:\\163'); process.exit(1); } if (!config.passphrase) { console.error('请在 .env 文件中设置 SSH_PASSPHRASE'); process.exit(1); } const sftp = new SftpClient(); // 添加详细日志 sftp.on('upload', (info) => { console.log(` 已上传: ${info.source}`); }); try { console.log('开始部署...'); console.log(`远程服务器: ${config.host}:${config.port}`); console.log(`用户名: ${config.username}`); console.log(`私钥文件: d:\\163`); console.log('正在连接...'); await sftp.connect({ ...config, keepaliveInterval: 5000, // 每5秒发送keepalive keepaliveCountMax: 10, readyTimeout: 60000, // 60秒连接超时 }); console.log('连接成功!'); // 上传 dist 目录内容到 server 目录 await uploadDirectory(sftp, 'dist', destinations.server); // 上传 frontend/dist 到 frontend 目录 await uploadDirectory( sftp, path.join('frontend', 'dist'), destinations.frontend, ); // 上传 src 目录 await uploadDirectory(sftp, 'src', destinations.src + 'src'); // 上传 package.json console.log('\n[上传] package.json -> ' + destinations.src + 'package.json'); await sftp.put('package.json', destinations.src + 'package.json'); console.log(' 已上传: package.json'); console.log('\n========================================'); console.log('部署完成!'); console.log('========================================'); } catch (err) { console.error('\n========================================'); console.error('部署失败!'); console.error('========================================'); console.error('错误信息:', err instanceof Error ? err.message : err); if (err instanceof Error && err.stack) { console.error('\n堆栈信息:'); console.error(err.stack); } process.exit(1); } finally { console.log('\n断开连接...'); await sftp.end(); console.log('连接已关闭'); } } deploy();