153 lines · 6.8 KB
import { all, batch, first, run, stmt } from "./db";
import { accountDb } from "./scoped-db";
import { forgetLocator } from "./scoped-db/locators";
import { badRequest, conflict, notFoundError } from "./errors";
import { notifySecurityEvent, sendEmailVerification } from "./account-security";
import { invalidateUserSignatures } from "./security/signatures";
import { promoteAdminEmailOnVerify, reclaimUnverifiedEmail } from "./accounts";
import { userChanged } from "./user-row";

const MAX_EMAILS = 20;

export type AccountEmail = { email: string; primary: boolean; verified: boolean; createdAt: number | null };

type PrimaryRow = { email: string; email_verified_at: number | null };

async function primaryOf(userId: string): Promise<PrimaryRow> {
  const row = await first<PrimaryRow>("SELECT email, email_verified_at FROM users WHERE account_id = ?", userId);
  if (!row) throw notFoundError("Account not found");
  return row;
}

function same(left: string, right: string): boolean {
  return left.toLowerCase() === right.toLowerCase();
}

export async function listAccountEmails(userId: string): Promise<AccountEmail[]> {
  const primary = await primaryOf(userId);
  const others = await all<{ email: string; verified_at: number | null; created_at: number }>(
    "SELECT email, verified_at, created_at FROM user_emails WHERE user_id = ? ORDER BY created_at",
    userId,
  );
  return [
    { email: primary.email, primary: true, verified: primary.email_verified_at !== null, createdAt: null },
    ...others.map((row) => ({
      email: row.email,
      primary: false,
      verified: row.verified_at !== null,
      createdAt: row.created_at,
    })),
  ];