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

150 lines
5.6 KiB
JavaScript
Raw Permalink Normal View History

/**
*
* 铸渊远程部署 · 第二阶段安装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)
})