Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: GitHub App Webhook #3

Merged
merged 2 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ SMTP_PORT=1025
SMTP_SECURE_ENABLED=0
SMTP_USER=smtpUser
SMTP_PASSWORD=smtpPassword

# GitHub Webhook Secret
GITHUB_WEBHOOK_SECRET=
8 changes: 5 additions & 3 deletions env.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";
import { createEnv } from "@t3-oss/env-nextjs"
import { z } from "zod"

export const env = createEnv({
server: {
Expand All @@ -17,6 +17,7 @@ export const env = createEnv({
SMTP_USER: z.string().min(1),
SMTP_PASSWORD: z.string().min(1),
SMTP_SECURE_ENABLED: z.enum("0", "1").optional(),
GITHUB_WEBHOOK_SECRET: z.string().min(1).optional(),
},
client: {
NEXT_PUBLIC_APP_URL: z.string().min(1),
Expand All @@ -35,5 +36,6 @@ export const env = createEnv({
SMTP_PASSWORD: process.env.SMTP_PASSWORD,
SMTP_SECURE_ENABLED: process.env.SMTP_SECURE_ENABLED,
NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
GITHUB_WEBHOOK_SECRET: process.env.GITHUB_WEBHOOK_SECRET,
},
});
})
10 changes: 10 additions & 0 deletions github/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const LEVEL_LABEL = "level"

export enum EVENT_TRIGGERS {
ISSUE_OPENED = "issues.opened",
INSTALLATION_CREATED = "installation.created",
}

export const ON_NEW_ISSUE = "Thanks for opening an issue! It's live on oss.gg!"

export const ON_REPO_NOT_REGISTERED = `This repository is not registered with oss.gg. Please register it at https://oss.gg.`
14 changes: 14 additions & 0 deletions github/hooks/installation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Webhooks } from "@octokit/webhooks"

import { EVENT_TRIGGERS } from "../constants"
import { sendInstallationDetails } from "../services/user"

export const onInstallationCreated = async (webhooks: Webhooks) => {
webhooks.on(EVENT_TRIGGERS.INSTALLATION_CREATED, async (context) => {
const installationId = context.payload.installation.id
const appId = context.payload.installation.app_id
const repos = context.payload.repositories

await sendInstallationDetails(installationId, appId, repos)
})
}
38 changes: 38 additions & 0 deletions github/hooks/issue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Webhooks } from "@octokit/webhooks"

import { EVENT_TRIGGERS, LEVEL_LABEL } from "../constants"

export const onIssueOpened = async (webhooks: Webhooks) => {
webhooks.on(EVENT_TRIGGERS.ISSUE_OPENED, async (context) => {
const projectId = context.payload.repository.id

// const isProjectRegistered = await getProject(projectId)
// if (!isProjectRegistered) {
// await context.octokit.issues.createComment(
// context.issue({
// body: ON_REPO_NOT_REGISTERED,
// })
// )
// return
// }

const labels = context.payload.issue.labels?.map((label) => label.name)
const isLevelLabel = labels?.includes(LEVEL_LABEL)

if (!isLevelLabel) {
return
}

// await sendNewIssue(
// context.payload.repository.id,
// context.payload.issue.user.id,
// context.payload.issue.id
// )

// await context.octokit.issues.createComment(
// context.issue({
// body: ON_NEW_ISSUE,
// })
// )
})
}
19 changes: 19 additions & 0 deletions github/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { createNodeMiddleware, Webhooks } from "@octokit/webhooks"

import { env } from "@/env.mjs"

import { onInstallationCreated } from "./hooks/installation"
import { onIssueOpened } from "./hooks/issue"

export const webhooks = new Webhooks({
secret: env.GITHUB_WEBHOOK_SECRET as string,
})

export const webhookMiddleware = createNodeMiddleware(webhooks, {
path: "/api/github-webhook",
})

export const registerListeners = () => {
onIssueOpened(webhooks)
onInstallationCreated(webhooks)
}
38 changes: 38 additions & 0 deletions github/services/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
export const sendInstallationDetails = async (
installationId: number,
appId: number,
repos: unknown
): Promise<unknown> => {
try {
console.log("sendInstallationDetails", installationId, appId, repos)

// const response = await fetch(url, {
// method: "POST",
// headers: {
// "Content-Type": "application/json",
// },
// body: JSON.stringify(postData),
// })

// if (!response.ok) {
// throw new Error(`HTTP error! status: ${response.status}`)
// }

// const data = await response.json()
return { data: "data" }
} catch (error) {
throw new Error(`Failed to post installation details: ${error}`)
}
}

export const getUser = async (userId: string): Promise<unknown> => {
// const url = `${WEB_API}/api/users/${userId}`

try {
// const response = await fetch(url)
// const data = await response.json()
return { data: "data" }
} catch (error) {
throw new Error(`Failed to get user: ${error}`)
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"dependencies": {
"@hookform/resolvers": "^3.3.4",
"@next-auth/prisma-adapter": "^1.0.7",
"@octokit/webhooks": "^12.0.11",
"@prisma/client": "5.8.1",
"@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-avatar": "^1.0.4",
Expand Down
20 changes: 20 additions & 0 deletions pages/api/github-webhook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { NextApiRequest, NextApiResponse } from "next"
import { registerListeners, webhookMiddleware } from "@/github"

import "@/github/hooks/issue"

export const config = {
api: {
bodyParser: false,
},
}

export default function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === "POST") {
registerListeners()
webhookMiddleware(req, res, () => res.status(200).end())
} else {
res.setHeader("Allow", "POST")
res.status(405).end("Method Not Allowed")
}
}
61 changes: 58 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading