sha1: getSha1()

in src/db/tables/emailAddresses.ts [67:178]


              sha1: getSha1(lowerCaseEmail),
              verification_token: uuidv4(),
              verified: false,
            })
            .returning("*");
      });
  });

  if (res) {
    return res[0];
  } else {
    throw new Error("Could not add email address");
  }
}
/* c8 ignore stop */

// Not covered by tests; mostly side-effects. See test-coverage.md#mock-heavy
/* c8 ignore start */
async function resetUnverifiedEmailAddress(
  emailAddressId: EmailAddressRow["id"],
  subscriber: SubscriberRow,
  l10n: ReactLocalization,
) {
  const newVerificationToken = uuidv4();

  // Time in ms to require between verification reset.
  const verificationWait = 5 * 60 * 1000; // 5 minutes

  const verificationRecentlyUpdated = await knex("email_addresses")
    .select("id")
    .whereRaw(
      "\"updated_at\" > NOW() - INTERVAL '1 MILLISECOND' * ?",
      verificationWait,
    )
    .andWhere("id", emailAddressId)
    .first();

  if (
    verificationRecentlyUpdated?.id ===
    (typeof emailAddressId === "number"
      ? emailAddressId
      : parseInt(emailAddressId, 10))
  ) {
    throw new ForbiddenError(l10n.getString("error-email-validation-pending"));
  }

  const res = await knex("email_addresses")
    .update({
      verification_token: newVerificationToken,
      // @ts-ignore knex.fn.now() results in it being set to a date,
      // even if it's not typed as a JS date object:
      updated_at: knex.fn.now(),
    })
    .where("id", emailAddressId)
    .andWhere("subscriber_id", subscriber.id)
    .returning("*");
  return res[0];
}
/* c8 ignore stop */

// Not covered by tests; mostly side-effects. See test-coverage.md#mock-heavy
/* c8 ignore start */
async function verifyEmailHash(token: string) {
  const unverifiedEmail = await getEmailByToken(token);
  if (!unverifiedEmail) {
    throw new UnauthorizedError(
      "Error message for this verification email timed out or something went wrong.",
    );
  }
  const verifiedEmail = await _verifyNewEmail(unverifiedEmail);
  return verifiedEmail[0];
}
/* c8 ignore stop */

// TODO: refactor into an upsert? https://jaketrent.com/post/upsert-knexjs/
// Used internally, ideally should not be called by consumers.

// Not covered by tests; mostly side-effects. See test-coverage.md#mock-heavy
/* c8 ignore start */
async function _getSha1EntryAndDo(
  sha1: string,
  aFoundCallback: (existingSubscriber: SubscriberRow) => Promise<SubscriberRow>,
  aNotFoundCallback: () => Promise<SubscriberRow>,
) {
  const existingEntries = await knex("subscribers").where("primary_sha1", sha1);

  if (existingEntries.length && aFoundCallback) {
    return await aFoundCallback(existingEntries[0]);
  }

  if (!existingEntries.length && aNotFoundCallback) {
    return await aNotFoundCallback();
  }
}
/* c8 ignore stop */

// Used internally.
// Not covered by tests; mostly side-effects. See test-coverage.md#mock-heavy
/* c8 ignore start */

async function _addEmailHash(
  sha1: string,
  email: string,
  signupLanguage: string,
  verified: boolean = false,
): Promise<SubscriberRow | undefined> {
  try {
    return await _getSha1EntryAndDo(
      sha1,
      async (aEntry: SubscriberRow) => {
        // Entry existed, patch the email value if supplied.
        if (email) {