in packages/fxa-auth-server/scripts/verification-reminders.js [68:328]
async function run() {
const { createDB } = require(`${LIB_DIR}/db`);
const [vReminders, saReminders, cReminders, db] = await Promise.all([
verificationReminders.process(),
subscriptionAccountReminders.process(),
cadReminders.process(),
createDB(config, log, {}, {}).connect(config),
]);
const bounces = require(`${LIB_DIR}/bounces`)(config, db);
const Mailer = require(`${LIB_DIR}/senders/email`)(log, config, bounces);
const mailer = new Mailer(config.smtp);
const sent = {};
await verificationReminders.keys.reduce(async (promise, key) => {
await promise;
const method = `verificationReminder${key[0].toUpperCase()}${key.substr(
1
)}Email`;
const reminders = vReminders[key];
log.info('verificationReminders.processing', {
count: reminders.length,
key,
});
const failedReminders = await reminders.reduce(
async (promise, { timestamp, uid, flowId, flowBeginTime }) => {
const failed = await promise;
try {
if (sent[uid]) {
// Don't send e.g. first and second reminders to the same email from a single batch
log.info('verificationReminders.skipped.alreadySent', { uid });
failed.push({ timestamp, uid, flowId, flowBeginTime });
return failed;
}
const account = await db.account(uid);
await mailer[method]({
acceptLanguage: account.locale,
code: account.emailCode,
email: account.email,
flowBeginTime,
flowId,
uid,
});
// eslint-disable-next-line require-atomic-updates
sent[uid] = true;
} catch (err) {
const { errno } = err;
switch (errno) {
case error.ERRNO.ACCOUNT_UNKNOWN:
case error.ERRNO.BOUNCE_COMPLAINT:
case error.ERRNO.BOUNCE_HARD:
case error.ERRNO.BOUNCE_SOFT:
log.info('verificationReminders.skipped.error', { uid, errno });
try {
await verificationReminders.delete(uid);
} catch (ignore) {}
break;
default:
log.error('verificationReminders.error', { err });
failed.push({ timestamp, uid, flowId, flowBeginTime });
}
}
return failed;
},
Promise.resolve([])
);
if (failedReminders.length > 0) {
log.info('verificationReminders.reinstating', {
count: reminders.length,
key,
});
return verificationReminders.reinstate(key, failedReminders);
}
}, Promise.resolve());
await subscriptionAccountReminders.keys.reduce(async (promise, key) => {
await promise;
const method = `subscriptionAccountReminder${key[0].toUpperCase()}${key.substr(
1
)}Email`;
const reminders = saReminders[key];
log.info('subscriptionAccountReminder.processing', {
count: reminders.length,
key,
});
const failedReminders = await reminders.reduce(
async (
promise,
{
timestamp,
uid,
flowId,
flowBeginTime,
deviceId,
productId,
productName,
}
) => {
const failed = await promise;
try {
if (sent[uid]) {
// Don't send e.g. first and second reminders to the same email from a single batch
log.info('subscriptionAccountReminder.skipped.alreadySent', {
uid,
});
failed.push({
timestamp,
uid,
flowId,
flowBeginTime,
deviceId,
productId,
productName,
});
return failed;
}
const account = await db.account(uid);
const token = await jwt.sign(
{ uid },
{
header: {
typ: 'fin+JWT',
},
}
);
await mailer[method]({
acceptLanguage: account.locale,
code: account.emailCode,
email: account.email,
accountVerified: account.verifierSetAt > 0,
token: token,
flowBeginTime,
flowId,
uid,
deviceId,
productId,
productName,
});
// eslint-disable-next-line require-atomic-updates
sent[uid] = true;
} catch (err) {
const { errno } = err;
switch (errno) {
case error.ERRNO.ACCOUNT_UNKNOWN:
case error.ERRNO.BOUNCE_COMPLAINT:
case error.ERRNO.BOUNCE_HARD:
case error.ERRNO.BOUNCE_SOFT:
log.info('subscriptionAccountReminder.skipped.error', {
uid,
errno,
});
try {
await subscriptionAccountReminders.delete(uid);
} catch (ignore) {}
break;
default:
log.error('subscriptionAccountReminder.error', { err });
failed.push({
timestamp,
uid,
flowId,
flowBeginTime,
deviceId,
productId,
productName,
});
}
}
return failed;
},
Promise.resolve([])
);
if (failedReminders.length > 0) {
log.info('subscriptionAccountReminder.reinstating', {
count: reminders.length,
key,
});
return subscriptionAccountReminders.reinstate(key, failedReminders);
}
}, Promise.resolve());
// TODO: This is intentionally an exact copy of the above code
// since CAD reminders are an experiment. At the end we'll either
// refactor reminders into a better abstraction of remove this code.
await cadReminders.keys.reduce(async (promise, key) => {
await promise;
const method = `cadReminder${key[0].toUpperCase()}${key.substr(1)}Email`;
const reminders = cReminders[key];
log.info('cadReminders.processing', {
count: reminders.length,
key,
});
await reminders.reduce(
async (promise, { timestamp, uid, flowId, flowBeginTime }) => {
const failed = await promise;
try {
if (sent[uid]) {
// Don't send e.g. first and second reminders to the same email from a single batch
log.info('cadReminders.skipped.alreadySent', { uid });
failed.push({ timestamp, uid, flowId, flowBeginTime });
return failed;
}
const account = await db.account(uid);
await mailer[method]({
acceptLanguage: account.locale,
code: account.emailCode,
email: account.email,
flowBeginTime,
flowId,
uid,
});
// eslint-disable-next-line require-atomic-updates
sent[uid] = true;
} catch (err) {
const { errno } = err;
switch (errno) {
case error.ERRNO.ACCOUNT_UNKNOWN:
case error.ERRNO.BOUNCE_COMPLAINT:
case error.ERRNO.BOUNCE_HARD:
case error.ERRNO.BOUNCE_SOFT:
log.info('cadReminders.skipped.error', { uid, errno });
try {
await cadReminders.delete(uid);
} catch (ignore) {}
break;
default:
log.error('cadReminders.error', { err });
failed.push({ timestamp, uid, flowId, flowBeginTime });
}
}
return failed;
},
Promise.resolve([])
);
}, Promise.resolve());
await db.close();
await verificationReminders.close();
await subscriptionAccountReminders.close();
await cadReminders.close();
}