mirror of
https://github.com/wassname/talk.git
synced 2026-07-27 11:28:12 +08:00
If for whatever reason an email job gets queued for a user with no email, an exception is thrown on the getEmailAddress() step. This exception was never handled and therefore the app crashed. Handle it and log instead..
153 lines
3.6 KiB
JavaScript
153 lines
3.6 KiB
JavaScript
const { task } = require('../services/mailer');
|
|
const nodemailer = require('nodemailer');
|
|
const { createLogger } = require('../services/logging');
|
|
const logger = createLogger('jobs:mailer');
|
|
const Context = require('../graph/context');
|
|
const { get } = require('lodash');
|
|
const {
|
|
SMTP_HOST,
|
|
SMTP_USERNAME,
|
|
SMTP_PORT,
|
|
SMTP_PASSWORD,
|
|
SMTP_FROM_ADDRESS,
|
|
} = require('../config');
|
|
const { ErrMissingEmail } = require('../errors');
|
|
|
|
// parseSMTPPort will return the port for SMTP.
|
|
const parseSMTPPort = () => {
|
|
if (!SMTP_PORT) {
|
|
return 25;
|
|
}
|
|
|
|
try {
|
|
return parseInt(SMTP_PORT);
|
|
} catch (e) {
|
|
throw new Error('TALK_SMTP_PORT is not an integer');
|
|
}
|
|
};
|
|
|
|
// createTransport will create a new transport.
|
|
const createTransport = () => {
|
|
const options = {
|
|
host: SMTP_HOST,
|
|
};
|
|
|
|
if (SMTP_USERNAME && SMTP_PASSWORD) {
|
|
options.auth = {
|
|
user: SMTP_USERNAME,
|
|
pass: SMTP_PASSWORD,
|
|
};
|
|
}
|
|
|
|
// Get the SMTP port.
|
|
options.port = parseSMTPPort();
|
|
|
|
return nodemailer.createTransport(options);
|
|
};
|
|
|
|
// sharedTransport is the transport singleton.
|
|
let sharedTransport;
|
|
|
|
// getTransport will retrieve the mailer transport singleton.
|
|
const getTransport = () => {
|
|
if (sharedTransport) {
|
|
return sharedTransport;
|
|
}
|
|
|
|
// enabled is true when the required configuration is available. When testing
|
|
// is enabled, we will be simulating that emails are being sent, because in a
|
|
// production system, emails should and would be sent.
|
|
|
|
// If the transport details aren't available, we will return null as the
|
|
// transport.
|
|
if (!SMTP_HOST || !SMTP_FROM_ADDRESS) {
|
|
return null;
|
|
}
|
|
|
|
// Create the transport.
|
|
sharedTransport = createTransport();
|
|
|
|
return sharedTransport;
|
|
};
|
|
|
|
// getEmailAddress will retrieve the email address to send the message to from
|
|
// the job data.
|
|
const getEmailAddress = async ({ email, user }) => {
|
|
// If the message has a specific email already to sent it to, just assign
|
|
// that to the message. If the email does not have an email, and instead has
|
|
// a user id, then we should lookup the user with the graph and get their
|
|
// email.
|
|
if (email) {
|
|
return email;
|
|
} else {
|
|
// Get the user to send the message to.
|
|
const ctx = Context.forSystem();
|
|
|
|
const { data, errors } = await ctx.graphql(
|
|
`
|
|
query GetUserEmail($user: ID!) {
|
|
user(id: $user) {
|
|
email
|
|
}
|
|
}
|
|
`,
|
|
{ user }
|
|
);
|
|
if (errors) {
|
|
throw errors;
|
|
}
|
|
|
|
const email = get(data, 'user.email');
|
|
if (!email) {
|
|
throw new ErrMissingEmail();
|
|
}
|
|
|
|
return email;
|
|
}
|
|
};
|
|
|
|
// processJob will handle new jobs sent via this queue.
|
|
const processJob = transport => async ({ id, data }, done) => {
|
|
const { message } = data;
|
|
|
|
// Get the email address from the job data.
|
|
try {
|
|
message.to = await getEmailAddress(data);
|
|
} catch (err) {
|
|
logger.error({ err }, 'Failed to get user email address to send mail');
|
|
return done(err);
|
|
}
|
|
|
|
const log = logger.child({ jobID: id });
|
|
log.info('Starting to send mail');
|
|
|
|
// Actually send the email.
|
|
transport.sendMail(message, err => {
|
|
if (err) {
|
|
logger.error({ err }, 'Failed to send mail');
|
|
return done(err);
|
|
}
|
|
|
|
logger.info('Finished sending mail');
|
|
return done();
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Start the queue processor for the mailer job.
|
|
*/
|
|
module.exports = () => {
|
|
// Get a transport.
|
|
const transport = getTransport();
|
|
if (transport === null) {
|
|
logger.warn(
|
|
'Sending email is not enabled because required configuration is not available'
|
|
);
|
|
return;
|
|
}
|
|
|
|
logger.info({ taskName: task.name }, 'Now processing jobs');
|
|
|
|
return task.process(processJob(transport));
|
|
};
|