guanghulab/image-studio/deploy/remote-deploy-2.mjs

150 lines
5.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* ═══════════════════════════════════════════════════
* 铸渊远程部署 · 第二阶段安装Chrome + 上传源码 + 启动服务
* ═══════════════════════════════════════════════════
*/
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' },
}
const LOCAL_BASE = '/workspace/guanghulab/image-studio'
import { readFileSync } from 'fs'
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(`${cmd.slice(0, 100)}`)
const result = await callGatekeeper(server, 'POST', '/exec', { cmd })
if (result.status !== 200) {
console.log(` ❌ [${result.status}] ${JSON.stringify(result.data).slice(0, 200)}`)
} else {
const out = (result.data.stdout || '').trim()
const err = (result.data.stderr || '').trim()
if (out) console.log(` ${out.split('\n').slice(0, 8).join('\n ')}`)
if (err) console.log(` ⚠️ ${err.split('\n').slice(0, 4).join('\n ')}`)
}
return result
}
async function writeFile(server, remotePath, localPath) {
const content = readFileSync(localPath, 'utf-8')
const result = await callGatekeeper(server, 'POST', '/file/write', { path: remotePath, content })
if (result.status === 200) {
console.log(`${remotePath} (${content.length} 字节)`)
} else {
console.log(`${remotePath}: ${JSON.stringify(result.data)}`)
}
return result
}
async function main() {
const sg = SERVERS.sg
const gz = SERVERS.gz
// ======= 1. 在新加坡服务器安装Chrome =======
console.log('═'.repeat(50))
console.log('📦 1. 在新加坡服务器安装Chrome')
console.log('═'.repeat(50))
// 检查系统版本安装Chromium
await exec(sg, 'cat /etc/os-release | head -3')
// 安装ChromiumUbuntu仓库中有chromium-browser
console.log('\n 安装Chromium...')
await exec(sg, 'apt-get install -y -qq chromium-browser 2>&1 | tail -5')
// 验证安装
await exec(sg, 'which chromium-browser && chromium-browser --version || echo "not found"')
// 如果没有chromium-browser试试用npx puppeteer安装
await exec(sg, 'which chromium-browser || (cd /data/image-studio && npx puppeteer browsers install chrome 2>&1 | tail -5)')
// ======= 2. 上传源代码文件 =======
console.log('\n' + '═'.repeat(50))
console.log('📤 2. 上传源代码到新加坡服务器')
console.log('═'.repeat(50))
const files = [
['config.js', '/data/image-studio/config.js'],
['renderer.js', '/data/image-studio/renderer.js'],
['generate.js', '/data/image-studio/generate.js'],
['server.js', '/data/image-studio/server.js'],
['templates/xiaohongshu.js', '/data/image-studio/templates/xiaohongshu.js'],
['templates/jike.js', '/data/image-studio/templates/jike.js'],
['templates/poster.js', '/data/image-studio/templates/poster.js'],
['templates/dynamic.js', '/data/image-studio/templates/dynamic.js'],
['deploy/setup.sh', '/data/image-studio/deploy/setup.sh'],
]
for (const [local, remote] of files) {
await writeFile(sg, remote, `${LOCAL_BASE}/${local}`)
}
// 更新package.json添加type:module
await exec(sg, `cd /data/image-studio && npm pkg set type="module"`)
// ======= 3. 启动服务 =======
console.log('\n' + '═'.repeat(50))
console.log('🚀 3. 启动图片工作室服务')
console.log('═'.repeat(50))
// 先用node直接启动测试是否能正常运行
await exec(sg, 'cd /data/image-studio && timeout 5 node server.js 2>&1 || true')
// 用PM2启动后台运行
await exec(sg, 'pm2 delete image-studio 2>/dev/null; cd /data/image-studio && pm2 start server.js --name image-studio -o /data/image-studio/output.log -e /data/image-studio/error.log')
await exec(sg, 'sleep 2 && pm2 list')
// 验证服务是否运行
await exec(sg, 'curl -s http://localhost:3912/ | head -5 || echo "服务未启动"')
await exec(sg, 'ss -tlnp | grep 3912')
// ======= 4. 保存PM2配置 =======
await exec(sg, 'pm2 save')
// ======= 5. 检查广州Nginx配置 =======
console.log('\n' + '═'.repeat(50))
console.log('🌐 4. 检查广州Nginx配置')
console.log('═'.repeat(50))
// 读取Nginx配置
const nginxResult = await callGatekeeper(gz, 'POST', '/file/read', { path: '/etc/nginx/nginx.conf' })
if (nginxResult.status === 200) {
console.log(nginxResult.data.content.slice(0, 1000))
} else {
// 试试conf.d
const confResult = await callGatekeeper(gz, 'POST', '/exec', { cmd: 'ls /etc/nginx/sites-enabled/ /etc/nginx/conf.d/ 2>&1' })
if (confResult.status === 200) {
console.log('Nginx配置目录:', confResult.data.stdout || confResult.data.stderr)
}
}
console.log('\n' + '═'.repeat(50))
console.log('✅ 部署流程完成!')
console.log('═'.repeat(50))
}
main().catch(err => {
console.error('❌ 失败:', err.message)
process.exit(1)
})