const http = require("http"); const crypto = require("crypto"); const { exec } = require("child_process"); const SECRET = process.env.WEBHOOK_SECRET || ""; const PORT = Number(process.env.PORT || process.env.WEBHOOK_PORT || 8787); function sign(body) { const h = crypto.createHmac("sha256", SECRET).update(body).digest("hex"); return "sha256=" + h; } function safeTimingEqual(a, b) { const ba = Buffer.from(a || ""); const bb = Buffer.from(b || ""); if (ba.length !== bb.length) return false; return crypto.timingSafeEqual(ba, bb); } function SAFE(a, b) { try { return safeTimingEqual(a, b); } catch { return false; } } const server = http.createServer((req, res) => { if (req.method !== "POST" || req.url !== "/github") { res.writeHead(404); return res.end("not found"); } let raw = ""; req.on("data", c => raw += c); req.on("end", () => { try { const sig = req.headers["x-hub-signature-256"] || ""; const calc = sign(raw); if (!SAFE(sig, calc)) { res.writeHead(401); return res.end("bad sig"); } const event = req.headers["x-github-event"] || ""; if (event !== "push") { res.writeHead(200); return res.end("ignored"); } exec(`${process.env.HOME}/skynet-deploy.sh`, { timeout: 10 * 60 * 1000 }, (err) => { if (err) { console.error("deploy error", err); res.writeHead(500); return res.end("deploy failed"); } res.writeHead(200); res.end("ok"); }); } catch (e) { console.error(e); res.writeHead(500); res.end("error"); } }); }); server.listen(PORT, "0.0.0.0", () => { console.log("webhook up on", PORT); });