import type { AdapterAccount } from "next-auth/adapters"; export type DiscordIdentity = { discordId: string; displayName: string; avatarUrl: string | null; email?: string | null; account?: Partial; }; export function discordAccountUpdate( account: Partial | undefined, ) { if (!account) return {}; return { ...(account.type !== undefined ? { type: account.type } : {}), ...(account.refresh_token !== undefined ? { refresh_token: account.refresh_token } : {}), ...(account.access_token !== undefined ? { access_token: account.access_token } : {}), ...(account.expires_at !== undefined ? { expires_at: account.expires_at } : {}), ...(account.token_type !== undefined ? { token_type: account.token_type } : {}), ...(account.scope !== undefined ? { scope: account.scope } : {}), ...(account.id_token !== undefined ? { id_token: account.id_token } : {}), ...(account.session_state !== undefined ? { session_state: account.session_state as string | null } : {}), }; } export type DiscordAccountUpdate = ReturnType; export type DiscordUserOperations = { findLinkedUserId(discordId: string): Promise; upsertUser(identity: DiscordIdentity): Promise; insertAccount( userId: string, discordId: string, account: Partial | undefined, ): Promise; updateUser(userId: string, identity: DiscordIdentity): Promise; updateAccount( discordId: string, update: DiscordAccountUpdate, ): Promise; }; export async function reconcileDiscordUser( identity: DiscordIdentity, operations: DiscordUserOperations, ) { const { discordId, account } = identity; let userId = await operations.findLinkedUserId(discordId); if (!userId) { userId = await operations.upsertUser(identity); await operations.insertAccount(userId, discordId, account); const winner = await operations.findLinkedUserId(discordId); if (!winner) throw new Error("Failed to link Discord account"); userId = winner; } const user = await operations.updateUser(userId, identity); const nextAccount = discordAccountUpdate(account); if (Object.keys(nextAccount).length > 0) { await operations.updateAccount(discordId, nextAccount); } if (!user) throw new Error("Failed to reconcile Discord user"); return user; }