From 4ec999d3a72f22a440c6b19194c330b8b30b63cb Mon Sep 17 00:00:00 2001 From: Fayorg Date: Sun, 24 Mar 2024 03:21:48 +0100 Subject: [PATCH] add: github api sdk --- actions/github/account.ts | 13 +++++++++ actions/github/installation.ts | 37 ++++++++++++++++++++++++++ actions/github/jwt.ts | 8 ++++++ actions/github/repository.ts | 48 ++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+) create mode 100644 actions/github/account.ts create mode 100644 actions/github/installation.ts create mode 100644 actions/github/jwt.ts create mode 100644 actions/github/repository.ts diff --git a/actions/github/account.ts b/actions/github/account.ts new file mode 100644 index 0000000..7b37838 --- /dev/null +++ b/actions/github/account.ts @@ -0,0 +1,13 @@ +"use server"; + +import prisma from "@/lib/prisma"; +import { Account } from "@prisma/client"; + +export async function getGithubAccount(userId: string): Promise { + return prisma.account.findFirst({ + where: { + userId: userId, + provider: "github", + }, + }); +} \ No newline at end of file diff --git a/actions/github/installation.ts b/actions/github/installation.ts new file mode 100644 index 0000000..67f2329 --- /dev/null +++ b/actions/github/installation.ts @@ -0,0 +1,37 @@ +"use server"; + +import { getGithubAccount } from "./account"; +import { generateGithubJWT } from "./jwt"; + +export async function getInstallationId(userId: string, username: string): Promise { + const account = await getGithubAccount(userId); + if(!account) return null; + + const installations = await fetch('https://api.github.com/users/' + username + '/installation', { + headers: { + Authorization: `Bearer ${await generateGithubJWT()}`, + Accept: "application/vnd.github+json", + "X-GitHub-Api-Version": "2022-11-28", + }, + method: 'GET', + }).then(res => res.json()); + + return installations.id as string; + +} + +export async function getInstallationAccessToken(userId: string, username: string) { + const installationId = await getInstallationId(userId, username); + if(!installationId) return null; + + const token = await fetch(`https://api.github.com/app/installations/${installationId}/access_tokens`, { + headers: { + Authorization: `Bearer ${await generateGithubJWT()}`, + Accept: "application/vnd.github+json", + "X-GitHub-Api-Version": "2022-11-28", + }, + method: 'POST', + }).then(res => res.json()); + + return token.token as string; +} \ No newline at end of file diff --git a/actions/github/jwt.ts b/actions/github/jwt.ts new file mode 100644 index 0000000..f17c5d7 --- /dev/null +++ b/actions/github/jwt.ts @@ -0,0 +1,8 @@ +"use server"; + +import { sign } from 'jsonwebtoken'; + +export async function generateGithubJWT() { + const jwt = sign({ }, process.env.GITHUB_APP_PRIVATE_KEY as string, { algorithm: 'RS256', issuer: process.env.GITHUB_APP_ID, expiresIn: 5*60}) + return jwt; +} \ No newline at end of file diff --git a/actions/github/repository.ts b/actions/github/repository.ts new file mode 100644 index 0000000..7b09aef --- /dev/null +++ b/actions/github/repository.ts @@ -0,0 +1,48 @@ +"use server"; + +import { getInstallationAccessToken } from "./installation"; + +export type GithubRepository = { + id: number; + name: string; + full_name: string; + owner: { + login: string; + id: number; + type: string; + }; + private: boolean; + html_url: string; + description: string; + fork: boolean; + url: string; + created_at: string; + updated_at: string; + pushed_at: string; + homepage: string; + size: number; + stargazers_count: number; + watchers_count: number; + language: string; + forks_count: number; + open_issues_count: number; + default_branch: string; + permissions: { + admin: boolean; + push: boolean; + pull: boolean; + }; +}; + +export async function getUserRepository(userId: string, username: string): Promise { + const repositories = await fetch('https://api.github.com/installation/repositories', { + headers: { + Authorization: `Bearer ${await getInstallationAccessToken(userId, username)}`, + Accept: "application/vnd.github+json", + "X-GitHub-Api-Version": "2022-11-28", + }, + method: 'GET', + }).then(res => res.json()); + + return repositories.repositories as GithubRepository[]; +} \ No newline at end of file