mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-07 22:38:02 +03:00
feat: ugh full github bot rewrite with octokit
This commit is contained in:
@@ -10,10 +10,10 @@ function ParseEnvVars() {
|
||||
{
|
||||
APP_ID: "string",
|
||||
WEBHOOK_SECRET: "string",
|
||||
BASE64_PRIVATE_KEY: "string",
|
||||
PORT: (self) =>
|
||||
p.isPositiveInteger(Number(self)) === true ||
|
||||
"Should be a string representing a whole integer port.",
|
||||
CLIENT_SECRET: "string",
|
||||
},
|
||||
{},
|
||||
{ allowExcessKeys: true }
|
||||
@@ -27,7 +27,7 @@ function ParseEnvVars() {
|
||||
appId: process.env.APP_ID!,
|
||||
webhookSecret: process.env.WEBHOOK_SECRET!,
|
||||
port: process.env.PORT!,
|
||||
clientSecret: process.env.CLIENT_SECRET!,
|
||||
privateKey: Buffer.from(process.env.BASE64_PRIVATE_KEY!, "base64").toString("utf-8"),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+30
-107
@@ -1,36 +1,17 @@
|
||||
/* eslint-disable no-console */
|
||||
import { ProcessEnv } from "./config";
|
||||
import bodyParser from "body-parser";
|
||||
import { App, createNodeMiddleware } from "@octokit/app";
|
||||
import express from "express";
|
||||
import fetch from "node-fetch";
|
||||
import crypto from "crypto";
|
||||
import { URLSearchParams } from "url";
|
||||
import type { EmitterWebhookEvent } from "@octokit/webhooks";
|
||||
import type { Express } from "express";
|
||||
|
||||
export const app: Express = express();
|
||||
|
||||
// Let NGINX work its magic.
|
||||
app.set("trust proxy", "loopback");
|
||||
|
||||
// Disable query string nesting such as ?a[b]=4 -> {a: {b: 4}}. This
|
||||
// almost always results in a painful security vuln.
|
||||
app.set("query parser", "simple");
|
||||
|
||||
/**
|
||||
* Return the status of this bot and the version it's running.
|
||||
*
|
||||
* @name GET /
|
||||
*/
|
||||
app.get("/", (req, res) =>
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
description: "Github Bot is online!",
|
||||
body: {
|
||||
time: Date.now(),
|
||||
},
|
||||
})
|
||||
);
|
||||
const app = new App({
|
||||
appId: ProcessEnv.appId,
|
||||
privateKey: ProcessEnv.privateKey,
|
||||
webhooks: {
|
||||
secret: ProcessEnv.webhookSecret,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Create a response that contains a link to the seeds diff viewer.
|
||||
@@ -55,94 +36,36 @@ function ConvertGitHubURL(url: string) {
|
||||
return url.replace("https://github.com/", "GitHub:");
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for GitHub webhook calls.
|
||||
*
|
||||
* @note THIS ENDPOINT DOES NOT PARSE REQ BODY INTO AN OBJECT!
|
||||
* it instead leaves it as a raw string, which is necessary because GitHub have
|
||||
* an insane secret checking process.
|
||||
*
|
||||
* @name POST /webhook
|
||||
*/
|
||||
app.post("/webhook", bodyParser.text({ type: "*/*" }), async (req, res) => {
|
||||
const hash = crypto
|
||||
.createHmac("SHA256", ProcessEnv.webhookSecret)
|
||||
.update(req.body as string)
|
||||
.digest("hex");
|
||||
app.webhooks.on(
|
||||
["pull_request.opened", "pull_request.edited"],
|
||||
async ({ octokit, payload: body }) => {
|
||||
const filesChanged = (await fetch(
|
||||
`https://api.github.com/repos/TNG-dev/Tachi/pulls/${body.number}/files`
|
||||
).then((r) => r.json())) as Array<{ filename: string }>;
|
||||
|
||||
if (`sha256=${hash}` !== req.header("X-Hub-Signature-256")) {
|
||||
console.log(
|
||||
`Signatures didn't match. ours="sha256=${hash}" theirs=${req.header(
|
||||
"X-Hub-Signature-256"
|
||||
)}`
|
||||
);
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
description: `Invalid Signature.`,
|
||||
});
|
||||
}
|
||||
|
||||
const event = req.header("X-GitHub-Event");
|
||||
|
||||
if (event !== "pull_request") {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
description: `Unsupported Event.`,
|
||||
});
|
||||
}
|
||||
|
||||
const body = JSON.parse(req.body as string) as EmitterWebhookEvent<"pull_request">["payload"];
|
||||
|
||||
if (body.action !== "opened" && body.action !== "edited") {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
description: `We only care about opened/edited pull requests!`,
|
||||
});
|
||||
}
|
||||
|
||||
const filesChanged = (await fetch(
|
||||
`https://api.github.com/repos/TNG-dev/Tachi/pulls/${body.number}/files`
|
||||
).then((r) => r.json())) as Array<{ filename: string }>;
|
||||
|
||||
// if any file modified in this pr is a collection
|
||||
if (filesChanged.some((k) => k.filename.startsWith("database-seeds/collections"))) {
|
||||
// post a link to the diff viewer in the PR comments.
|
||||
await fetch(body.pull_request._links.comments.href, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${ProcessEnv.clientSecret}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
// if any file modified in this pr is a collection
|
||||
if (filesChanged.some((k) => k.filename.startsWith("database-seeds/collections"))) {
|
||||
// post a link to the diff viewer in the PR comments.
|
||||
await octokit.request("POST /repos/{owner}/{repo}/issues/{issue_number}/comments", {
|
||||
owner: body.repository.owner.login,
|
||||
repo: body.repository.name,
|
||||
issue_number: body.pull_request.number,
|
||||
body: mkSeedDiffViewMsg(
|
||||
body.pull_request.head.repo.url,
|
||||
body.pull_request.head.sha,
|
||||
body.pull_request.base.repo.url,
|
||||
body.pull_request.base.sha
|
||||
),
|
||||
}),
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
description: "Handled request.",
|
||||
body: {},
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 404 Handler. If something gets to this point, they haven't matched with anything.
|
||||
*
|
||||
* @name ALL *
|
||||
*/
|
||||
app.all("*", (req, res) =>
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
description: "Nothing found here.",
|
||||
})
|
||||
);
|
||||
|
||||
console.log(`Starting express server on port ${ProcessEnv.port}.`);
|
||||
const serverMiddleware = createNodeMiddleware(app);
|
||||
|
||||
app.listen(ProcessEnv.port);
|
||||
const expressApp = express();
|
||||
|
||||
expressApp.use(serverMiddleware);
|
||||
|
||||
console.log(`Listening on port ${ProcessEnv.port}.`);
|
||||
expressApp.listen(ProcessEnv.port);
|
||||
|
||||
Reference in New Issue
Block a user