shadowsocks-manager
Version:
A shadowsocks manager tool for multi user and traffic control.
248 lines (214 loc) • 6.46 kB
JavaScript
;
const log4js = require('log4js');
const logger = log4js.getLogger('email');
const nodemailer = require('nodemailer');
const rp = require('request-promise');
const config = appRequire('services/config').all();
const knex = appRequire('init/knex').knex;
const isInBlackList = appRequire('plugins/email/blackList').isInBlackList;
let emailConfig;
let transporter;
if (!config.plugins.email.type) {
config.plugins.email.type = 'smtp';
}
if (config.plugins.email.type === 'smtp') {
emailConfig = {
host: config.plugins.email.host,
port: config.plugins.email.port || 465,
secure: config.plugins.email.hasOwnProperty('secure') ? config.plugins.email.secure : true,
auth: {
user: config.plugins.email.username,
pass: config.plugins.email.password
},
tls: {
rejectUnauthorized: !config.plugins.email.allowUnauthorizedTls
},
proxy: config.plugins.email.proxy || ''
};
transporter = nodemailer.createTransport(emailConfig);
if (config.plugins.email.proxy && config.plugins.email.proxy.indexOf('socks') >= 0) {
transporter.set('proxy_socks_module', require('socks'));
}
} else if (config.plugins.email.type === 'mailgun') {
emailConfig = {
baseUrl: config.plugins.email.baseUrl,
apiKey: config.plugins.email.apiKey
};
config.plugins.email.email = 'mailgun@' + emailConfig.baseUrl.split('/').slice(-1);
const uri = 'https://api:' + emailConfig.apiKey + '@' + emailConfig.baseUrl.split('https://')[1] + '/messages';
transporter = {};
transporter.sendMail = (options, cb) => {
rp({
uri,
method: 'POST',
form: {
from: options.from,
to: options.to,
subject: options.subject,
text: options.text
}
}).then(success => {
cb(null);
}).catch(err => {
cb(err);
});
};
} else if (config.plugins.email.type === 'sendgrid') {
emailConfig = {
apiKey: config.plugins.email.apiKey
};
const uri = 'https://api.sendgrid.com/v3/mail/send';
transporter = {};
transporter.sendMail = (options, cb) => {
rp({
uri,
method: 'POST',
json: true,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${emailConfig.apiKey}`
},
body: {
personalizations: [{
to: [{
email: options.to
}]
}],
from: {
email: options.from
},
subject: options.subject,
content: [{
type: 'text/plain',
value: options.text
}]
}
}).then(success => {
cb(null);
}).catch(err => {
cb(err);
});
};
}
const sendMail = async (to, subject, text, options = {}) => {
if (isInBlackList(to)) {
logger.error('Email in black list: ' + to);
return Promise.reject('email in black list');
}
const send = (to, subject, text) => {
return new Promise((resolve, reject) => {
transporter.sendMail({
from: `"${config.plugins.email.name || ''}" <${config.plugins.email.email || config.plugins.email.username}>`,
to,
subject,
text
}, (error, info) => {
if (error) {
return reject(error);
}
return resolve(info);
});
});
};
const checkLimit = async (ip = '', session = '') => {
let ipNumber = await knex('email').where({
ip
}).whereBetween('time', [Date.now() - 3600 * 1000, Date.now()]).count('time as count').then(success => success[0].count);
let sessionNumber = await knex('email').where({
session
}).whereBetween('time', [Date.now() - 3600 * 1000, Date.now()]).count('time as count').then(success => success[0].count);
if (ip === '127.0.0.1' || !ip) {
ipNumber = 0;
}
if (!session) {
sessionNumber = 0;
}
return ipNumber + sessionNumber;
};
const number = await checkLimit(options.ip, options.session);
if (number >= 40) {
return Promise.reject('send email out of limit');
}
await send(to, subject, text);
await knex('email').insert({
to,
subject,
text,
type: options.type,
remark: options.remark,
ip: options.ip,
session: options.session,
telegramId: options.telegramId,
time: Date.now()
});
return;
};
const sendCode = async (to, subject = 'subject', text, options = {}) => {
const sendEmailTime = 10;
try {
const findEmail = await knex('email').select(['remark']).where({
to,
type: 'code'
}).whereBetween('time', [Date.now() - sendEmailTime * 60 * 1000, Date.now()]);
if (findEmail.length > 0) {
return findEmail[0].remark;
}
const code = Math.random().toString().substr(2, 6);
if (text.indexOf('${code}') >= 0) {
text = text.replace(/\$\{code\}/g, '[ ' + code + ' ]');
} else {
text += '\n[ ' + code + ' ]';
}
await sendMail(to, subject, text, {
type: 'code',
remark: code,
ip: options.ip,
session: options.session,
telegramId: options.telegramId
});
logger.info(`[${to}] Send code: ${code}`);
return code;
} catch (err) {
logger.error(`Send code fail: ${err}`);
return Promise.reject(err);
}
};
const checkCode = async (email, code) => {
logger.info(`[${email}] Check code: ${code}`);
const sendEmailTime = 10;
try {
const findEmail = await knex('email').select(['remark']).where({
to: email,
remark: code,
type: 'code'
}).whereBetween('time', [Date.now() - sendEmailTime * 60 * 1000, Date.now()]);
if (findEmail.length === 0) {
throw new Error('Email or code not found');
}
} catch (err) {
logger.error(`Check code fail: ${err}`);
return Promise.reject(err);
}
};
const checkCodeFromTelegram = async (telegramId, code) => {
logger.info(`Telegram[${telegramId}] Check code: ${code}`);
const sendEmailTime = 10;
try {
const findEmail = await knex('email').where({
telegramId,
remark: code,
type: 'code'
}).whereBetween('time', [Date.now() - sendEmailTime * 60 * 1000, Date.now()]);
if (findEmail.length === 0) {
throw new Error('Email or code not found');
}
return findEmail[0];
} catch (err) {
logger.error(`Check code fail: ${err}`);
return Promise.reject(err);
}
};
exports.checkCodeFromTelegram = checkCodeFromTelegram;
exports.checkCode = checkCode;
exports.sendCode = sendCode;
exports.sendMail = sendMail;