#!/usr/bin/env node // webhook-server.js — nhận webhook từ Gogs và trigger deploy // Run: node webhook-server.js [port] // // Gogs webhook URL: http://:9000/webhook const http = require('http') const PORT = process.env['WEBHOOK_PORT'] || 9000 const DEPLOY_SCRIPT = process.env['DEPLOY_SCRIPT'] || '/opt/timelapse/scripts/deploy.sh' const WEBHOOK_SECRET = process.env['WEBHOOK_SECRET'] || '' function verifySignature(body, signature) { if (!WEBHOOK_SECRET) return true const crypto = require('crypto') const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET) hmac.update(body) const expected = 'sha256=' + hmac.digest('hex') return crypto.timingSafeEqual(Buffer.from(signature || ''), Buffer.from(expected)) } const server = http.createServer((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.toString() }) req.on('end', () => { const signature = req.headers['x-gogs-signature'] || req.headers['x-hub-signature-256'] || '' if (!verifySignature(body, signature)) { res.writeHead(401) res.end('Unauthorized') return } let payload try { payload = JSON.parse(body) } catch { payload = {} } const branch = payload.ref ? payload.ref.replace('refs/heads/', '') : 'main' console.log(`[${new Date().toISOString()}] Webhook received — branch: ${branch}`) res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ ok: true, branch })) // Trigger deploy async const { spawn } = require('child_process') const deploy = spawn('bash', [DEPLOY_SCRIPT, branch], { cwd: '/opt/timelapse', env: { ...process.env, DEPLOY_BRANCH: branch }, }) deploy.stdout.on('data', d => process.stdout.write(d)) deploy.stderr.on('data', d => process.stderr.write(d)) deploy.on('exit', code => { console.log(`[${new Date().toISOString()}] Deploy exited with code ${code}`) }) }) }) server.listen(PORT, () => { console.log(`Webhook server listening on http://0.0.0.0:${PORT}/webhook`) })