async function run()

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();
}