From 9970c6987271559725924bb38d1c65d09db7c461 Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Sun, 21 Dec 2025 13:16:52 -0500 Subject: [PATCH] conflict warning --- src/routes/api/auth/callback/github.ts | 23 +++++++-- src/routes/api/auth/callback/google.ts | 23 +++++++-- src/routes/login/index.tsx | 21 ++++++-- src/server/api/routers/auth.ts | 66 ++++++++++++++++++++++---- 4 files changed, 110 insertions(+), 23 deletions(-) diff --git a/src/routes/api/auth/callback/github.ts b/src/routes/api/auth/callback/github.ts index c306e57..b59f43e 100644 --- a/src/routes/api/auth/callback/github.ts +++ b/src/routes/api/auth/callback/github.ts @@ -11,7 +11,7 @@ export async function GET(event: APIEvent) { if (error) { return new Response(null, { status: 302, - headers: { Location: `/login?error=${encodeURIComponent(error)}` }, + headers: { Location: `/login?error=${encodeURIComponent(error)}` } }); } @@ -19,7 +19,7 @@ export async function GET(event: APIEvent) { if (!code) { return new Response(null, { status: 302, - headers: { Location: "/login?error=missing_code" }, + headers: { Location: "/login?error=missing_code" } }); } @@ -35,20 +35,33 @@ export async function GET(event: APIEvent) { // Redirect to account page on success return new Response(null, { status: 302, - headers: { Location: result.redirectTo || "/account" }, + headers: { Location: result.redirectTo || "/account" } }); } else { // Redirect to login with error return new Response(null, { status: 302, - headers: { Location: "/login?error=auth_failed" }, + headers: { Location: "/login?error=auth_failed" } }); } } catch (error) { console.error("GitHub OAuth callback error:", error); + + // Handle specific TRPC errors + if (error && typeof error === "object" && "code" in error) { + const trpcError = error as { code: string; message?: string }; + + if (trpcError.code === "CONFLICT") { + return new Response(null, { + status: 302, + headers: { Location: "/login?error=email_in_use" } + }); + } + } + return new Response(null, { status: 302, - headers: { Location: "/login?error=server_error" }, + headers: { Location: "/login?error=server_error" } }); } } diff --git a/src/routes/api/auth/callback/google.ts b/src/routes/api/auth/callback/google.ts index d99e4c2..d044650 100644 --- a/src/routes/api/auth/callback/google.ts +++ b/src/routes/api/auth/callback/google.ts @@ -11,7 +11,7 @@ export async function GET(event: APIEvent) { if (error) { return new Response(null, { status: 302, - headers: { Location: `/login?error=${encodeURIComponent(error)}` }, + headers: { Location: `/login?error=${encodeURIComponent(error)}` } }); } @@ -19,7 +19,7 @@ export async function GET(event: APIEvent) { if (!code) { return new Response(null, { status: 302, - headers: { Location: "/login?error=missing_code" }, + headers: { Location: "/login?error=missing_code" } }); } @@ -35,20 +35,33 @@ export async function GET(event: APIEvent) { // Redirect to account page on success return new Response(null, { status: 302, - headers: { Location: result.redirectTo || "/account" }, + headers: { Location: result.redirectTo || "/account" } }); } else { // Redirect to login with error return new Response(null, { status: 302, - headers: { Location: "/login?error=auth_failed" }, + headers: { Location: "/login?error=auth_failed" } }); } } catch (error) { console.error("Google OAuth callback error:", error); + + // Handle specific TRPC errors + if (error && typeof error === "object" && "code" in error) { + const trpcError = error as { code: string; message?: string }; + + if (trpcError.code === "CONFLICT") { + return new Response(null, { + status: 302, + headers: { Location: "/login?error=email_in_use" } + }); + } + } + return new Response(null, { status: 302, - headers: { Location: "/login?error=server_error" }, + headers: { Location: "/login?error=server_error" } }); } } diff --git a/src/routes/login/index.tsx b/src/routes/login/index.tsx index e99fb68..952546c 100644 --- a/src/routes/login/index.tsx +++ b/src/routes/login/index.tsx @@ -111,7 +111,9 @@ export default function LoginPage() { server_error: "Server error - please try again later", missing_params: "Invalid login link - missing parameters", link_expired: "Login link has expired - please request a new one", - access_denied: "Access denied - you cancelled the login" + access_denied: "Access denied - you cancelled the login", + email_in_use: + "This email is already associated with another account. Please sign in with that account instead." }; setError(errorMessages[errorParam] || "An error occurred during login"); } @@ -333,11 +335,22 @@ export default function LoginPage() { {/* Main content */}
{/* Error message */} -
+
- Passwords did not match! +
Passwords did not match!
+
+ +
Email Already Exists!
+
+ +
{error()}
- Email Already Exists!
{/* Title */} diff --git a/src/server/api/routers/auth.ts b/src/server/api/routers/auth.ts index e1efd01..5546b97 100644 --- a/src/server/api/routers/auth.ts +++ b/src/server/api/routers/auth.ts @@ -154,10 +154,25 @@ export const authRouter = createTRPCRouter({ // User exists - update email and image if changed userId = (res.rows[0] as unknown as User).id; - await conn.execute({ - sql: `UPDATE User SET email = ?, email_verified = ?, image = ? WHERE id = ?`, - args: [email, emailVerified ? 1 : 0, icon, userId] - }); + try { + await conn.execute({ + sql: `UPDATE User SET email = ?, email_verified = ?, image = ? WHERE id = ?`, + args: [email, emailVerified ? 1 : 0, icon, userId] + }); + } catch (updateError: any) { + // Handle unique constraint error on email + if ( + updateError.code === "SQLITE_CONSTRAINT" && + updateError.message?.includes("User.email") + ) { + throw new TRPCError({ + code: "CONFLICT", + message: + "This email is already associated with another account. Please sign in with that account or use a different email address." + }); + } + throw updateError; + } } else { // Create new user userId = uuidV4(); @@ -171,7 +186,23 @@ export const authRouter = createTRPCRouter({ "github", icon ]; - await conn.execute({ sql: insertQuery, args: insertParams }); + + try { + await conn.execute({ sql: insertQuery, args: insertParams }); + } catch (insertError: any) { + // Handle unique constraint error on email + if ( + insertError.code === "SQLITE_CONSTRAINT" && + insertError.message?.includes("User.email") + ) { + throw new TRPCError({ + code: "CONFLICT", + message: + "This email is already associated with another account. Please sign in with that account or use a different email address." + }); + } + throw insertError; + } } // Create JWT token @@ -291,6 +322,7 @@ export const authRouter = createTRPCRouter({ // User exists - update email, email_verified, display_name, and image if changed userId = (res.rows[0] as unknown as User).id; + // No need to catch constraint error here since we're updating the same user's record await conn.execute({ sql: `UPDATE User SET email = ?, email_verified = ?, display_name = ?, image = ? WHERE id = ?`, args: [email, email_verified ? 1 : 0, name, image, userId] @@ -308,10 +340,26 @@ export const authRouter = createTRPCRouter({ "google", image ]; - await conn.execute({ - sql: insertQuery, - args: insertParams - }); + + try { + await conn.execute({ + sql: insertQuery, + args: insertParams + }); + } catch (insertError: any) { + // Handle unique constraint error on email + if ( + insertError.code === "SQLITE_CONSTRAINT" && + insertError.message?.includes("User.email") + ) { + throw new TRPCError({ + code: "CONFLICT", + message: + "This email is already associated with another account. Please sign in with that account instead." + }); + } + throw insertError; + } } // Create JWT token