#284 nicer email template
This commit is contained in:
parent
7fd9a4e46a
commit
8383bb7482
|
@ -480,11 +480,9 @@ user:
|
|||
emailSent: 'We''ve sent you an email with a 6-digit code. Enter it here. The code is single-use and stays valid for 15 minutes.'
|
||||
userNotFound: 'User not found.'
|
||||
email:
|
||||
subject: 'Your login code is %code%'
|
||||
content: |
|
||||
To confirm your email address, enter the following single-use code on the website: %code%.
|
||||
|
||||
If you didn't order this code, simply ignore this message.
|
||||
subject: 'Your login code is {{code}}'
|
||||
instruction: 'To confirm your email address, enter the following single-use code on the website:'
|
||||
extra: 'If you didn''t order this code, simply ignore this message.'
|
||||
why: >
|
||||
Registering lets you manage your cards ({/@example=like this one}).
|
||||
passwordless: 'The website doesn''t store any passwords. {https://avris.it/blog/passwords-are-passé=More info.}'
|
||||
|
|
|
@ -377,11 +377,9 @@ user:
|
|||
emailSent: 'Wir haben dir eine E-Mail mit einem 6-stelligen Code geschickt. Gib ihn hier ein. Der Code ist einmalig verwendbar und bleibt 15 Minuten lang gültig.'
|
||||
userNotFound: 'Nutzer*in nicht gefunden.'
|
||||
email:
|
||||
subject: 'Dein Logincode ist %code%'
|
||||
content: |
|
||||
Um deine E-Mail-Adresse zu bestätigen, gib den folgenden Code auf der Seite ein: %code%
|
||||
|
||||
Wenn du diesen Code nicht angefordert hast, ignoriere einfach diese Meldung.
|
||||
subject: 'Dein Logincode ist {{code}}'
|
||||
instruction: 'Um deine E-Mail-Adresse zu bestätigen, gib den folgenden Code auf der Seite ein:'
|
||||
extra: 'Wenn du diesen Code nicht angefordert hast, ignoriere einfach diese Meldung.'
|
||||
why: >
|
||||
Mit der Registrierung kannst du deine Visitenkarten verwalten ({/@example=wie diese}).
|
||||
passwordless: 'Die Website speichert keine Passwörter. {https://avris.it/blog/passwords-are-passé=Weitere Infos.}'
|
||||
|
|
|
@ -481,11 +481,9 @@ user:
|
|||
emailSent: 'We''ve sent you an email with a 6-digit code. Enter it here. The code is single-use and stays valid for 15 minutes.'
|
||||
userNotFound: 'User not found.'
|
||||
email:
|
||||
subject: 'Your login code is %code%'
|
||||
content: |
|
||||
To confirm your email address, enter the following single-use code on the website: %code%.
|
||||
|
||||
If you didn't order this code, simply ignore this message.
|
||||
subject: 'Your login code is {{code}}'
|
||||
instruction: 'To confirm your email address, enter the following single-use code on the website:'
|
||||
extra: 'If you didn''t order this code, simply ignore this message.'
|
||||
why: >
|
||||
Registering lets you manage your cards ({/@example=like this one}).
|
||||
passwordless: 'The website doesn''t store any passwords. {https://avris.it/blog/passwords-are-passé=More info.}'
|
||||
|
|
|
@ -390,11 +390,9 @@ user:
|
|||
emailSent: 'Te hemos enviado un correo electrónico con un código de 6 dígitos. Introdúcelo aquí. El código es de un solo uso y es válido por 15 minutos.'
|
||||
userNotFound: 'Usuarie no encontrade.'
|
||||
email:
|
||||
subject: 'Tu código de inicio de sesión es %code%'
|
||||
content: |
|
||||
Para confirmar tu dirección de correo electrónico, introduce este código en el sitio web: %code%.
|
||||
|
||||
Si no solicitaste este código, simplemente ignora este mensaje.
|
||||
subject: 'Tu código de inicio de sesión es {{code}}'
|
||||
instruction: 'Para confirmar tu dirección de correo electrónico, introduce este código en el sitio web:'
|
||||
extra: 'Si no solicitaste este código, simplemente ignora este mensaje.'
|
||||
why: >
|
||||
Registrarte te permite manejar tus tarjetas ({/@example=como esta}).
|
||||
passwordless: 'Este sitio web no guarda las contraseñas. {https://avris.it/blog/passwords-are-passé=More info.}'
|
||||
|
|
|
@ -384,11 +384,9 @@ user:
|
|||
emailSent: 'Nous vous avons envoyé un mail avec un code à 6 chiffres. Entrez-le ici. Ce code est à usage unique et reste utilisable pendant 15 minutes.'
|
||||
userNotFound: 'Cet utilisateur n’existe pas.'
|
||||
email:
|
||||
subject: 'Votre code de connexion est %code%'
|
||||
content: |
|
||||
Pour confirmer votre adresse mail, saisissez ce code sur le site : %code%.
|
||||
|
||||
Si vous n’avez pas demandé ce code, ignorez simplement ce message.
|
||||
subject: 'Votre code de connexion est {{code}}'
|
||||
instruction: 'Pour confirmer votre adresse mail, saisissez ce code sur le site :'
|
||||
extra: 'Si vous n’avez pas demandé ce code, ignorez simplement ce message.'
|
||||
why: >
|
||||
S’inscrire vous permet de gérer vos cartes ({/@example=comme celle-ci}).
|
||||
passwordless: 'Ce site ne stocke aucun mot de passe. {https://avris.it/blog/passwords-are-passé=Plus d’infos.}'
|
||||
|
|
|
@ -386,11 +386,9 @@ user:
|
|||
emailSent: 'Te enviamos um email com um código de 6 dígitos. Digite aqui. O código é pode ser usado apenas uma vez e é válido por 15 minutos.'
|
||||
userNotFound: 'Usuarie não encontrade.'
|
||||
email:
|
||||
subject: 'O código de início da sessão é %code%'
|
||||
content: |
|
||||
Para confirmar seu endereço de email, entre com o seguinte código no site: %code%.
|
||||
|
||||
Se não solicitou este código, simplesmente ignore esta mensagem.
|
||||
subject: 'O código de início da sessão é {{code}}'
|
||||
instruction: 'Para confirmar seu endereço de email, entre com o seguinte código no site:'
|
||||
extra: 'Se não solicitou este código, simplesmente ignore esta mensagem.'
|
||||
why: >
|
||||
Registrar-se te permite dirigir os cartões ({/@example=como esta}).
|
||||
passwordless: 'O site não grava qualquer senha. {https://avris.it/blog/passwords-are-passé=More info.}'
|
||||
|
|
|
@ -396,11 +396,9 @@ user:
|
|||
emailSent: 'メールアドレスに6桁のコードが送信された後、ここに入力してください。コードは一度しか使用できません。有効期限は今から15分です。'
|
||||
userNotFound: '無効なユーザー名'
|
||||
email:
|
||||
subject: 'ログインコードは%code%です。'
|
||||
content: |
|
||||
ウェブサイトでこのコードを入力して、あなたのメールアドレスを確認します。%code%
|
||||
|
||||
このコードは求めなかった場合は、このメッセージを無視してください。
|
||||
subject: 'ログインコードは{{code}}です。'
|
||||
instruction: 'ウェブサイトでこのコードを入力して、あなたのメールアドレスを確認します。'
|
||||
extra: 'このコードは求めなかった場合は、このメッセージを無視してください。'
|
||||
why: >
|
||||
ご登録いただいた方は、カードの設定を行うことができます。({/@example=こんなに}).
|
||||
passwordless: 'このウェブサイトはパスワードを保存しません。 {https://avris.it/blog/passwords-are-passé=詳細はこちら。}'
|
||||
|
|
|
@ -367,11 +367,9 @@ user:
|
|||
emailSent: 'We hebben een email verstuurd met een code bestaande uit 6 getallen. Voer deze code hier in. De code is voor eenmalig gebruik en vervalt na 15 minuten.'
|
||||
userNotFound: 'Gebruiker niet gevonden.'
|
||||
email:
|
||||
subject: 'Jouw logincode is %code%'
|
||||
content: |
|
||||
Voer de volgende code in om jouw e-mailadres te bevestigen: %code%.
|
||||
|
||||
Als je deze code niet hebt aangevraagd, kun je dit bericht gewoon negeren.
|
||||
subject: 'Jouw logincode is {{code}}'
|
||||
instruction: 'Voer de volgende code in om jouw e-mailadres te bevestigen:'
|
||||
extra: 'Als je deze code niet hebt aangevraagd, kun je dit bericht gewoon negeren.'
|
||||
why: >
|
||||
Door te registreren kun je een kaart ({/@example=zoals deze}) maken.
|
||||
passwordless: 'De website slaat geen wachtwoorden op. {https://avris.it/blog/passwords-are-passé=Meer info.}'
|
||||
|
|
|
@ -378,11 +378,9 @@ user:
|
|||
emailSent: 'Vi har sendt deg en email med en 6 sifret kode. Skriv den ned her. Koden er en engangskode og kan brukes i 15 minutter.'
|
||||
userNotFound: 'Bruker ikke funnet.'
|
||||
email:
|
||||
subject: 'Din Logg inn kode er %code%'
|
||||
content: |
|
||||
For å bekrefte din email addresse, vennligst skriv den følgende koden på nettsiden: %code%.
|
||||
|
||||
Hvis du ikke spurte om denne koden, ignorer denne meldingen.
|
||||
subject: 'Din Logg inn kode er {{code}}'
|
||||
instruction: 'For å bekrefte din email addresse, vennligst skriv den følgende koden på nettsiden:'
|
||||
extra: 'Hvis du ikke spurte om denne koden, ignorer denne meldingen.'
|
||||
why: >
|
||||
Å registrere seg lar deg redigere kortene dine ({/@example=sånn som denne}).
|
||||
passwordless: 'Denne nettsiden lagrer ingen passord. {https://avris.it/blog/passwords-are-passé=More info.}'
|
||||
|
|
|
@ -1178,11 +1178,9 @@ user:
|
|||
emailSent: 'Na Twój adres wysłałośmy email z sześciocyfrowym kodem. Wpisz go poniżej. Kod jest jednorazowy i ważny przez 15 minut.'
|
||||
userNotFound: 'Konto nie zostało znalezione.'
|
||||
email:
|
||||
subject: 'Twój kod logowania to %code%'
|
||||
content: |
|
||||
Aby potwierdzić swój adres email, wpisz na stronie następujący jednorazowy kod: %code%.
|
||||
|
||||
Jeśli nie zamawiałxś tego kodu, po prostu zignoruj tę wiadomość.
|
||||
subject: 'Twój kod logowania to {{code}}'
|
||||
instruction: 'Aby potwierdzić swój adres email, wpisz na stronie następujący jednorazowy kod:'
|
||||
extra: 'Jeśli nie zamawiałxś tego kodu, po prostu zignoruj tę wiadomość.'
|
||||
why: >
|
||||
Założenie konta pozwala na zarządzanie swoimi wizytówkami ({/@example=takimi jak ta}).
|
||||
passwordless: 'Strona nie zapisuje żadnych haseł. {https://avris.it/blog/passwords-are-passé=Więcej info.}'
|
||||
|
|
|
@ -386,11 +386,9 @@ user:
|
|||
emailSent: 'Te enviamos um email com um código de 6 dígitos. Digite aqui. O código é pode ser usado apenas uma vez e é válido por 15 minutos.'
|
||||
userNotFound: 'Usuarie não encontrade.'
|
||||
email:
|
||||
subject: 'O código de início da sessão é %code%'
|
||||
content: |
|
||||
Para confirmar seu endereço de email, entre com o seguinte código no site: %code%.
|
||||
|
||||
Se não solicitou este código, simplesmente ignore esta mensagem.
|
||||
subject: 'O código de início da sessão é {{code}}'
|
||||
instruction: 'Para confirmar seu endereço de email, entre com o seguinte código no site:'
|
||||
extra: 'Se não solicitou este código, simplesmente ignore esta mensagem.'
|
||||
why: >
|
||||
Registrar-se te permite dirigir os cartões ({/@example=como esta}).
|
||||
passwordless: 'O site não grava qualquer senha. {https://avris.it/blog/passwords-are-passé=More info.}'
|
||||
|
|
|
@ -367,11 +367,9 @@ user:
|
|||
emailSent: 'Мы отправили на указанную вами почту письмо с шестизначным кодом. Пожалуйста, введите его сюда. Код одноразовый и работает в течение 15-ти минут.'
|
||||
userNotFound: 'Пользователь не найден.'
|
||||
email:
|
||||
subject: 'Ваш код для авторизации: %code%'
|
||||
content: |
|
||||
Введите на сайте этот код для подтверждения электронной почты: %code%.
|
||||
|
||||
Если вы не оформляли запрос на код, то просто проигнорируйте это письмо.
|
||||
subject: 'Ваш код для авторизации: {{code}}'
|
||||
instruction: 'Введите на сайте этот код для подтверждения электронной почты:'
|
||||
extra: 'Если вы не оформляли запрос на код, то просто проигнорируйте это письмо.'
|
||||
why: >
|
||||
Регистрация позволяет вам управлять своими аккаунтами/карточками ({/@excemple=как, например, этой}).
|
||||
passwordless: 'Сайт не хранит пароли. {https://avris.it/blog/passwords-are-passé=Больше информации}'
|
||||
|
|
|
@ -385,12 +385,9 @@ user:
|
|||
emailSent: 'We''ve sent you an email with a 6-digit code. Enter it here. The code is single-use and stays valid for 15 minutes.'
|
||||
userNotFound: 'User not found.'
|
||||
email:
|
||||
subject: 'Your login code is %code%'
|
||||
# TODO change: To confirm your email address, enter the following code on the website: %code%.
|
||||
content: |
|
||||
To confirm your email address, use the following code: %code%.
|
||||
|
||||
If you didn't order this code, simply ignore this message.
|
||||
subject: 'Your login code is {{code}}'
|
||||
instruction: 'To confirm your email address, enter the following single-use code on the website:'
|
||||
extra: 'If you didn''t order this code, simply ignore this message.'
|
||||
why: >
|
||||
Registering lets you manage your cards ({/@example=like this one}).
|
||||
passwordless: 'The website doesn''t store any passwords. {https://avris.it/blog/passwords-are-passé=More info.}'
|
||||
|
|
|
@ -350,12 +350,10 @@ user:
|
|||
emailSent: '我們已經通過電子郵件向您發送了6位數字的代碼。請在這裡輸入。代碼是可以用一次,有效期為15分鐘。'
|
||||
userNotFound: '找不到用戶。'
|
||||
email:
|
||||
subject: '你的登錄代碼是:%code%'
|
||||
subject: '你的登錄代碼是:{{code}}'
|
||||
# TODO change: To confirm your email address, enter the following code on the website: %code%.
|
||||
content: |
|
||||
要確認你的電郵地址,請使用以下代碼: %code%.
|
||||
|
||||
如果你沒有要求這個代碼,請忽略消息。
|
||||
instruction: '要確認你的電郵地址,請使用以下代碼:'
|
||||
extra: '如果你沒有要求這個代碼,請忽略消息。'
|
||||
why: >
|
||||
註冊可以讓你管理你的卡({/@example=像這個})。
|
||||
passwordless: '該網站不存儲任何密碼。 {https://avris.it/blog/passwords-are-passé=更多信息。}'
|
||||
|
|
|
@ -60,14 +60,10 @@ async function notify() {
|
|||
if (!awaitingModerationGrouped.hasOwnProperty(email)) {
|
||||
continue;
|
||||
}
|
||||
const message = awaitingModerationGrouped[email];
|
||||
console.log('Sending email:', email, message);
|
||||
const stats = awaitingModerationGrouped[email];
|
||||
console.log('Sending email:', email, stats);
|
||||
|
||||
mailer(
|
||||
email,
|
||||
'[Pronouns.page] There are entries awaiting moderation',
|
||||
'Entries awaiting moderation: \n' + JSON.stringify(message, null, 4),
|
||||
);
|
||||
mailer(email, 'notify', { stats });
|
||||
}
|
||||
|
||||
await db.close();
|
||||
|
|
|
@ -255,11 +255,7 @@ router.post('/user/init', handleErrorAsync(async (req, res) => {
|
|||
async () => {
|
||||
codeKey = await saveAuthenticator(req.db, 'email', user, payload, 15);
|
||||
|
||||
mailer(
|
||||
payload.email,
|
||||
`[${translations.title}] ${translations.user.login.email.subject.replace('%code%', payload.code)}`,
|
||||
translations.user.login.email.content.replace('%code%', payload.code),
|
||||
)
|
||||
mailer(payload.email, 'confirmCode', { code: payload.code });
|
||||
},
|
||||
async () => {
|
||||
const auth = await findLatestEmailAuthenticator(req.db, payload.email, 'email');
|
||||
|
@ -342,11 +338,7 @@ router.post('/user/change-email', handleErrorAsync(async (req, res) => {
|
|||
|
||||
const authId = await saveAuthenticator(req.db, 'changeEmail', req.user, payload, 15);
|
||||
|
||||
mailer(
|
||||
payload.to,
|
||||
`[${translations.title}] ${translations.user.login.email.subject.replace('%code%', payload.code)}`,
|
||||
translations.user.login.email.content.replace('%code%', payload.code),
|
||||
)
|
||||
mailer(payload.to, 'confirmCode', { code: payload.code });
|
||||
|
||||
return res.json({ authId });
|
||||
}
|
||||
|
|
121
src/mailer.js
121
src/mailer.js
|
@ -1,22 +1,109 @@
|
|||
const mailer = require('mailer');
|
||||
const fs = require('fs');
|
||||
const Suml = require('suml');
|
||||
|
||||
module.exports = (to, subject, body = undefined, html = undefined) => {
|
||||
const color = '#C71585';
|
||||
const logo = fs.readFileSync(__dirname + '/../node_modules/@fortawesome/fontawesome-pro/svgs/light/tags.svg').toString('utf-8');
|
||||
const logoEncoded = 'data:image/svg+xml,' + encodeURIComponent(logo.replace('<path ', `<path fill="${color}" `));
|
||||
|
||||
const loadSuml = name => new Suml().parse(fs.readFileSync(`${__dirname}/../data/${name}.suml`).toString());
|
||||
const translations = loadSuml('translations');
|
||||
|
||||
const sendEmail = (to, subject, body = undefined, html = undefined) => {
|
||||
mailer.send({
|
||||
host: process.env.MAILER_HOST,
|
||||
port: parseInt(process.env.MAILER_PORT),
|
||||
ssl: parseInt(process.env.MAILER_PORT) === 465,
|
||||
authentication: 'login',
|
||||
username: process.env.MAILER_USER,
|
||||
password: process.env.MAILER_PASS,
|
||||
from: process.env.MAILER_FROM,
|
||||
to,
|
||||
subject,
|
||||
body,
|
||||
html,
|
||||
host: process.env.MAILER_HOST,
|
||||
port: parseInt(process.env.MAILER_PORT),
|
||||
ssl: parseInt(process.env.MAILER_PORT) === 465,
|
||||
authentication: 'login',
|
||||
username: process.env.MAILER_USER,
|
||||
password: process.env.MAILER_PASS,
|
||||
from: process.env.MAILER_FROM,
|
||||
to,
|
||||
subject,
|
||||
body,
|
||||
html,
|
||||
},
|
||||
function(err){
|
||||
if (err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const templates = {
|
||||
base: {
|
||||
subject: `[[title]] » {{content}}`,
|
||||
html: `
|
||||
<div style="margin: 36px auto; width: 100%; max-width: 480px; border: 1px solid #aaa;border-radius: 8px;overflow: hidden;font-family: Helvetica, sans-serif;font-size: 16px;">
|
||||
<div style="padding: 16px; padding-top: 10px; background: #f8f8f8; border-bottom: 1px solid #aaa;font-size: 20px;color: ${color};">
|
||||
<img src="${logoEncoded}" style="height: 24px;width: 24px; position: relative; top: 6px; margin-right: 6px;" alt="Logo"/>
|
||||
[[title]]
|
||||
</div>
|
||||
<div style="padding: 8px 16px; background: #fff;">
|
||||
{{content}}
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
},
|
||||
function(err){
|
||||
if (err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
notify: {
|
||||
subject: 'There are entries awaiting moderation',
|
||||
text: 'Entries awaiting moderation:\n\n{{list:stats}}',
|
||||
html: `
|
||||
<p>Entries awaiting moderation</p>
|
||||
<ul>{{list:stats}}</ul>
|
||||
`,
|
||||
},
|
||||
confirmCode: {
|
||||
subject: '[[user.login.email.subject]]',
|
||||
text: `[[user.login.email.instruction]]\n\n{{code}}\n\n[[user.login.email.extra]]`,
|
||||
html: `
|
||||
<p>[[user.login.email.instruction]]</p>
|
||||
<p style="border: 1px solid #aaa;border-radius: 8px;overflow: hidden;text-align: center;user-select: all;font-size: 24px; padding:8px;letter-spacing: 8px; font-weight: bold;">{{code}}</p>
|
||||
<p style="font-size: 12px; color: #777">[[user.login.email.extra]]</p>
|
||||
`,
|
||||
}
|
||||
}
|
||||
|
||||
const applyTemplate = (template, context, params) => {
|
||||
template = templates[template][context];
|
||||
|
||||
if (templates.base[context] !== undefined) {
|
||||
template = templates.base[context].replace('{{content}}', template);
|
||||
}
|
||||
|
||||
template = template.replace(/\[\[([^\]]+)]]/g, m => {
|
||||
let x = translations;
|
||||
for (let part of m.substring(2, m.length - 2).split('.')) {
|
||||
x = x[part];
|
||||
}
|
||||
return x;
|
||||
});
|
||||
|
||||
template = template.replace(/{{([^}]+)}}/g, m => {
|
||||
const key = m.substring(2, m.length - 2);
|
||||
if (key.startsWith('list:')) {
|
||||
const value = params[key.substring(5)];
|
||||
if (Array.isArray(value)) {
|
||||
return context === 'html'
|
||||
? value.map(s => `<li>${s}</li>`).join('')
|
||||
: value.map(s => ` - ${s}`).join('\n');
|
||||
} else {
|
||||
return context === 'html'
|
||||
? Object.keys(value).map(s => `<li><strong>${s}:</strong> ${value[s]}</li>`).join('')
|
||||
: Object.keys(value).map(s => ` - ${s}: ${value[s]}`).join('\n');
|
||||
}
|
||||
}
|
||||
return params[key];
|
||||
});
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
module.exports = (to, template, params = {}) => {
|
||||
sendEmail(
|
||||
to,
|
||||
applyTemplate(template, 'subject', params),
|
||||
applyTemplate(template, 'text', params),
|
||||
applyTemplate(template, 'html', params),
|
||||
);
|
||||
};
|
||||
|
|
Reference in New Issue