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) {