144 lines
3.9 KiB
JavaScript
144 lines
3.9 KiB
JavaScript
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();
|