- 添加 Dockerfile 和 docker-compose.yml 支持容器化部署 - 添加 PM2 配置文件 ecosystem.config.js - 添加部署脚本模板 deploy.sh.example - 添加 Gitea webhook 处理器模板 webhook-handler.js.example - 添加生产环境配置模板 .env.production.example - 添加详细的部署指南 DEPLOYMENT.md - 更新 .gitignore 排除敏感配置文件
86 lines
2.5 KiB
Plaintext
86 lines
2.5 KiB
Plaintext
const http = require('http');
|
||
const crypto = require('crypto');
|
||
const { exec } = require('child_process');
|
||
|
||
// 配置 - 复制此文件为 webhook-handler.js 并修改配置
|
||
const PORT = 9000;
|
||
const SECRET = 'your_webhook_secret_change_this'; // 与 Gitea 中配置的密钥一致
|
||
const DEPLOY_SCRIPT = '/var/www/pixel-game-server/deploy.sh'; // 修改为实际路径
|
||
|
||
// 验证 Gitea 签名
|
||
function verifySignature(payload, signature, secret) {
|
||
const hmac = crypto.createHmac('sha256', secret);
|
||
hmac.update(payload);
|
||
const calculatedSignature = hmac.digest('hex');
|
||
return crypto.timingSafeEqual(
|
||
Buffer.from(signature, 'hex'),
|
||
Buffer.from(calculatedSignature, 'hex')
|
||
);
|
||
}
|
||
|
||
// 创建 HTTP 服务器
|
||
const server = http.createServer((req, res) => {
|
||
if (req.method !== 'POST') {
|
||
res.writeHead(405, { 'Content-Type': 'text/plain' });
|
||
res.end('Method Not Allowed');
|
||
return;
|
||
}
|
||
|
||
let body = '';
|
||
req.on('data', chunk => {
|
||
body += chunk.toString();
|
||
});
|
||
|
||
req.on('end', () => {
|
||
try {
|
||
// 验证签名
|
||
const signature = req.headers['x-gitea-signature'];
|
||
if (!signature || !verifySignature(body, signature.replace('sha256=', ''), SECRET)) {
|
||
console.log('签名验证失败');
|
||
res.writeHead(401, { 'Content-Type': 'text/plain' });
|
||
res.end('Unauthorized');
|
||
return;
|
||
}
|
||
|
||
const payload = JSON.parse(body);
|
||
|
||
// 检查是否是推送到 main 分支
|
||
if (payload.ref === 'refs/heads/main') {
|
||
console.log('收到 main 分支推送,开始部署...');
|
||
|
||
// 执行部署脚本
|
||
exec(`bash ${DEPLOY_SCRIPT}`, (error, stdout, stderr) => {
|
||
if (error) {
|
||
console.error('部署失败:', error);
|
||
console.error('stderr:', stderr);
|
||
} else {
|
||
console.log('部署成功:', stdout);
|
||
}
|
||
});
|
||
|
||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||
res.end('Deployment triggered');
|
||
} else {
|
||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||
res.end('Not main branch, ignored');
|
||
}
|
||
} catch (error) {
|
||
console.error('处理 webhook 失败:', error);
|
||
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
||
res.end('Internal Server Error');
|
||
}
|
||
});
|
||
});
|
||
|
||
server.listen(PORT, () => {
|
||
console.log(`Webhook 处理器运行在端口 ${PORT}`);
|
||
});
|
||
|
||
// 优雅关闭
|
||
process.on('SIGTERM', () => {
|
||
console.log('收到 SIGTERM,正在关闭服务器...');
|
||
server.close(() => {
|
||
console.log('服务器已关闭');
|
||
process.exit(0);
|
||
});
|
||
}); |