mirror of
https://github.com/nodemailer/wildduck.git
synced 2024-12-27 02:10:52 +08:00
Allow specifying defualt emails for created users
This commit is contained in:
parent
bef736c1b8
commit
12585229a3
7 changed files with 205 additions and 4 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,3 +4,4 @@ npm-debug.log
|
|||
.npmrc
|
||||
config/production.*
|
||||
config/development.*
|
||||
emails/*.json
|
||||
|
|
4
api.js
4
api.js
|
@ -100,9 +100,9 @@ module.exports = done => {
|
|||
database: db.database,
|
||||
redis: db.redis
|
||||
});
|
||||
userHandler = new UserHandler({ database: db.database, users: db.users, redis: db.redis });
|
||||
mailboxHandler = new MailboxHandler({ database: db.database, users: db.users, redis: db.redis, notifier });
|
||||
messageHandler = new MessageHandler({ database: db.database, gridfs: db.gridfs, redis: db.redis });
|
||||
userHandler = new UserHandler({ database: db.database, users: db.users, redis: db.redis, messageHandler });
|
||||
mailboxHandler = new MailboxHandler({ database: db.database, users: db.users, redis: db.redis, notifier });
|
||||
|
||||
usersRoutes(db, server, userHandler);
|
||||
addressesRoutes(db, server);
|
||||
|
|
17
emails/01.json.example
Normal file
17
emails/01.json.example
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"mailbox": "INBOX",
|
||||
"seen": true,
|
||||
"flag": true,
|
||||
|
||||
"from": {
|
||||
"name": "Support",
|
||||
"address": "info@example.com"
|
||||
},
|
||||
"to": {
|
||||
"name": "[NAME]",
|
||||
"address": "[EMAIL]"
|
||||
},
|
||||
"subject": "[FNAME], welcome to our awesome service!",
|
||||
"text": "[FNAME], your new email account [EMAIL] is now ready to be used!",
|
||||
"html": "<p><strong>[FNAME]</strong>, your new email account [EMAIL] is now ready to be used!</p>"
|
||||
}
|
23
emails/README.md
Normal file
23
emails/README.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Default messages
|
||||
|
||||
Add here messages that should be inserted to new users INBOX. Messages are formatted according to [Nodemailer message structure](https://nodemailer.com/message/) and sorted by filename. Only files with .json extension are used.
|
||||
|
||||
All string values can take the following template tags (case sensitive):
|
||||
|
||||
- **[USERNAME]** will be replaced by the username of the user
|
||||
- **[DOMAIN]** will be replaced by the service domain
|
||||
- **[EMAIL]** will be replaced by the email address of the user
|
||||
- **[NAME]** will be replaced by the registered name of the user
|
||||
- **[FNAME]** will be replaced by the first part of the registered name of the user
|
||||
|
||||
You can also specify some extra options with the mail data object
|
||||
|
||||
- **flag** is a boolean. If true, then the message is flagged
|
||||
- **seen** is a boolean. If true, then the message is marked as seen
|
||||
- **mailbox** is a string with one of the following values (case insensitive):
|
||||
- **'INBOX'** (the default) to store the message to INBOX
|
||||
- **'Sent'** to store the message to the Sent Mail folder
|
||||
- **'Trash'** to store the message to the Trash folder
|
||||
- **'Junk'** to store the message to the Spam folder
|
||||
- **'Drafts'** to store the message to the Drafts folder
|
||||
- **'Archive'** to store the message to the Archive folder
|
92
lib/tools.js
92
lib/tools.js
|
@ -3,6 +3,11 @@
|
|||
const punycode = require('punycode');
|
||||
const libmime = require('libmime');
|
||||
const consts = require('./consts');
|
||||
const fs = require('fs');
|
||||
const he = require('he');
|
||||
const pathlib = require('path');
|
||||
|
||||
let templates = false;
|
||||
|
||||
function checkRangeQuery(uids, ne) {
|
||||
// check if uids is a straight continous array and if such then return a range query,
|
||||
|
@ -156,10 +161,95 @@ function getMailboxCounter(db, mailbox, type, done) {
|
|||
});
|
||||
}
|
||||
|
||||
function renderEmailTemplate(tags, template) {
|
||||
let result = JSON.parse(JSON.stringify(template));
|
||||
|
||||
let walk = (node, nodeKey) => {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys(node || {}).forEach(key => {
|
||||
if (!node[key]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(node[key])) {
|
||||
return node[key].forEach(child => walk(child, nodeKey));
|
||||
}
|
||||
|
||||
if (typeof node[key] === 'object') {
|
||||
return walk(node[key], key);
|
||||
}
|
||||
|
||||
if (typeof node[key] === 'string') {
|
||||
let isHTML = /html/i.test(key);
|
||||
node[key] = node[key].replace(/\[([^\]]+)\]/g, (match, tag) => {
|
||||
if (tag in tags) {
|
||||
return isHTML ? he.encode(tags[tag]) : tags[tag];
|
||||
}
|
||||
return match;
|
||||
});
|
||||
return;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
walk(result, false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getEmailTemplates(tags, callback) {
|
||||
if (templates) {
|
||||
return callback(null, templates.map(template => renderEmailTemplate(tags, template)));
|
||||
}
|
||||
let templateFolder = pathlib.join(__dirname, '..', 'emails');
|
||||
fs.readdir(templateFolder, (err, files) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
files = files.sort((a, b) => a.localeCompare(b));
|
||||
|
||||
let pos = 0;
|
||||
let newTemplates = [];
|
||||
let checkFiles = () => {
|
||||
if (pos >= files.length) {
|
||||
templates = newTemplates;
|
||||
return callback(null, templates.map(template => renderEmailTemplate(tags, template)));
|
||||
}
|
||||
let file = files[pos++];
|
||||
if (!/\.json$/i.test(file)) {
|
||||
return checkFiles();
|
||||
}
|
||||
fs.readFile(pathlib.join(templateFolder, file), 'utf-8', (err, email) => {
|
||||
if (err) {
|
||||
// ignore?
|
||||
return checkFiles();
|
||||
}
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(email);
|
||||
} catch (E) {
|
||||
//ignore?
|
||||
}
|
||||
if (parsed) {
|
||||
newTemplates.push(parsed);
|
||||
}
|
||||
return checkFiles();
|
||||
});
|
||||
};
|
||||
|
||||
checkFiles();
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
normalizeAddress,
|
||||
redisConfig,
|
||||
checkRangeQuery,
|
||||
decodeAddresses,
|
||||
getMailboxCounter
|
||||
getMailboxCounter,
|
||||
getEmailTemplates
|
||||
};
|
||||
|
|
|
@ -13,12 +13,14 @@ const os = require('os');
|
|||
const crypto = require('crypto');
|
||||
const mailboxTranslations = require('./translations');
|
||||
const base32 = require('base32.js');
|
||||
const MailComposer = require('nodemailer/lib/mail-composer');
|
||||
|
||||
class UserHandler {
|
||||
constructor(options) {
|
||||
this.database = options.database;
|
||||
this.users = options.users || options.database;
|
||||
this.redis = options.redis;
|
||||
this.messageHandler = options.messageHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -463,7 +465,20 @@ class UserHandler {
|
|||
return callback(new Error('Database Error, failed to create user'));
|
||||
}
|
||||
|
||||
return callback(null, id);
|
||||
if (!this.messageHandler) {
|
||||
return callback(null, id);
|
||||
}
|
||||
|
||||
this.pushDefaultMessages(
|
||||
id,
|
||||
{
|
||||
NAME: data.name || address,
|
||||
FNAME: (data.name || '').trim().replace(/\s+/g, ' ').split(' ').shift() || address,
|
||||
DOMAIN: address.substr(address.indexOf('@') + 1),
|
||||
EMAIL: address
|
||||
},
|
||||
() => callback(null, id)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -471,6 +486,60 @@ class UserHandler {
|
|||
});
|
||||
}
|
||||
|
||||
pushDefaultMessages(user, tags, callback) {
|
||||
tools.getEmailTemplates(tags, (err, messages) => {
|
||||
if (err || !messages || !messages.length) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
let pos = 0;
|
||||
let insertMessages = () => {
|
||||
if (pos >= messages.length) {
|
||||
return callback();
|
||||
}
|
||||
let data = messages[pos++];
|
||||
let compiler = new MailComposer(data);
|
||||
|
||||
compiler.compile().build((err, message) => {
|
||||
if (err) {
|
||||
return insertMessages();
|
||||
}
|
||||
|
||||
let mailboxQueryKey = 'path';
|
||||
let mailboxQueryValue = 'INBOX';
|
||||
|
||||
if (['sent', 'trash', 'junk', 'drafts', 'archive'].includes((data.mailbox || '').toString().toLowerCase())) {
|
||||
mailboxQueryKey = 'specialUse';
|
||||
mailboxQueryValue = '\\' + data.mailbox.toLowerCase().replace(/^./g, c => c.toUpperCase());
|
||||
}
|
||||
|
||||
let flags = [];
|
||||
if (data.seen) {
|
||||
flags.push('\\Seen');
|
||||
}
|
||||
if (data.flag) {
|
||||
flags.push('\\Flagged');
|
||||
}
|
||||
|
||||
this.messageHandler.add(
|
||||
{
|
||||
user,
|
||||
[mailboxQueryKey]: mailboxQueryValue,
|
||||
meta: {
|
||||
source: 'AUTO',
|
||||
time: Date.now()
|
||||
},
|
||||
flags,
|
||||
raw: message
|
||||
},
|
||||
insertMessages
|
||||
);
|
||||
});
|
||||
};
|
||||
insertMessages();
|
||||
});
|
||||
}
|
||||
|
||||
reset(username, callback) {
|
||||
let password = generatePassword.generate({
|
||||
length: 12,
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
"addressparser": "^1.0.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"generate-password": "^1.3.0",
|
||||
"he": "^1.1.1",
|
||||
"html-to-text": "^3.3.0",
|
||||
"iconv-lite": "^0.4.18",
|
||||
"joi": "^10.6.0",
|
||||
|
|
Loading…
Reference in a new issue