Add create and confirm account emails in backend

This commit is contained in:
Robbie Antenesse 2019-10-03 13:04:17 -06:00
parent fd84706104
commit 040b967725
6 changed files with 176 additions and 7 deletions

View File

@ -1,4 +1,5 @@
{
"domain": "localhost",
"port": 3000,
"db_engine": "postgres",
"sqlite_location": "./database.sqlite",
@ -11,6 +12,8 @@
"email_port": 465,
"email_username": null,
"email_password": "password",
"email_from_name": "Readlebee Admin",
"email_from_address": null,
"jwtSecretKey": "SomethingAtLeast32CharactersLong!",
"tokenExpireDays": 7,
"inventaireDomain": "https://inventaire.io"

View File

@ -58,6 +58,19 @@ class Account {
}
}
static confirmAccountDataIsValid(createAccountData) {
if (typeof createAccountData.id === 'undefined'
|| typeof createAccountData.confirm === 'undefined'
|| !createAccountData.confirm) {
return {
error: true,
message: 'api.account_confirm_required_data_missing',
};
}
return true;
}
async emailExists (email) {
const existingUser = await this.model.find({
attributes: ['id'],
@ -109,6 +122,39 @@ class Account {
accountConfirm: needsConfirmation ? crypto.randomBytes(32).toString('hex') : null,
});
}
async confirmUser (id, accountConfirm) {
const userToConfirm = await this.model.findOne({
where: {
id,
accountConfirm,
},
});
if (!userToConfirm) {
return {
error: true,
message: 'api.account_confirm_invalid_code',
}
}
return await this.model.update({
accountConfirm: null,
}, {
where: {
id,
accountConfirm,
},
}).then(success => {
if (success[0] < 1) {
return {
error: true,
message: 'api.account_confirm_update_fail',
}
}
return userToConfirm;
});
}
}

View File

@ -33,7 +33,7 @@ const sequelizeConfig = {
switch (fastify.siteConfig.db_engine) {
case 'sqlite': {
sequelizeConfig.storage = typeof fastify.siteConfig.sqlite_location !== 'undefined'
? path.resolve(fastify.siteConfig.sqlite_location)
? path.resolve(__dirname, fastify.siteConfig.sqlite_location)
: path.resolve(__dirname, './database.sqlite');
break;
}

View File

@ -1,3 +1,5 @@
const fs = require('fs');
const path = require('path');
const Account = require('../controllers/account');
async function routes(fastify, options) {
@ -27,20 +29,56 @@ async function routes(fastify, options) {
return reply.code(400).send(canCreateUser);
}
const result = await account.createUser(formData.email, formData.username, formData.displayName, formData.password, fastify.canEmail);
const newUser = await account.createUser(formData.email, formData.username, formData.displayName, formData.password, fastify.canEmail);
if (typeof result.error !== 'undefined') {
return reply.code(400).send(result);
if (typeof newUser.error !== 'undefined') {
return reply.code(400).send(newUser);
}
if (fastify.canEmail) {
// fastify.nodemailer.sendMail();
return reply.send({
try {
const file = fs.readFileSync(path.resolve(__dirname, '../templates/email.confirm_account.txt'));
console.log(file.toString());
const text = file.toString()
.replace(/\{display_name\}/g, newUser.displayName)
.replace(/\{username\}/g, newUser.username)
.replace(/\{domain\}/g, fastify.siteConfig.domain)
.replace(/\{id\}/g, newUser.id)
.replace(/\{code\}/g, newUser.accountConfirm)
.replace(/\{sender\}/g, fastify.siteConfig.email_from_name);
return fastify.nodemailer.sendMail({
// Default to email_username if email_from_address is null/falsy
from: `"${fastify.siteConfig.email_from_name}" ${!fastify.siteConfig.email_from_address ? fastify.siteConfig.email_username : fastify.siteConfig.email_from_address}`,
to: `"${newUser.displayName}" ${newUser.email}`,
subject: 'Please Confirm your Account',
text,
// Definitely gonna have to wait to design the HTML version of the email!
// html: '<p>HTML version of the message</p>',
}).then(email => {
if (email.err) {
console.error(email.err);
return reply.send({
error: true,
message: 'api.account_email_send_fail',
newUser,
});
}
return reply.send({
error: false,
message: 'api.account_confirm_email',
});
})
} catch (ex) {
console.error(ex);
return reply.send({
error: false,
message: 'api.account_create_success',
});
}
} else {
const token = fastify.jwt.sign({ id: result.id });
const token = fastify.jwt.sign({ id: newUser.id });
const expireTime = fastify.siteConfig.tokenExpireDays * (24 * 60 * 60e3); // The section in parentheses is milliseconds in a day
return reply
@ -58,6 +96,68 @@ async function routes(fastify, options) {
}
});
fastify.post('/api/account/confirm', async (request, reply) => {
if (request.isLoggedInUser) {
return reply.code(400).send({
error: true,
message: 'api.account_already_logged_in',
});
}
const formDataIsValid = Account.confirmAccountDataIsValid(request.body);
if (formDataIsValid !== true) {
return reply.code(400).send(formDataIsValid);
}
const account = new Account(fastify.models.User);
const confirmed = await account.confirmUser(request.body.id, request.body.confirm);
if (typeof confirmed.error !== 'undefined') {
return reply.code(400).send(confirmed);
}
// Expects email to be working, and indeed it should be working because that's how the confirmation code was sent.
try {
const file = fs.readFileSync(path.resolve(__dirname, '../templates/email.confirm_account_thanks.txt'));
console.log(file.toString());
const text = file.toString()
.replace(/\{display_name\}/g, confirmed.displayName)
.replace(/\{username\}/g, confirmed.username)
.replace(/\{domain\}/g, fastify.siteConfig.domain)
.replace(/\{sender\}/g, fastify.siteConfig.email_from_name);
return fastify.nodemailer.sendMail({
// Default to email_username if email_from_address is null/falsy
from: `"${fastify.siteConfig.email_from_name}" ${!fastify.siteConfig.email_from_address ? fastify.siteConfig.email_username : fastify.siteConfig.email_from_address}`,
to: `"${confirmed.displayName}" ${confirmed.email}`,
subject: 'Account Confirmed Successfully',
text,
// Definitely gonna have to wait to design the HTML version of the email!
// html: '<p>HTML version of the message</p>',
}).then(email => {
if (email.err) {
console.error(email.err);
return reply.send({
error: true,
message: 'api.account_confirm_email_send_fail',
});
}
return reply.send({
error: false,
message: 'api.account_confirm_success_email',
});
})
} catch (ex) {
console.error(ex);
return reply.send({
error: false,
message: 'api.account_confirm_success',
});
}
});
fastify.get('/api/login', async (request, reply) => {
reply.view('login.hbs', { text: request.isLoggedInUser ? JSON.stringify(fastify.jwt.decode(request.cookies.token)) : 'you are NOT logged in' });
});

View File

@ -0,0 +1,10 @@
Hello {display_name} (username: {username}),
Someone used this email address to create an account at {domain}.
If this was you, please confirm your account and log in using the link below:
{domain}/login?id={id}&confirm={code}
Thanks!
{sender}

View File

@ -0,0 +1,10 @@
Hello {display_name} (username: {username}),
You have successfully confirmed your account on {domain}.
You may now log in using your email address and password at the following address:
{domain}/login
Thanks!
{sender}