Files
gitops-automation/agent/src/server.js
william 357791d6e3
build-agent / build-and-push (push) Failing after 18s
Scaffold Matrix + GitOps + Claude automation stack
2026-08-23 10:25:57 +00:00

67 lines
1.9 KiB
JavaScript

import express from "express";
import crypto from "node:crypto";
import { postPRComment } from "./gitea.js";
import { reviewPullRequest } from "./runner.js";
import { startMatrixBot } from "./matrixBot.js";
const app = express();
app.use(
express.json({
verify: (req, _res, buf) => {
req.rawBody = buf;
},
})
);
const PORT = process.env.PORT || 3001;
const WEBHOOK_SECRET = process.env.GITEA_WEBHOOK_SECRET;
function verifySignature(req) {
if (!WEBHOOK_SECRET) return false;
const sig = req.get("X-Gitea-Signature");
if (!sig) return false;
const expected = crypto.createHmac("sha256", WEBHOOK_SECRET).update(req.rawBody).digest("hex");
const sigBuf = Buffer.from(sig, "hex");
const expBuf = Buffer.from(expected, "hex");
return sigBuf.length === expBuf.length && crypto.timingSafeEqual(sigBuf, expBuf);
}
app.get("/healthz", (_req, res) => res.send("ok"));
app.post("/webhooks/gitea", async (req, res) => {
if (!verifySignature(req)) {
return res.status(401).send("bad signature");
}
// Ack immediately — Gitea has a short webhook timeout and the review itself takes a while.
res.status(202).send("accepted");
const event = req.get("X-Gitea-Event");
const body = req.body;
try {
if (event === "pull_request" && ["opened", "synchronize"].includes(body.action)) {
const { repository, pull_request: pr } = body;
const [owner, repo] = repository.full_name.split("/");
const review = await reviewPullRequest({
owner,
repo,
ref: pr.head.sha,
cloneUrl: repository.clone_url,
prTitle: pr.title,
prBody: pr.body || "",
});
await postPRComment(owner, repo, pr.number, review);
}
} catch (err) {
console.error("webhook handling failed:", err);
}
});
app.listen(PORT, () => {
console.log(`claude-agent listening on :${PORT}`);
});
startMatrixBot().catch((err) => {
console.error("matrix bot failed to start:", err);
});