import { currentUser } from "@clerk/nextjs/server"; import type { IDatabase, User } from "./database/types"; import log from "./logger"; export async function ensureUserSynced( userId: string, db: IDatabase, ): Promise { const existingUser = await db.getUserById(userId); if (existingUser) return existingUser; log.debug("User not found in DB by ID, checking Clerk", { userId }); const clerkUser = await currentUser(); if (!clerkUser || clerkUser.id !== userId) { // If we can't get the user from Clerk (e.g. running locally without full auth sync), // we might want to fail gracefully or throw. // For now, throw to be safe. throw new Error("Could not fetch Clerk user details"); } const email = clerkUser.emailAddresses[0]?.emailAddress; if (!email) throw new Error("User has no email"); // Check if user exists by email (e.g. seeded user) const existingByEmail = await db.getUserByEmail(email); if (existingByEmail) { log.debug("User found by email but ID mismatch, migrating ID", { oldId: existingByEmail.id, newId: userId, }); // Update the ID to match Clerk ID // We need to do this manually via SQL because IDatabase interface might not expose a direct ID update method easily // But we can use a raw query if we had access, or add a method. // Since we don't have direct access to `db.db` here (it's hidden behind IDatabase), // we should add a method to IDatabase or use a workaround. // Actually, `SQLiteDatabase` is what we have at runtime. // Let's assume we can cast it or add `updateUserId` to the interface. // For now, let's try to update it using `updateUser` but `updateUser` usually updates fields based on ID. // We can't update the ID itself using `updateUser(id, { id: newId })` because `updateUser` implementation filters out `id` from updates. // We need to add a method to migrate user ID in IDatabase. // Or, strictly for this prototype, we can delete the old user and create a new one (DATA LOSS RISK). // But since it's a seeded super admin with no data, it's fine. // However, if they had clients, we'd lose the link. // Let's add `migrateUserId(oldId, newId)` to IDatabase. await db.migrateUserId(existingByEmail.id, userId); return db.getUserById(userId); } log.debug("Creating new user from Clerk data", { userId, email }); const user = await db.createUser({ id: userId, email, firstName: clerkUser.firstName || "", lastName: clerkUser.lastName || "", password: "", // Managed by Clerk phone: clerkUser.phoneNumbers[0]?.phoneNumber || undefined, gymId: (clerkUser.publicMetadata.gymId as string | undefined) || undefined, role: (() => { const r = clerkUser.publicMetadata.role as string | undefined; const validRoles = ["superAdmin", "admin", "trainer", "client"] as const; return r && validRoles.includes(r as (typeof validRoles)[number]) ? (r as (typeof validRoles)[number]) : "client"; })(), }); return user; }