79 lines
2.4 KiB
TypeScript
79 lines
2.4 KiB
TypeScript
import type { AdapterAccount } from "next-auth/adapters";
|
|
|
|
export type DiscordIdentity = {
|
|
discordId: string;
|
|
displayName: string;
|
|
avatarUrl: string | null;
|
|
email?: string | null;
|
|
account?: Partial<AdapterAccount>;
|
|
};
|
|
|
|
export function discordAccountUpdate(
|
|
account: Partial<AdapterAccount> | 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<typeof discordAccountUpdate>;
|
|
|
|
export type DiscordUserOperations<TUser> = {
|
|
findLinkedUserId(discordId: string): Promise<string | null>;
|
|
upsertUser(identity: DiscordIdentity): Promise<string>;
|
|
insertAccount(
|
|
userId: string,
|
|
discordId: string,
|
|
account: Partial<AdapterAccount> | undefined,
|
|
): Promise<void>;
|
|
updateUser(userId: string, identity: DiscordIdentity): Promise<TUser | null>;
|
|
updateAccount(
|
|
discordId: string,
|
|
update: DiscordAccountUpdate,
|
|
): Promise<void>;
|
|
};
|
|
|
|
export async function reconcileDiscordUser<TUser>(
|
|
identity: DiscordIdentity,
|
|
operations: DiscordUserOperations<TUser>,
|
|
) {
|
|
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;
|
|
}
|