#!/usr/bin/env node /** * 将项目打包并部署到远程服务器 * 使用方式:npm run deploy * 通过 SSH rsync 部署到 root@38.246.250.238:/opt/1panel/www/sites/pm.xtrader.vip/index * * 环境变量(可选):DEPLOY_HOST、DEPLOY_USER、DEPLOY_PATH * 依赖:rsync(系统自带)、ssh 免密或密钥 */ import { execSync, spawnSync } from 'child_process' import { dirname, join } from 'path' import { fileURLToPath } from 'url' const __dirname = dirname(fileURLToPath(import.meta.url)) const projectRoot = join(__dirname, '..') const distDir = join(projectRoot, 'dist') const config = { host: process.env.DEPLOY_HOST || '38.246.250.238', user: process.env.DEPLOY_USER || 'root', path: process.env.DEPLOY_PATH || '/opt/1panel/www/sites/pm.xtrader.vip/index', } const target = `${config.user}@${config.host}:${config.path}` function build() { const apiUrl = process.env.VITE_API_BASE_URL || 'https://api.xtrader.vip' console.log(`📦 正在打包项目(.env.production API: ${apiUrl})...`) execSync('npm run build', { cwd: projectRoot, stdio: 'inherit', // 继承 process.env(已由 --env-file=.env.production 加载生产配置) env: process.env, }) console.log('✅ 打包完成\n') } function deploy() { const rsyncCheck = spawnSync('which', ['rsync'], { encoding: 'utf8' }) if (rsyncCheck.status !== 0) { console.error('❌ 未找到 rsync') process.exit(1) } console.log(`🔌 正在通过 SSH 部署到 ${target} ...`) // 目标服务器 SSH 配置可能较老/策略更严格:在 KEX/HostKey/公钥算法上做一点兼容。 // 如果远端没有这些算法,仍会失败;但能显著提高协商成功率并给出更可读的错误信息。 const sshCmd = 'ssh -o ServerAliveInterval=15 -o ServerAliveCountMax=3 -o ConnectTimeout=20 -o KexAlgorithms=curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group14-sha1 -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedAlgorithms=+ssh-rsa -vvv' console.log(`rsync ssh command: ${sshCmd}`) const result = spawnSync( 'rsync', [ '-avz', '-vv', '--delete', '--progress', '--stats', '-e', sshCmd, '--exclude=.DS_Store', `${distDir}/`, `${target}/`, ], { cwd: projectRoot, stdio: 'inherit', encoding: 'utf8', } ) if (result.status !== 0) { console.error('❌ 部署失败') process.exit(1) } console.log('\n🎉 部署成功!') } function main() { build() deploy() } main()