177 lines
5.5 KiB
JavaScript
177 lines
5.5 KiB
JavaScript
|
/*
|
||
|
* functions for mail verify
|
||
|
* @flow
|
||
|
*/
|
||
|
|
||
|
// must use require for arguments
|
||
|
import logger from './logger';
|
||
|
|
||
|
import { HOUR, MINUTE } from './constants';
|
||
|
import { HOSTURL } from './config';
|
||
|
import { DailyCron, HourlyCron } from '../utils/cron';
|
||
|
|
||
|
import Sequelize from 'sequelize';
|
||
|
import RegUser from '../data/models/RegUser';
|
||
|
|
||
|
const sendmail = require('sendmail')({ silent: true });
|
||
|
|
||
|
|
||
|
// TODO make code expire
|
||
|
class MailProvider {
|
||
|
verify_codes: Object;
|
||
|
|
||
|
constructor() {
|
||
|
this.clear_codes = this.clear_codes.bind(this);
|
||
|
|
||
|
this.verify_codes = {};
|
||
|
HourlyCron.hook(this.clear_codes);
|
||
|
DailyCron.hook(MailProvider.clean_users);
|
||
|
}
|
||
|
|
||
|
send_verify_mail(to, name) {
|
||
|
const past_mail = this.verify_codes[to];
|
||
|
if (past_mail) {
|
||
|
const min_left = Math.floor(past_mail.timestamp / MINUTE + 15 - Date.now() / MINUTE);
|
||
|
if (min_left > 0) {
|
||
|
logger.info(`Verify mail for ${to} - already sent, ${min_left} minutes left`);
|
||
|
return `We already sent you a verification mail, you can request another one in ${min_left} minutes.`;
|
||
|
}
|
||
|
}
|
||
|
logger.info(`Sending verification mail to ${to} / ${name}`);
|
||
|
const code = this.set_code(to);
|
||
|
const verify_url = `${HOSTURL}/api/auth/verify?token=${code}`;
|
||
|
sendmail({
|
||
|
from: 'donotreply@pixelplanet.fun',
|
||
|
to,
|
||
|
replyTo: 'donotreply@pixelplanet.fun',
|
||
|
subject: `Welcome ${name} to PixelPlanet, plese verify your mail`,
|
||
|
text: `Hello,\nwelcome to our little community of pixelplacers, to use your account, you have to verify your mail. You can do that here:\n ${verify_url} \nHave fun and don't hesitate to contact us if you encouter any problems :)\nThanks`,
|
||
|
}, (err, reply) => {
|
||
|
if (err) {
|
||
|
logger.error(err & err.stack);
|
||
|
}
|
||
|
});
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
async send_passd_reset_mail(to, ip) {
|
||
|
const past_mail = this.verify_codes[to];
|
||
|
if (past_mail) {
|
||
|
if (Date.now() < past_mail.timestamp + 15 * MINUTE) {
|
||
|
logger.info(`Password reset mail for ${to} requested by ${ip} - already sent`);
|
||
|
return 'We already sent you a mail with instructions. Please wait before requesting another mail.';
|
||
|
}
|
||
|
}
|
||
|
const reguser = await RegUser.findOne({ where: { email: to } });
|
||
|
if (past_mail || !reguser) {
|
||
|
logger.info(`Password reset mail for ${to} requested by ${ip} - mail not found`);
|
||
|
return "Couldn't find this mail in our database";
|
||
|
}
|
||
|
/*
|
||
|
* not sure if this is needed yet
|
||
|
* does it matter if spaming password reset mails or verifications mails?
|
||
|
*
|
||
|
if(!reguser.verified) {
|
||
|
logger.info(`Password reset mail for ${to} requested by ${ip} - mail not verified`);
|
||
|
return "Can't reset password of unverified account.";
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
logger.info(`Sending Password reset mail to ${to}`);
|
||
|
const code = this.set_code(to);
|
||
|
const restore_url = `${HOSTURL}/reset_password?token=${code}`;
|
||
|
sendmail({
|
||
|
from: 'donotreply@pixelplanet.fun',
|
||
|
to,
|
||
|
replyTo: 'donotreply@pixelplanet.fun',
|
||
|
subject: 'You forgot your password for PixelPlanet? Get a new one here',
|
||
|
text: `Hello,\nYou requested to get a new password. You can change your password within the next 30min here:\n ${restore_url} \nHave fun and don't hesitate to contact us if you encouter any problems :)\nIf you did not request this mail, please just ignore it (the ip that requested this mail was ${ip}).\nThanks`,
|
||
|
}, (err, reply) => {
|
||
|
if (err) {
|
||
|
logger.error(err & err.stack);
|
||
|
}
|
||
|
});
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
set_code(email) {
|
||
|
const code = MailProvider.create_code();
|
||
|
this.verify_codes[email] = {
|
||
|
code,
|
||
|
timestamp: Date.now(),
|
||
|
};
|
||
|
return code;
|
||
|
}
|
||
|
|
||
|
async clear_codes() {
|
||
|
const cur_time = Date.now();
|
||
|
const to_delete = [];
|
||
|
for (const iteremail in this.verify_codes) {
|
||
|
if (cur_time > this.verify_codes[iteremail].timestamp + HOUR) {
|
||
|
to_delete.push(iteremail);
|
||
|
}
|
||
|
}
|
||
|
to_delete.forEach((email) => {
|
||
|
logger.info(`Mail Code for ${email} expired`);
|
||
|
delete this.verify_codes[email];
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// Note: code gets deleted on check
|
||
|
check_code(code) {
|
||
|
let email = null;
|
||
|
for (const iteremail in this.verify_codes) {
|
||
|
if (this.verify_codes[iteremail].code == code) {
|
||
|
email = iteremail;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!email) {
|
||
|
logger.info(`Mail Code ${code} not found.`);
|
||
|
return false;
|
||
|
}
|
||
|
logger.info(`Got Mail Code from ${email}.`);
|
||
|
delete this.verify_codes[email];
|
||
|
return email;
|
||
|
}
|
||
|
|
||
|
async verify(code) {
|
||
|
const email = this.check_code(code);
|
||
|
if (!email) return false;
|
||
|
|
||
|
const reguser = await RegUser.findOne({ where: { email } });
|
||
|
if (!reguser) {
|
||
|
logger.error(`${email} does not exist in database`);
|
||
|
return false;
|
||
|
}
|
||
|
await reguser.update({
|
||
|
mailVerified: true,
|
||
|
verificationReqAt: null,
|
||
|
});
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static create_code() {
|
||
|
const part1 = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
|
||
|
const part2 = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
|
||
|
return `${part1}-${part2}`;
|
||
|
}
|
||
|
|
||
|
static clean_users() {
|
||
|
// delete users that requier verification for more than 4 days
|
||
|
RegUser.destroy({
|
||
|
where: {
|
||
|
verificationReqAt: {
|
||
|
[Sequelize.Op.lt]: Sequelize.literal('CURRENT_TIMESTAMP - INTERVAL 4 DAY'),
|
||
|
},
|
||
|
// NOTE: this means that minecraft verified accounts do not get deleted
|
||
|
verified: 0,
|
||
|
},
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export const mailProvider = new MailProvider();
|
||
|
|
||
|
export default mailProvider;
|