Merge pull request #189 from slowlydev/feat/enable-autodeploy-github

Add web hook verification to GitHub Autodeploy
This commit is contained in:
Mauricio Siu
2024-07-02 18:02:06 -06:00
committed by GitHub
8 changed files with 10269 additions and 6344 deletions

View File

@@ -0,0 +1 @@
ALTER TABLE "admin" ADD COLUMN "githubWebhookSecret" text;

File diff suppressed because it is too large Load Diff

View File

@@ -127,6 +127,13 @@
"when": 1719547174326, "when": 1719547174326,
"tag": "0017_minor_post", "tag": "0017_minor_post",
"breakpoints": true "breakpoints": true
},
{
"idx": 18,
"version": "6",
"when": 1719928377858,
"tag": "0018_careful_killmonger",
"breakpoints": true
} }
] ]
} }

View File

@@ -36,10 +36,12 @@
"@codemirror/lang-yaml": "^6.1.1", "@codemirror/lang-yaml": "^6.1.1",
"@codemirror/language": "^6.10.1", "@codemirror/language": "^6.10.1",
"@codemirror/legacy-modes": "6.4.0", "@codemirror/legacy-modes": "6.4.0",
"@dokploy/trpc-openapi": "0.0.4",
"@faker-js/faker": "^8.4.1", "@faker-js/faker": "^8.4.1",
"@hookform/resolvers": "^3.3.4", "@hookform/resolvers": "^3.3.4",
"@lucia-auth/adapter-drizzle": "1.0.7", "@lucia-auth/adapter-drizzle": "1.0.7",
"@octokit/auth-app": "^6.0.4", "@octokit/auth-app": "^6.0.4",
"@octokit/webhooks": "^13.2.7",
"@radix-ui/react-accordion": "1.1.2", "@radix-ui/react-accordion": "1.1.2",
"@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-avatar": "^1.0.4",
@@ -113,7 +115,6 @@
"tailwind-merge": "^2.2.0", "tailwind-merge": "^2.2.0",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"tar-fs": "3.0.5", "tar-fs": "3.0.5",
"@dokploy/trpc-openapi": "0.0.4",
"use-resize-observer": "9.1.0", "use-resize-observer": "9.1.0",
"ws": "8.16.0", "ws": "8.16.0",
"xterm-addon-fit": "^0.8.0", "xterm-addon-fit": "^0.8.0",

View File

@@ -1,17 +1,39 @@
import { and, eq } from "drizzle-orm"; import { and, eq } from "drizzle-orm";
import { db } from "@/server/db"; import { db } from "@/server/db";
import { Webhooks } from "@octokit/webhooks";
import type { NextApiRequest, NextApiResponse } from "next"; import type { NextApiRequest, NextApiResponse } from "next";
import { applications, compose } from "@/server/db/schema"; import { applications, compose } from "@/server/db/schema";
import { extractCommitMessage, extractHash } from "./[refreshToken]"; import { extractCommitMessage, extractHash } from "./[refreshToken]";
import type { DeploymentJob } from "@/server/queues/deployments-queue"; import type { DeploymentJob } from "@/server/queues/deployments-queue";
import { myQueue } from "@/server/queues/queueSetup"; import { myQueue } from "@/server/queues/queueSetup";
export default async function handler( export default async function handler(req: NextApiRequest, res: NextApiResponse) {
req: NextApiRequest, const admin = await db.query.admins.findFirst({});
res: NextApiResponse,
) { if (!admin) {
res.status(200).json({ message: "Could not find admin" });
return;
}
if (!admin.githubWebhookSecret) {
res.status(200).json({ message: "Github Webhook Secret not set" });
return;
}
const webhooks = new Webhooks({
secret: admin.githubWebhookSecret,
});
const signature = req.headers["x-hub-signature-256"];
const github = req.body; const github = req.body;
const verified = await webhooks.verify(JSON.stringify(github), signature as string);
if (!verified) {
res.status(401).json({ message: "Unauthorized" });
return;
}
if (req.headers["x-github-event"] === "ping") { if (req.headers["x-github-event"] === "ping") {
res.status(200).json({ message: "Ping received, webhook is active" }); res.status(200).json({ message: "Ping received, webhook is active" });
return; return;
@@ -33,7 +55,7 @@ export default async function handler(
eq(applications.sourceType, "github"), eq(applications.sourceType, "github"),
eq(applications.autoDeploy, true), eq(applications.autoDeploy, true),
eq(applications.branch, branchName), eq(applications.branch, branchName),
eq(applications.repository, repository), eq(applications.repository, repository)
), ),
}); });
@@ -51,17 +73,12 @@ export default async function handler(
{ {
removeOnComplete: true, removeOnComplete: true,
removeOnFail: true, removeOnFail: true,
}, }
); );
} }
const composeApps = await db.query.compose.findMany({ const composeApps = await db.query.compose.findMany({
where: and( where: and(eq(compose.sourceType, "github"), eq(compose.autoDeploy, true), eq(compose.branch, branchName), eq(compose.repository, repository)),
eq(compose.sourceType, "github"),
eq(compose.autoDeploy, true),
eq(compose.branch, branchName),
eq(compose.repository, repository),
),
}); });
for (const composeApp of composeApps) { for (const composeApp of composeApps) {
@@ -79,7 +96,7 @@ export default async function handler(
{ {
removeOnComplete: true, removeOnComplete: true,
removeOnFail: true, removeOnFail: true,
}, }
); );
} }

View File

@@ -38,6 +38,7 @@ export default async function handler(
githubAppName: data.name, githubAppName: data.name,
githubClientId: data.client_id, githubClientId: data.client_id,
githubClientSecret: data.client_secret, githubClientSecret: data.client_secret,
githubWebhookSecret: data.webhook_secret,
githubPrivateKey: data.pem, githubPrivateKey: data.pem,
}) })
.where(eq(admins.authId, authId as string)) .where(eq(admins.authId, authId as string))

13925
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,7 @@ export const admins = pgTable("admin", {
githubClientSecret: text("githubClientSecret"), githubClientSecret: text("githubClientSecret"),
githubInstallationId: text("githubInstallationId"), githubInstallationId: text("githubInstallationId"),
githubPrivateKey: text("githubPrivateKey"), githubPrivateKey: text("githubPrivateKey"),
githubWebhookSecret: text("githubWebhookSecret"),
letsEncryptEmail: text("letsEncryptEmail"), letsEncryptEmail: text("letsEncryptEmail"),
sshPrivateKey: text("sshPrivateKey"), sshPrivateKey: text("sshPrivateKey"),
enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(false), enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(false),