feat(auth): add custom adapter for generating unique usernames

This commit is contained in:
Fatih Kadir Akın
2025-12-11 01:15:49 +03:00
parent dd95a41a64
commit 2f5c0109ae
2 changed files with 104 additions and 1 deletions
+95 -1
View File
@@ -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<string> {
// 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,
+9
View File
@@ -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
};
},
}),
};