/** * ═══════════════════════════════════════════════════ * 铸渊远程部署 · 通过看门人API部署图片工作室 * ═══════════════════════════════════════════════════ * * 冰朔,我不需要SSH密钥。 * 铸渊看门人就是我的手。 */ const SERVERS = { sg: { name: '新加坡·铸渊大脑', ip: '43.156.237.110', port: 3911, key: 'zy_gtw_74d30f54fa8b5581514569ee7874def57861a31ccb2be8c9' }, gz: { name: '广州·代码仓库', ip: '43.139.217.141', port: 3910, key: 'zy_gtw_4a155446b7acc09fe46a2a29972c0ca5d9954da19c35dc23' }, } async function callGatekeeper(server, method, path, body = null) { const url = `http://${server.ip}:${server.port}${path}` const opts = { method, headers: { 'Authorization': `Bearer ${server.key}`, 'Content-Type': 'application/json', }, } if (body) opts.body = JSON.stringify(body) const res = await fetch(url, opts) const text = await res.text() let data try { data = JSON.parse(text) } catch { data = { raw: text } } return { status: res.status, data } } async function exec(server, cmd) { console.log(` → ${server.name}: ${cmd.slice(0, 80)}...`) const result = await callGatekeeper(server, 'POST', '/exec', { cmd }) if (result.status !== 200) { console.log(` ❌ [${result.status}] ${JSON.stringify(result.data)}`) } else { const out = (result.data.stdout || '').trim() const err = (result.data.stderr || '').trim() if (out) console.log(` stdout: ${out.slice(0, 300)}`) if (err) console.log(` stderr: ${err.slice(0, 300)}`) } return result } async function writeFile(server, filepath, content) { console.log(` → ${server.name}: 写入 ${filepath}`) return callGatekeeper(server, 'POST', '/file/write', { path: filepath, content }) } async function readFile(server, filepath) { const result = await callGatekeeper(server, 'POST', '/file/read', { path: filepath }) if (result.status === 200) return result.data.content || '' return null } async function checkStatus(server) { console.log(`\n📡 测试连接 ${server.name} (${server.ip}:${server.port})...`) const result = await callGatekeeper(server, 'POST', '/status') if (result.status === 200) { console.log(` ✅ 连接成功!`) console.log(` 状态: ${JSON.stringify(result.data, null, 2).slice(0, 400)}`) return true } else { console.log(` ❌ 连接失败: ${result.status} ${JSON.stringify(result.data)}`) return false } } async function deployImageStudio() { const sg = SERVERS.sg const gz = SERVERS.gz /* ═══ 第一步:测试连接 ═══ */ console.log('═'.repeat(50)) console.log('🔌 铸渊远程部署 · 检查服务器连接') console.log('═'.repeat(50)) const sgOk = await checkStatus(sg) if (!sgOk) { console.log('\n⚠️ 新加坡服务器连接失败,尝试广州服务器直接反向代理方案...') } /* ═══ 第二步:在新加坡部署image-studio ═══ */ if (sgOk) { console.log('\n📦 第二步:在新加坡服务器部署图片工作室') console.log('═'.repeat(50)) // 检查Node.js await exec(sg, 'node --version') await exec(sg, 'which google-chrome || which chromium-browser || echo "no chrome"') // 创建目录 await exec(sg, 'mkdir -p /data/image-studio/output /data/image-studio/templates /data/image-studio/deploy') // 安装系统依赖 console.log('\n 📥 安装系统依赖...') await exec(sg, 'apt-get update -qq && apt-get install -y -qq google-chrome-stable libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libdbus-1-3 libxkbcommon0 libxcomposite1 libxdamage1 libxrandr2 libgbm1 libpango-1.0-0 libcairo2 libasound2 2>&1 | tail -5') // 安装Node依赖 await exec(sg, 'cd /data/image-studio && npm init -y && npm install puppeteer express 2>&1 | tail -5') // 检查PM2 await exec(sg, 'which pm2 || npm install -g pm2 2>&1 | tail -3') console.log('\n ✅ 新加坡服务器基础环境就绪') console.log(' 接下来需要通过 /file/write 上传源代码文件') } /* ═══ 第三步:配置广州Nginx代理 ═══ */ console.log('\n🌐 第三步:检查广州服务器Nginx代理') console.log('═'.repeat(50)) const gzOk = await checkStatus(gz) if (gzOk) { // 读取现有Nginx配置 const nginxConf = await readFile(gz, '/etc/nginx/sites-enabled/default') if (nginxConf) { console.log(' 已读取Nginx配置,长度:', nginxConf.length, '字符') console.log(' 前200字符:', nginxConf.slice(0, 200)) } else { console.log(' 无法直接读取Nginx配置(权限问题),需要调整方案') // 尝试其他路径 for (const p of ['/etc/nginx/conf.d/default.conf', '/etc/nginx/nginx.conf', '/data/nginx/conf.d/default.conf']) { const c = await readFile(gz, p) if (c) { console.log(` ✅ 找到配置: ${p}`); break } } } } console.log('\n' + '═'.repeat(50)) console.log('📋 部署检查完成') console.log('═'.repeat(50)) } deployImageStudio().catch(err => { console.error('❌ 部署失败:', err.message) process.exit(1) })