Skip to content

Commit

Permalink
fix: improved error/bounce parsing and handling, implemented undici r…
Browse files Browse the repository at this point in the history
…etries, fixed pool concurrency, fixed invite if domain already exists, bump deps, sync locales
  • Loading branch information
titanism committed Sep 14, 2023
1 parent 81db5eb commit 5d0a065
Show file tree
Hide file tree
Showing 39 changed files with 992 additions and 805 deletions.
7 changes: 7 additions & 0 deletions app/controllers/web/my-account/retrieve-invite.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ async function retrieveInvite(ctx) {
Boom.notFound(ctx.translateError('DOMAIN_DOES_NOT_EXIST'))
);

// if the user already is an admin or member of domain with same name
const match = ctx.state.domains.find((d) => d.name === domain.name);
if (match)
return ctx.throw(
Boom.badRequest(ctx.translateError('DOMAIN_ALREADY_EXISTS'))
);

// convert invitee to a member with the same group as invite had
const invite = domain.invites.find(
(invite) => invite.email === ctx.state.user.email
Expand Down
45 changes: 9 additions & 36 deletions app/models/domains.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ const { promisify } = require('node:util');
const Boom = require('@hapi/boom');
const RE2 = require('re2');
const _ = require('lodash');
const captainHook = require('captain-hook');
const cryptoRandomString = require('crypto-random-string');
const dayjs = require('dayjs-with-plugins');
const delay = require('delay');
Expand All @@ -22,17 +21,17 @@ const striptags = require('striptags');
const { boolean } = require('boolean');
const { convert } = require('html-to-text');
const { isIP, isEmail, isPort, isURL } = require('validator');
const { request, errors } = require('undici');

const pkg = require('../../package.json');
const Users = require('./users');

const config = require('#config');
const i18n = require('#helpers/i18n');
const logger = require('#helpers/logger');
const parseRootDomain = require('#helpers/parse-root-domain');
const retryRequest = require('#helpers/retry-request');
const verificationRecordOptions = require('#config/verification-record');
const { encrypt } = require('#helpers/encrypt-decrypt');
const parseRootDomain = require('#helpers/parse-root-domain');

const concurrency = os.cpus().length;
const CACHE_TYPES = ['NS', 'MX', 'TXT'];
Expand Down Expand Up @@ -88,24 +87,10 @@ const REGEX_MAIL_DISPOSABLE_INBOX = new RE2(
const disposableDomains = new Set();
async function crawlDisposable() {
try {
const response = await request(
'https://raw.githubusercontent.com/disposable/disposable-email-domains/master/domains.json',
{
signal: AbortSignal.timeout(10000),
throwOnError: true
}
const response = await retryRequest(
'https://raw.githubusercontent.com/disposable/disposable-email-domains/master/domains.json'
);

// the error code is between 200-400 (e.g. 302 redirect)
// in order to mirror the behavior of `throwOnError` we will re-use the undici errors
// <https://github.com/nodejs/undici/issues/2093>
if (response.statusCode !== 200)
throw new errors.ResponseStatusCodeError(
`Response status code ${response.statusCode}`,
response.statusCode,
response.headers
);

const json = await response.body.json();
if (!Array.isArray(json) || json.length === 0) {
throw new Error('Disposable did not crawl data.');
Expand Down Expand Up @@ -390,8 +375,6 @@ Domains.index(
}
);

Domains.plugin(captainHook);

// shared tangerine resolver
Domains.virtual('resolver')
.get(function () {
Expand Down Expand Up @@ -1058,17 +1041,16 @@ async function getVerificationResults(domain, resolver) {
CACHE_TYPES,
(type) => {
const url = new URL(CLOUDFLARE_PURGE_CACHE_URL);
url.searchParams = new URLSearchParams({
domain: domain.name,
type
});
return request(url, {
url.searchParams.append('domain', domain.name);
url.searchParams.append('type', type);
return retryRequest(url, {
method: 'POST',
headers: {
Accept: 'application/json',
'User-Agent': USER_AGENT
},
signal: AbortSignal.timeout(10000)
timeout: ms('5s'),
retries: 2
});
},
{ concurrency }
Expand Down Expand Up @@ -1614,15 +1596,6 @@ async function ensureUserHasValidPlan(user, locale) {

Domains.statics.ensureUserHasValidPlan = ensureUserHasValidPlan;

Domains.postCreate((domain, next) => {
// log that the domain was created
logger.info('domain created', {
domain: domain.toObject()
});

next();
});

const conn = mongoose.connections.find(
(conn) => conn[Symbol.for('connection.name')] === 'MONGO_URI'
);
Expand Down
20 changes: 3 additions & 17 deletions app/models/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ const striptags = require('striptags');
const validator = require('validator');
const { authenticator } = require('otplib');
const { boolean } = require('boolean');
const { request, errors } = require('undici');

// <https://github.com/Automattic/mongoose/issues/5534>
mongoose.Error.messages = require('@ladjs/mongoose-error-messages');

const Payments = require('./payments');

const retryRequest = require('#helpers/retry-request');
const logger = require('#helpers/logger');
const config = require('#config');
const i18n = require('#helpers/i18n');
Expand Down Expand Up @@ -568,24 +568,10 @@ Users.virtual(config.userFields.verificationPinHasExpired).get(function () {
const disposableDomains = new Set();
async function crawlDisposable() {
try {
const response = await request(
'https://raw.githubusercontent.com/disposable/disposable-email-domains/master/domains.json',
{
signal: AbortSignal.timeout(10000),
throwOnError: true
}
const response = await retryRequest(
'https://raw.githubusercontent.com/disposable/disposable-email-domains/master/domains.json'
);

// the error code is between 200-400 (e.g. 302 redirect)
// in order to mirror the behavior of `throwOnError` we will re-use the undici errors
// <https://github.com/nodejs/undici/issues/2093>
if (response.statusCode !== 200)
throw new errors.ResponseStatusCodeError(
`Response status code ${response.statusCode}`,
response.statusCode,
response.headers
);

const json = await response.body.json();
if (!Array.isArray(json) || json.length === 0) {
throw new Error('Disposable did not crawl data.');
Expand Down
11 changes: 5 additions & 6 deletions config/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@ const signale = require('signale');

const env = require('./env');

const isProduction = env.NODE_ENV === 'production';

module.exports = {
// eslint-disable-next-line no-undef
logger: typeof window === 'object' ? console : signale,
level: isProduction ? 'info' : 'debug',
levels: isProduction
? ['info', 'warn', 'error', 'fatal']
: ['trace', 'info', 'debug', 'warn', 'error', 'fatal'],
level: env.NODE_ENV === 'test' ? 'debug' : 'info',
levels:
env.NODE_ENV === 'test'
? ['trace', 'info', 'debug', 'warn', 'error', 'fatal']
: ['info', 'warn', 'error', 'fatal'],
showStack: env.AXE_SHOW_STACK,
meta: {
show: env.AXE_SHOW_META
Expand Down
1 change: 0 additions & 1 deletion helpers/check-srs.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ function checkSRS(address, shouldThrow = false, ignoreHook = false) {
err = error;
}

// TODO: rewrite `err.response` and `err.message` if either/both start with diagnostic code
if (!err.responseCode) err.responseCode = 553;
if (ignoreHook) err.ignoreHook = true;

Expand Down
4 changes: 2 additions & 2 deletions helpers/create-bounce.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ const ip = require('ip');
const isFQDN = require('is-fqdn');
const { convert } = require('html-to-text');

const getErrorCode = require('#helpers/get-error-code');
const getDiagnosticCode = require('#helpers/get-diagnostic-code');
const getErrorCode = require('./get-error-code');
const getDiagnosticCode = require('./get-diagnostic-code');
const config = require('#config');

const HOSTNAME = os.hostname();
Expand Down
1 change: 0 additions & 1 deletion helpers/email.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const config = require('#config');
const email = new Email(config.email);

// TODO: email-templates should strip tags from HTML in subject
// TODO: rewrite to create a new email, queue it, and send
module.exports = async (data) => {
try {
logger.info('sending email', { data });
Expand Down
4 changes: 2 additions & 2 deletions helpers/get-all-paypal-subscription-transactions.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const pWhilst = require('p-whilst');
const { paypalAgent } = require('#helpers/paypal');
const logger = require('#helpers/logger');
const { paypalAgent } = require('./paypal');
const logger = require('./logger');

// subscription is PayPal subscription object
async function getAllPayPalSubscriptionTransactions(subscription, agent) {
Expand Down
Loading

0 comments on commit 5d0a065

Please sign in to comment.