Skip to content

Commit

Permalink
feat(server): add github webhook api route
Browse files Browse the repository at this point in the history
  • Loading branch information
Miodec committed Aug 17, 2023
1 parent 4e81114 commit 7f7d333
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 1 deletion.
15 changes: 15 additions & 0 deletions backend/src/api/controllers/webhooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { MonkeyResponse } from "../../utils/monkey-response";
import GeorgeQueue from "../../queues/george-queue";

export async function githubRelease(
req: MonkeyTypes.Request
): Promise<MonkeyResponse> {
const action = req.body.action;

if (action === "published") {
const releaseName = req.body.release.name;
await GeorgeQueue.sendReleaseAnnouncement(releaseName);
return new MonkeyResponse("Added release announcement task to queue");
}
return new MonkeyResponse("No action taken");
}
2 changes: 2 additions & 0 deletions backend/src/api/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import results from "./results";
import presets from "./presets";
import apeKeys from "./ape-keys";
import admin from "./admin";
import webhooks from "./webhooks";
import configuration from "./configuration";
import { version } from "../../version";
import leaderboards from "./leaderboards";
Expand Down Expand Up @@ -39,6 +40,7 @@ const API_ROUTE_MAP = {
"/quotes": quotes,
"/ape-keys": apeKeys,
"/admin": admin,
"/webhooks": webhooks,
};

function addApiRoutes(app: Application): void {
Expand Down
17 changes: 17 additions & 0 deletions backend/src/api/routes/webhooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// import joi from "joi";
import { Router } from "express";
import { authenticateGithubWebhook } from "../../middlewares/auth";
import { asyncHandler } from "../../middlewares/api-utils";
import { webhookLimit } from "../../middlewares/rate-limit";
import { githubRelease } from "../controllers/webhooks";

const router = Router();

router.post(
"/githubRelease",
webhookLimit,
authenticateGithubWebhook(),
asyncHandler(githubRelease)
);

export default router;
47 changes: 46 additions & 1 deletion backend/src/middlewares/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
recordRequestCountry,
// recordRequestForUid,
} from "../utils/prometheus";
import crypto from "crypto";
import { performance } from "perf_hooks";

interface RequestAuthenticationOptions {
Expand Down Expand Up @@ -256,4 +257,48 @@ async function authenticateWithApeKey(
}
}

export { authenticateRequest };
function authenticateGithubWebhook(): Handler {
return async (
req: MonkeyTypes.Request,
_res: Response,
next: NextFunction
): Promise<void> => {
//authorize github webhook
const { "x-hub-signature-256": authHeader } = req.headers;

const webhookSecret = process.env.GITHUB_WEBHOOK_SECRET;

console.log(webhookSecret);
console.log(authHeader);
console.log(JSON.stringify(req.body));

try {
if (!webhookSecret) {
throw new MonkeyError(500, "Missing Github Webhook Secret");
} else if (!authHeader) {
throw new MonkeyError(401, "Missing Github signature header");
} else {
const signature = crypto
.createHmac("sha256", webhookSecret)
.update(JSON.stringify(req.body))
.digest("hex");
const trusted = Buffer.from(`sha256=${signature}`, "ascii");
const untrusted = Buffer.from(authHeader as string, "ascii");
const isSignatureValid = crypto.timingSafeEqual(trusted, untrusted);

console.log(trusted.toString());
console.log(untrusted.toString());

if (!isSignatureValid) {
throw new MonkeyError(401, "Github webhook signature invalid");
}
}
} catch (e) {
return next(e);
}

next();
};
}

export { authenticateRequest, authenticateGithubWebhook };
7 changes: 7 additions & 0 deletions backend/src/middlewares/rate-limit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,13 @@ export const apeKeysGenerate = rateLimit({
handler: customHandler,
});

export const webhookLimit = rateLimit({
windowMs: 1000,
max: 1 * REQUEST_MULTIPLIER,
keyGenerator: getKeyWithUid,
handler: customHandler,
});

export const apeKeysUpdate = apeKeysGenerate;

export const apeKeysDelete = apeKeysGenerate;
8 changes: 8 additions & 0 deletions backend/src/queues/george-queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ function buildGeorgeTask(taskName: string, taskArgs: any[]): GeorgeTask {
}

class GeorgeQueue extends MonkeyQueue<GeorgeTask> {
async sendReleaseAnnouncement(releaseName: string): Promise<void> {
const taskName = "sendReleaseAnnouncement";
const sendReleaseAnnouncementTask = buildGeorgeTask(taskName, [
releaseName,
]);
await this.add(taskName, sendReleaseAnnouncementTask);
}

async updateDiscordRole(discordId: string, wpm: number): Promise<void> {
const taskName = "updateRole";
const updateDiscordRoleTask = buildGeorgeTask(taskName, [discordId, wpm]);
Expand Down

0 comments on commit 7f7d333

Please sign in to comment.