1- import { db , eq , user , account , sql , and } from '@databuddy/db' ;
2- import { NextRequest , NextResponse } from 'next/server' ;
3- import { randomUUID } from 'crypto' ;
1+ import { randomUUID } from 'node:crypto' ;
2+ import { account , and , db , eq , user } from '@databuddy/db' ;
3+ import { type NextRequest , NextResponse } from 'next/server' ;
4+
5+ interface VercelTokenResponse {
6+ access_token : string ;
7+ token_type : string ;
8+ installation_id : string ;
9+ }
10+
11+ interface VercelUserInfo {
12+ id : string ;
13+ email : string ;
14+ name ?: string ;
15+ avatar ?: string ;
16+ }
417
518export async function GET ( request : NextRequest ) {
619 try {
@@ -40,7 +53,7 @@ export async function GET(request: NextRequest) {
4053 ) ;
4154 }
4255
43- const tokens = await tokenResponse . json ( ) ;
56+ const tokens : VercelTokenResponse = await tokenResponse . json ( ) ;
4457
4558 const userResponse = await fetch ( 'https://api.vercel.com/v2/user' , {
4659 headers : {
@@ -55,9 +68,9 @@ export async function GET(request: NextRequest) {
5568 }
5669
5770 const userResponse_json = await userResponse . json ( ) ;
58- const userInfo = userResponse_json . user ;
71+ const userInfo : VercelUserInfo = userResponse_json . user ;
5972
60- if ( ! userInfo . email || ! userInfo . id ) {
73+ if ( ! ( userInfo . email && userInfo . id ) ) {
6174 return NextResponse . redirect (
6275 `${ process . env . BETTER_AUTH_URL } /auth/error?error=invalid_user_info`
6376 ) ;
@@ -67,82 +80,58 @@ export async function GET(request: NextRequest) {
6780 where : eq ( user . email , userInfo . email ) ,
6881 } ) ;
6982
70- let userId : string ;
83+ if ( ! existingUser ) {
84+ // Create the callback URL that will complete the integration after auth
85+ const callbackUrl = new URL ( request . url ) ;
86+ const completeIntegrationUrl = `${ process . env . BETTER_AUTH_URL } ${ callbackUrl . pathname } ${ callbackUrl . search } ` ;
7187
72- if ( existingUser ) {
73- userId = existingUser . id ;
74- } else {
75- const newUserId = randomUUID ( ) ;
76- const now = new Date ( ) . toISOString ( ) ;
77-
78- await db . execute ( sql `
79- INSERT INTO "user" (
80- id, name, email, email_verified, image, created_at, updated_at
81- ) VALUES (
82- ${ newUserId } ,
83- ${ userInfo . name || userInfo . email } ,
84- ${ userInfo . email } ,
85- ${ true } ,
86- ${ userInfo . avatar || null } ,
87- ${ now } ,
88- ${ now }
89- )
90- ` ) ;
91-
92- userId = newUserId ;
88+ return NextResponse . redirect (
89+ `${ process . env . BETTER_AUTH_URL } /register?email=${ encodeURIComponent ( userInfo . email ) } &name=${ encodeURIComponent ( userInfo . name || '' ) } &callback=${ encodeURIComponent ( completeIntegrationUrl ) } `
90+ ) ;
9391 }
9492
93+ const userId = existingUser . id ;
94+
9595 const existingAccount = await db . query . account . findFirst ( {
9696 where : and ( eq ( account . userId , userId ) , eq ( account . providerId , 'vercel' ) ) ,
9797 } ) ;
9898
99- const now = new Date ( ) ;
100- const accountData = {
101- id : existingAccount ?. id || randomUUID ( ) ,
102- accountId : userInfo . id ,
103- providerId : 'vercel' ,
104- userId : userId ,
105- accessToken : tokens . access_token ,
106- scope : JSON . stringify ( {
107- configurationId,
108- teamId,
109- installationId : tokens . installation_id ,
110- tokenType : tokens . token_type ,
111- } ) ,
112- createdAt : existingAccount ?. createdAt || now . toISOString ( ) ,
113- updatedAt : now . toISOString ( ) ,
114- } ;
99+ const now = new Date ( ) . toISOString ( ) ;
100+ const scopeData = JSON . stringify ( {
101+ configurationId,
102+ teamId,
103+ installationId : tokens . installation_id ,
104+ tokenType : tokens . token_type ,
105+ } ) ;
115106
116107 if ( existingAccount ) {
117- await db . execute ( sql `
118- UPDATE "account" SET
119- access_token = ${ accountData . accessToken } ,
120- scope = ${ accountData . scope } ,
121- updated_at = ${ accountData . updatedAt }
122- WHERE id = ${ existingAccount . id }
123- ` ) ;
108+ await db
109+ . update ( account )
110+ . set ( {
111+ accessToken : tokens . access_token ,
112+ scope : scopeData ,
113+ updatedAt : now ,
114+ } )
115+ . where ( eq ( account . id , existingAccount . id ) ) ;
124116 } else {
125- await db . execute ( sql `
126- INSERT INTO "account" (
127- id, account_id, provider_id, user_id, access_token, scope, created_at, updated_at
128- ) VALUES (
129- ${ accountData . id } ,
130- ${ accountData . accountId } ,
131- ${ accountData . providerId } ,
132- ${ accountData . userId } ,
133- ${ accountData . accessToken } ,
134- ${ accountData . scope } ,
135- ${ accountData . createdAt } ,
136- ${ accountData . updatedAt }
137- )
138- ` ) ;
117+ await db . insert ( account ) . values ( {
118+ id : randomUUID ( ) ,
119+ accountId : userInfo . id ,
120+ providerId : 'vercel' ,
121+ userId,
122+ accessToken : tokens . access_token ,
123+ scope : scopeData ,
124+ createdAt : now ,
125+ updatedAt : now ,
126+ } ) ;
139127 }
140128
141129 const redirectUrl =
142130 next || `${ process . env . BETTER_AUTH_URL } /dashboard?vercel_integrated=true` ;
143131
144132 return NextResponse . redirect ( redirectUrl ) ;
145133 } catch ( error ) {
134+ console . error ( 'Vercel OAuth callback error:' , error ) ;
146135 return NextResponse . redirect (
147136 `${ process . env . BETTER_AUTH_URL } /auth/error?error=internal_error`
148137 ) ;
0 commit comments