diff --git a/src/lib/auth/index.ts b/src/lib/auth/index.ts index 82b68351..ddb80d8b 100644 --- a/src/lib/auth/index.ts +++ b/src/lib/auth/index.ts @@ -4,10 +4,104 @@ import { db } from "@/lib/db"; import { getConfig } from "@/lib/config"; import { initializePlugins, getAuthPlugin } from "@/lib/plugins"; import type { User } from "@prisma/client"; +import type { Adapter, AdapterUser } from "next-auth/adapters"; // Initialize plugins before use initializePlugins(); +// Generate a unique username from email or name +async function generateUsername(email: string, name?: string | null): Promise { + // Try to use the part before @ in email + let baseUsername = email.split("@")[0].toLowerCase().replace(/[^a-z0-9_]/g, ""); + + // If too short, use name + if (baseUsername.length < 3 && name) { + baseUsername = name.toLowerCase().replace(/[^a-z0-9_]/g, "").slice(0, 15); + } + + // Ensure minimum length + if (baseUsername.length < 3) { + baseUsername = "user"; + } + + // Check if username exists and append number if needed + let username = baseUsername; + let counter = 1; + while (await db.user.findUnique({ where: { username } })) { + username = `${baseUsername}${counter}`; + counter++; + } + + return username; +} + +// Custom adapter that wraps PrismaAdapter to add username +function CustomPrismaAdapter(): Adapter { + const prismaAdapter = PrismaAdapter(db); + + return { + ...prismaAdapter, + async createUser(data: AdapterUser & { username?: string }) { + // Use GitHub username if provided, otherwise generate one + let username = (data as any).username; + if (!username) { + username = await generateUsername(data.email, data.name); + } else { + username = username.toLowerCase(); + + // Check if there's an unclaimed account with this username + const unclaimedEmail = `${username}@unclaimed.prompts.chat`; + const unclaimedUser = await db.user.findUnique({ + where: { email: unclaimedEmail }, + }); + + if (unclaimedUser) { + // Claim this account - update with real user info + const claimedUser = await db.user.update({ + where: { id: unclaimedUser.id }, + data: { + name: data.name, + email: data.email, + avatar: data.image, + emailVerified: data.emailVerified, + }, + }); + + return { + ...claimedUser, + image: claimedUser.avatar, + } as AdapterUser; + } + + // Ensure GitHub username is unique, append number if taken + const baseUsername = username; + let finalUsername = baseUsername; + let counter = 1; + while (await db.user.findUnique({ where: { username: finalUsername } })) { + finalUsername = `${baseUsername}${counter}`; + counter++; + } + username = finalUsername; + } + + const user = await db.user.create({ + data: { + name: data.name, + email: data.email, + avatar: data.image, + emailVerified: data.emailVerified, + username, + }, + }); + + return { + ...user, + image: user.avatar, + } as AdapterUser; + }, + }; +} + // Build auth config dynamically based on prompts.config.ts async function buildAuthConfig() { const config = await getConfig(); @@ -18,7 +112,7 @@ async function buildAuthConfig() { } return { - adapter: PrismaAdapter(db), + adapter: CustomPrismaAdapter(), providers: [authPlugin.getProvider()], session: { strategy: "jwt" as const, diff --git a/src/lib/plugins/auth/github.ts b/src/lib/plugins/auth/github.ts index 50e4f781..c209e206 100644 --- a/src/lib/plugins/auth/github.ts +++ b/src/lib/plugins/auth/github.ts @@ -8,5 +8,14 @@ export const githubPlugin: AuthPlugin = { GitHub({ clientId: process.env.GITHUB_CLIENT_ID!, clientSecret: process.env.GITHUB_CLIENT_SECRET!, + profile(profile) { + return { + id: profile.id.toString(), + name: profile.name || profile.login, + email: profile.email, + image: profile.avatar_url, + username: profile.login, // GitHub username + }; + }, }), };