From c06b5722e34d6a0bc90c2631dda4022393b4c7d8 Mon Sep 17 00:00:00 2001 From: Igor Zalutski Date: Mon, 19 Aug 2024 21:58:53 +0100 Subject: [PATCH] Add github callback route to proxy backend --- package.json | 1 + pnpm-lock.yaml | 16 ++++ .../github_app/error/page.tsx | 11 +++ .../github_app/success/page.tsx | 10 +++ src/app/api/github-callback/route.ts | 81 +++++++++++++++++++ 5 files changed, 119 insertions(+) create mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/github_app/error/page.tsx create mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/github_app/success/page.tsx create mode 100644 src/app/api/github-callback/route.ts diff --git a/package.json b/package.json index 531f10e0..5a1615f6 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,7 @@ "@unkey/nextjs": "^0.15.0", "@vercel/analytics": "^1.0.1", "ai": "^3.1.12", + "async-retry": "^1.3.3", "autoprefixer": "^10.4.13", "axios": "^1.2.1", "checkbox": "^0.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f008181b..32eff637 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -275,6 +275,9 @@ importers: ai: specifier: ^3.1.12 version: 3.2.24(react@18.3.1)(svelte@4.2.18)(vue@3.4.31(typescript@5.5.3))(zod@3.23.8) + async-retry: + specifier: ^1.3.3 + version: 1.3.3 autoprefixer: specifier: ^10.4.13 version: 10.4.19(postcss@8.4.39) @@ -4290,6 +4293,9 @@ packages: async-limiter@1.0.1: resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} + async-retry@1.3.3: + resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} + asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -8248,6 +8254,10 @@ packages: resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -13742,6 +13752,10 @@ snapshots: async-limiter@1.0.1: {} + async-retry@1.3.3: + dependencies: + retry: 0.13.1 + asynckit@0.4.0: {} atob@2.1.2: {} @@ -18833,6 +18847,8 @@ snapshots: onetime: 5.1.2 signal-exit: 3.0.7 + retry@0.13.1: {} + reusify@1.0.4: {} rgb-regex@1.0.1: {} diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/github_app/error/page.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/github_app/error/page.tsx new file mode 100644 index 00000000..063c3a9f --- /dev/null +++ b/src/app/(dynamic-pages)/(authenticated-pages)/github_app/error/page.tsx @@ -0,0 +1,11 @@ +import Link from "next/link"; + +export default function GithubAppError() { + const appSlug = process.env.NEXT_PUBLIC_GITHUB_APP_SLUG; + return ( + <> +

Something went wrong

+

GitHub App installation failed. Maybe try re-install it?

+ + ) +} \ No newline at end of file diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/github_app/success/page.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/github_app/success/page.tsx new file mode 100644 index 00000000..b24c9b38 --- /dev/null +++ b/src/app/(dynamic-pages)/(authenticated-pages)/github_app/success/page.tsx @@ -0,0 +1,10 @@ +import Link from "next/link"; + +export default function GithubAppError() { + return ( + <> +

Something went wrong

+

GitHub App installation failed. You can now close this tab or go to dashboard

+ + ) +} \ No newline at end of file diff --git a/src/app/api/github-callback/route.ts b/src/app/api/github-callback/route.ts new file mode 100644 index 00000000..5d0e4114 --- /dev/null +++ b/src/app/api/github-callback/route.ts @@ -0,0 +1,81 @@ +import { createSupabaseUserRouteHandlerClient } from '@/supabase-clients/user/createSupabaseUserRouteHandlerClient'; +import { toSiteURL } from '@/utils/helpers'; +import retry from 'async-retry'; +import { NextRequest, NextResponse } from 'next/server'; + +// Use the environment variable for the callback URL +const AUTH_SERVICE_URL = process.env.GITHUB_CALLBACK_URL; +const DIGGER_WEBHOOK_SECRET = process.env.DIGGER_WEBHOOK_SECRET; + +if (!AUTH_SERVICE_URL) { + throw new Error('GITHUB_CALLBACK_URL environment variable is not set'); +} + +export async function GET(request: NextRequest) { + const searchParams = request.nextUrl.searchParams; + const installationId = searchParams.get('installation_id'); + + if (!installationId) { + return NextResponse.json( + { error: 'Missing installation_id' }, + { status: 400 }, + ); + } + + try { + console.log( + 'Trying to get org id for the following installation ID:', + installationId, + ); + const organizationId = await getOrganizationId(installationId); + const response = await fetch( + `${AUTH_SERVICE_URL}?${searchParams.toString()}`, + { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'X-Digger-Org-ID': organizationId, + Authorization: `Bearer ${DIGGER_WEBHOOK_SECRET}`, + }, + }, + ); + + if (!response.ok) { + throw new Error(`Auth service responded with status: ${response.status}`); + } + return NextResponse.redirect(toSiteURL('/github_app/success')); + } catch (error) { + console.error('Error handling GitHub App installation callback:', error); + return NextResponse.redirect(toSiteURL('/github_app/error')); + } +} + +async function getOrganizationId(installationId: string): Promise { + const supabase = createSupabaseUserRouteHandlerClient(); + return retry( + async (bail) => { + const { data, error } = await supabase + .from('github_app_installation_links') + .select('organization_id') + .eq('github_installation_id', installationId) + .single(); + + if (error) { + if (error.code === '404') bail(new Error('Organization not found')); + throw error; + } + + if (!data?.organization_id) { + throw new Error('Organization ID not found'); + } + + return data.organization_id; + }, + { + retries: 2, + onRetry: (error, attempt) => { + console.log(`Attempt ${attempt} failed. Retrying...`); + }, + }, + ); +}