const http = require('http'); const { exec } = require('child_process'); const path = require('path'); const crypto = require('crypto'); const PORT = 19001; const DEPLOY_DIR = '/opt/miniapp-api_test'; const COMPOSE_FILE = 'docker-compose.test.yml'; const SECRET = process.env.WEBHOOK_SECRET || 'miniapp-api-deploy-secret'; function verifySignature(payload, signature) { const hmac = crypto.createHmac('sha256', SECRET); const digest = 'sha256=' + hmac.update(payload).digest('hex'); return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature)); } function runCommand(command, cwd) { return new Promise((resolve, reject) => { console.log(`[${new Date().toISOString()}] 执行命令: ${command}`); const child = exec(command, { cwd, maxBuffer: 1024 * 1024 }, (error, stdout, stderr) => { if (error) { console.error(`[${new Date().toISOString()}] 命令执行失败:`, error.message); console.error('stderr:', stderr); reject(error); return; } console.log(`[${new Date().toISOString()}] 命令输出:\n${stdout}`); if (stderr) console.error(`stderr: ${stderr}`); resolve(stdout); }); }); } async function deploy() { const timestamp = new Date().toISOString(); console.log(`\n========== 开始部署 miniapp-api_test [${timestamp}] ==========`); try { await runCommand('git fetch origin', DEPLOY_DIR); await runCommand('git reset --hard origin/main', DEPLOY_DIR); await runCommand(`docker-compose -f ${COMPOSE_FILE} build --no-cache`, DEPLOY_DIR); await runCommand(`docker-compose -f ${COMPOSE_FILE} up -d`, DEPLOY_DIR); await runCommand('docker image prune -f', DEPLOY_DIR); console.log(`[${new Date().toISOString()}] 部署成功完成`); return { success: true, message: '部署成功' }; } catch (error) { console.error(`[${new Date().toISOString()}] 部署失败:`, error.message); return { success: false, message: error.message }; } } const server = http.createServer(async (req, res) => { if (req.method !== 'POST' || req.url !== '/webhook') { res.writeHead(404); res.end('Not Found'); return; } let body = ''; req.on('data', chunk => { body += chunk; }); req.on('end', async () => { const signature = req.headers['x-hub-signature-256'] || req.headers['x-gitlab-token']; if (SECRET !== 'your_webhook_secret_here' && signature) { const isValid = verifySignature(body, signature); if (!isValid) { console.warn(`[${new Date().toISOString()}] Webhook 签名验证失败`); res.writeHead(401); res.end('Unauthorized'); return; } } let payload; try { payload = JSON.parse(body); } catch (e) { payload = {}; } const ref = payload.ref || payload.object_attributes?.ref || 'unknown'; console.log(`[${new Date().toISOString()}] 收到 Webhook 请求, ref: ${ref}`); if (ref === 'refs/heads/main' || ref === 'main' || !ref.includes('refs/')) { res.writeHead(202); res.end(JSON.stringify({ status: 'accepted', message: '部署任务已启动' })); const result = await deploy(); console.log(`[${new Date().toISOString()}] 部署结果:`, result); } else { res.writeHead(200); res.end(JSON.stringify({ status: 'ignored', message: '非 main 分支推送, 忽略' })); } }); }); server.listen(PORT, '127.0.0.1', () => { console.log(`Webhook 服务器已启动, 监听端口 ${PORT}`); console.log(`部署目录: ${DEPLOY_DIR}`); console.log(`接收地址: http://127.0.0.1:${PORT}/webhook`); console.log(`Nginx 代理地址: http://your-server-ip/webhook`); });