2020-01-02 16:58:06 +00:00
/ *
* functions for mail verify
* @ flow
* /
// must use require for arguments
2020-01-04 06:00:47 +00:00
import Sequelize from 'sequelize' ;
2020-01-02 16:58:06 +00:00
import logger from './logger' ;
import { HOUR , MINUTE } from './constants' ;
import { HOSTURL } from './config' ;
import { DailyCron , HourlyCron } from '../utils/cron' ;
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, \n welcome to our little community of pixelplacers, to use your account, you have to verify your mail. You can do that here: \n ${ verify _url } \n Have fun and don't hesitate to contact us if you encouter any problems :) \n Thanks ` ,
} , ( 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, \n You requested to get a new password. You can change your password within the next 30min here: \n ${ restore _url } \n Have fun and don't hesitate to contact us if you encouter any problems :) \n If you did not request this mail, please just ignore it (the ip that requested this mail was ${ ip } ). \n Thanks ` ,
} , ( 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 ;