b9e0668d7d
* First pass (working) collection export to zip * Add export confirmation screen * 👕 * Refactor * Job for team export, move to tmp file, settings UI * Export all collections job * 👕 * Add specs * Clarify UI
148 lines
3.5 KiB
JavaScript
148 lines
3.5 KiB
JavaScript
// @flow
|
|
import * as React from 'react';
|
|
import debug from 'debug';
|
|
import bugsnag from 'bugsnag';
|
|
import nodemailer from 'nodemailer';
|
|
import Oy from 'oy-vey';
|
|
import Queue from 'bull';
|
|
import { baseStyles } from './emails/components/EmailLayout';
|
|
import { WelcomeEmail, welcomeEmailText } from './emails/WelcomeEmail';
|
|
import { ExportEmail, exportEmailText } from './emails/ExportEmail';
|
|
|
|
const log = debug('emails');
|
|
|
|
type Emails = 'welcome' | 'export';
|
|
|
|
type SendMailType = {
|
|
to: string,
|
|
properties?: any,
|
|
title: string,
|
|
previewText?: string,
|
|
text: string,
|
|
html: React.Node,
|
|
headCSS?: string,
|
|
attachments?: Object[],
|
|
};
|
|
|
|
type EmailJob = {
|
|
data: {
|
|
type: Emails,
|
|
opts: SendMailType,
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Mailer
|
|
*
|
|
* Mailer class to contruct and send emails.
|
|
*
|
|
* To preview emails, add a new preview to `emails/index.js` if they
|
|
* require additional data (properties). Otherwise preview will work automatically.
|
|
*
|
|
* HTML: http://localhost:3000/email/:email_type/html
|
|
* TEXT: http://localhost:3000/email/:email_type/text
|
|
*/
|
|
export default class Mailer {
|
|
transporter: ?any;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
sendMail = async (data: SendMailType): ?Promise<*> => {
|
|
const { transporter } = this;
|
|
|
|
if (transporter) {
|
|
const html = Oy.renderTemplate(data.html, {
|
|
title: data.title,
|
|
headCSS: [baseStyles, data.headCSS].join(' '),
|
|
previewText: data.previewText,
|
|
});
|
|
|
|
try {
|
|
log(`Sending email "${data.title}" to ${data.to}`);
|
|
await transporter.sendMail({
|
|
from: process.env.SMTP_FROM_EMAIL,
|
|
replyTo: process.env.SMTP_REPLY_EMAIL || process.env.SMTP_FROM_EMAIL,
|
|
to: data.to,
|
|
subject: data.title,
|
|
html: html,
|
|
text: data.text,
|
|
attachments: data.attachments,
|
|
});
|
|
} catch (err) {
|
|
bugsnag.notify(err);
|
|
throw err; // Re-throw for queue to re-try
|
|
}
|
|
}
|
|
};
|
|
|
|
welcome = async (opts: { to: string }) => {
|
|
this.sendMail({
|
|
to: opts.to,
|
|
title: 'Welcome to Outline',
|
|
previewText:
|
|
'Outline is a place for your team to build and share knowledge.',
|
|
html: <WelcomeEmail />,
|
|
text: welcomeEmailText,
|
|
});
|
|
};
|
|
|
|
export = async (opts: { to: string, attachments: Object[] }) => {
|
|
this.sendMail({
|
|
to: opts.to,
|
|
attachments: opts.attachments,
|
|
title: 'Your requested export',
|
|
previewText: "Here's your request data export from Outline",
|
|
html: <ExportEmail />,
|
|
text: exportEmailText,
|
|
});
|
|
};
|
|
|
|
constructor() {
|
|
if (process.env.SMTP_HOST) {
|
|
let smtpConfig = {
|
|
host: process.env.SMTP_HOST,
|
|
port: process.env.SMTP_PORT,
|
|
secure: process.env.NODE_ENV === 'production',
|
|
auth: undefined,
|
|
};
|
|
|
|
if (process.env.SMTP_USERNAME) {
|
|
smtpConfig.auth = {
|
|
user: process.env.SMTP_USERNAME,
|
|
pass: process.env.SMTP_PASSWORD,
|
|
};
|
|
}
|
|
|
|
this.transporter = nodemailer.createTransport(smtpConfig);
|
|
}
|
|
}
|
|
}
|
|
|
|
const mailer = new Mailer();
|
|
export const mailerQueue = new Queue('email', process.env.REDIS_URL);
|
|
|
|
mailerQueue.process(async (job: EmailJob) => {
|
|
// $FlowIssue flow doesn't like dynamic values
|
|
await mailer[job.data.type](job.data.opts);
|
|
});
|
|
|
|
export const sendEmail = (type: Emails, to: string, options?: Object = {}) => {
|
|
mailerQueue.add(
|
|
{
|
|
type,
|
|
opts: {
|
|
to,
|
|
...options,
|
|
},
|
|
},
|
|
{
|
|
attempts: 5,
|
|
backoff: {
|
|
type: 'exponential',
|
|
delay: 60 * 1000,
|
|
},
|
|
}
|
|
);
|
|
};
|