Add create and confirm account emails in backend
This commit is contained in:
parent
fd84706104
commit
040b967725
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"domain": "localhost",
|
||||||
"port": 3000,
|
"port": 3000,
|
||||||
"db_engine": "postgres",
|
"db_engine": "postgres",
|
||||||
"sqlite_location": "./database.sqlite",
|
"sqlite_location": "./database.sqlite",
|
||||||
|
@ -11,6 +12,8 @@
|
||||||
"email_port": 465,
|
"email_port": 465,
|
||||||
"email_username": null,
|
"email_username": null,
|
||||||
"email_password": "password",
|
"email_password": "password",
|
||||||
|
"email_from_name": "Readlebee Admin",
|
||||||
|
"email_from_address": null,
|
||||||
"jwtSecretKey": "SomethingAtLeast32CharactersLong!",
|
"jwtSecretKey": "SomethingAtLeast32CharactersLong!",
|
||||||
"tokenExpireDays": 7,
|
"tokenExpireDays": 7,
|
||||||
"inventaireDomain": "https://inventaire.io"
|
"inventaireDomain": "https://inventaire.io"
|
||||||
|
|
|
@ -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) {
|
async emailExists (email) {
|
||||||
const existingUser = await this.model.find({
|
const existingUser = await this.model.find({
|
||||||
attributes: ['id'],
|
attributes: ['id'],
|
||||||
|
@ -109,6 +122,39 @@ class Account {
|
||||||
accountConfirm: needsConfirmation ? crypto.randomBytes(32).toString('hex') : null,
|
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;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ const sequelizeConfig = {
|
||||||
switch (fastify.siteConfig.db_engine) {
|
switch (fastify.siteConfig.db_engine) {
|
||||||
case 'sqlite': {
|
case 'sqlite': {
|
||||||
sequelizeConfig.storage = typeof fastify.siteConfig.sqlite_location !== 'undefined'
|
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');
|
: path.resolve(__dirname, './database.sqlite');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
const Account = require('../controllers/account');
|
const Account = require('../controllers/account');
|
||||||
|
|
||||||
async function routes(fastify, options) {
|
async function routes(fastify, options) {
|
||||||
|
@ -27,20 +29,56 @@ async function routes(fastify, options) {
|
||||||
return reply.code(400).send(canCreateUser);
|
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') {
|
if (typeof newUser.error !== 'undefined') {
|
||||||
return reply.code(400).send(result);
|
return reply.code(400).send(newUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fastify.canEmail) {
|
if (fastify.canEmail) {
|
||||||
// fastify.nodemailer.sendMail();
|
try {
|
||||||
return reply.send({
|
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,
|
error: false,
|
||||||
message: 'api.account_create_success',
|
message: 'api.account_create_success',
|
||||||
});
|
});
|
||||||
|
}
|
||||||
} else {
|
} 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
|
const expireTime = fastify.siteConfig.tokenExpireDays * (24 * 60 * 60e3); // The section in parentheses is milliseconds in a day
|
||||||
|
|
||||||
return reply
|
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) => {
|
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' });
|
reply.view('login.hbs', { text: request.isLoggedInUser ? JSON.stringify(fastify.jwt.decode(request.cookies.token)) : 'you are NOT logged in' });
|
||||||
});
|
});
|
||||||
|
|
|
@ -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}
|
|
@ -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}
|
Loading…
Reference in New Issue