bookwiz.io / app / auth / github / integration-callback / route.ts
route.ts
Raw
import { NextRequest, NextResponse } from 'next/server'

export async function GET(request: NextRequest) {
  const searchParams = request.nextUrl.searchParams
  const code = searchParams.get('code')
  const state = searchParams.get('state')
  const error = searchParams.get('error')

  // Handle OAuth error
  if (error) {
    const errorUrl = new URL('/dashboard', process.env.NEXT_PUBLIC_BASE_URL!)
    errorUrl.searchParams.set('error', `GitHub OAuth error: ${error}`)
    return NextResponse.redirect(errorUrl.toString())
  }

  if (!code || !state) {
    const errorUrl = new URL('/dashboard', process.env.NEXT_PUBLIC_BASE_URL!)
    errorUrl.searchParams.set('error', 'Missing authorization code or state')
    return NextResponse.redirect(errorUrl.toString())
  }

  try {
    // Decode state to get bookId and redirectUrl
    const decodedState = JSON.parse(Buffer.from(state, 'base64').toString())
    const { bookId, redirectUrl } = decodedState

    // Exchange code for access token using repository management app
    const tokenResponse = await fetch('https://github.com/login/oauth/access_token', {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        client_id: process.env.GITHUB_REPO_CLIENT_ID,
        client_secret: process.env.GITHUB_REPO_CLIENT_SECRET,
        code,
      }),
    })

    if (!tokenResponse.ok) {
      throw new Error('Failed to exchange code for token')
    }

    const tokenData = await tokenResponse.json()
    
    if (tokenData.error) {
      throw new Error(`GitHub OAuth error: ${tokenData.error}`)
    }

    const accessToken = tokenData.access_token

    // Get user info from GitHub
    const userResponse = await fetch('https://api.github.com/user', {
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'Accept': 'application/vnd.github.v3+json',
      },
    })

    if (!userResponse.ok) {
      throw new Error('Failed to fetch user info from GitHub')
    }

    const githubUser = await userResponse.json()

    // Get user's repositories
    const reposResponse = await fetch('https://api.github.com/user/repos?type=all&sort=updated&per_page=100', {
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'Accept': 'application/vnd.github.v3+json',
      },
    })

    if (!reposResponse.ok) {
      throw new Error('Failed to fetch repositories from GitHub')
    }

    const repositories = await reposResponse.json()

    // Store the OAuth data and redirect to repository selection
    const oauthData = {
      bookId,
      accessToken,
      githubUsername: githubUser.login,
      redirectUrl,
      repositories: repositories.slice(0, 50).map((repo: any) => ({
        id: repo.id,
        name: repo.name,
        full_name: repo.full_name,
        private: repo.private,
        updated_at: repo.updated_at,
        description: repo.description
      }))
    }

    // Redirect to repository selection page
    const handlerUrl = new URL('/auth/github/complete', process.env.NEXT_PUBLIC_BASE_URL!)
    handlerUrl.searchParams.set('data', Buffer.from(JSON.stringify(oauthData)).toString('base64'))

    return NextResponse.redirect(handlerUrl.toString())

  } catch (error) {
    const errorUrl = new URL('/dashboard', process.env.NEXT_PUBLIC_BASE_URL!)
    errorUrl.searchParams.set('error', error instanceof Error ? error.message : 'GitHub OAuth failed')
    return NextResponse.redirect(errorUrl.toString())
  }
}