import SftpClient from 'ssh2-sftp-client'; import { readdir, stat } from 'fs/promises'; import { join } from 'path'; const sftp = new SftpClient(); const config = { host: 'nas.rucky.cn', port: 22, username: 'rucky', password: 'Scl@qq.com1' }; const localRoot = './dist'; const remoteRoot = '/web/sports'; console.log('🚀 开始部署到 SFTP 服务器...'); console.log(`📦 服务器: ${config.host}:${config.port}`); console.log(`📂 远程路径: ${remoteRoot}\n`); let uploadedFiles = 0; let uploadedSize = 0; // 递归删除远程目录 async function removeDirectory(remotePath) { try { const exists = await sftp.exists(remotePath); if (!exists) return; const list = await sftp.list(remotePath); for (const item of list) { const itemPath = `${remotePath}/${item.name}`.replace(/\\/g, '/'); if (item.type === 'd') { await removeDirectory(itemPath); await sftp.rmdir(itemPath); } else { await sftp.delete(itemPath); } } } catch (err) { console.log(`⚠️ 清理失败: ${remotePath} - ${err.message}`); } } // 递归上传目录 async function uploadDirectory(localDir, remoteDir) { const files = await readdir(localDir); for (const file of files) { const localPath = join(localDir, file); const remotePath = `${remoteDir}/${file}`.replace(/\\/g, '/'); try { const stats = await stat(localPath); if (stats.isDirectory()) { // 创建目录 try { const exists = await sftp.exists(remotePath); if (!exists) { await sftp.mkdir(remotePath); } } catch (err) { // 忽略 } await uploadDirectory(localPath, remotePath); } else { // 上传文件 await sftp.put(localPath, remotePath); uploadedFiles++; uploadedSize += stats.size; const sizeMB = (stats.size / 1024 / 1024).toFixed(2); console.log(`✓ [${uploadedFiles}] ${file} (${sizeMB} MB)`); } } catch (err) { console.log(`❌ 失败: ${file} - ${err.message}`); } } } async function deploy() { const startTime = Date.now(); try { // 1. 连接服务器 console.log('🔗 连接服务器...'); await sftp.connect(config); console.log('✓ 连接成功!\n'); // 2. 检查并清理旧文件 console.log('🧹 清理旧文件...'); const exists = await sftp.exists(remoteRoot); if (exists) { await removeDirectory(remoteRoot); console.log('✓ 旧文件已清理\n'); } else { console.log('✓ 无需清理(首次部署)\n'); } // 3. 创建根目录 console.log('📁 创建目标目录...'); await sftp.mkdir(remoteRoot, true); console.log('✓ 目录创建完成\n'); // 4. 上传所有文件 console.log('📤 上传文件...\n'); await uploadDirectory(localRoot, remoteRoot); // 5. 验证部署 console.log('\n🔍 验证部署...'); const fileList = await sftp.list(remoteRoot); console.log(`✓ 部署成功!共 ${fileList.length} 个文件/目录`); const totalSizeMB = (uploadedSize / 1024 / 1024).toFixed(2); const duration = ((Date.now() - startTime) / 1000).toFixed(1); console.log(`\n📊 统计信息:`); console.log(` - 上传文件数: ${uploadedFiles}`); console.log(` - 总大小: ${totalSizeMB} MB`); console.log(` - 耗时: ${duration} 秒`); console.log(` - 平均速度: ${(uploadedSize / 1024 / 1024 / duration * 1000).toFixed(2)} MB/s`); console.log(`\n✅ 部署完成!`); console.log(`🌐 访问地址: http://${config.host}/sports/\n`); await sftp.end(); process.exit(0); } catch (err) { console.error('\n❌ 部署失败:', err.message); console.error('详细信息:', err); try { await sftp.end(); } catch (e) {} process.exit(1); } } deploy();