mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 21:00:31 +00:00
fix: issues in authentication mail messages.
This commit is contained in:
@@ -76,14 +76,15 @@ export default class AuthenticationController extends BaseController{
|
||||
*/
|
||||
get resetPasswordSchema(): ValidationChain[] {
|
||||
return [
|
||||
check('password').exists().isLength({ min: 5 }).custom((value, { req }) => {
|
||||
if (value !== req.body.confirm_password) {
|
||||
throw new Error("Passwords don't match");
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}),
|
||||
]
|
||||
check('password').exists().isLength({ min: 5 })
|
||||
.custom((value, { req }) => {
|
||||
if (value !== req.body.confirm_password) {
|
||||
throw new Error("Passwords don't match");
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,7 +112,7 @@ export default class AuthenticationController extends BaseController{
|
||||
return res.status(200).send({ token, user });
|
||||
} catch (error) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'invalid_details') {
|
||||
if (['invalid_details', 'invalid_password'].indexOf(error.errorType) !== -1) {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'INVALID_DETAILS', code: 100 }],
|
||||
});
|
||||
@@ -201,8 +202,6 @@ export default class AuthenticationController extends BaseController{
|
||||
type: 'RESET_PASSWORD_SUCCESS',
|
||||
})
|
||||
} catch(error) {
|
||||
console.log(error);
|
||||
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'token_invalid') {
|
||||
return res.boom.badRequest(null, {
|
||||
|
||||
@@ -2,8 +2,17 @@ import { Container, Inject } from 'typedi';
|
||||
import AuthenticationService from '@/services/Authentication';
|
||||
|
||||
export default class WelcomeEmailJob {
|
||||
@Inject()
|
||||
authService: AuthenticationService;
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {Agenda} agenda
|
||||
*/
|
||||
constructor(agenda) {
|
||||
agenda.define(
|
||||
'reset-password-mail',
|
||||
{ priority: 'high' },
|
||||
this.handler.bind(this),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle send welcome mail job.
|
||||
@@ -11,17 +20,18 @@ export default class WelcomeEmailJob {
|
||||
* @param {Function} done
|
||||
*/
|
||||
public async handler(job, done: Function): Promise<void> {
|
||||
const { email, organizationName, firstName } = job.attrs.data;
|
||||
const { user, token } = job.attrs.data;
|
||||
const Logger = Container.get('logger');
|
||||
const authService = Container.get(AuthenticationService);
|
||||
|
||||
Logger.info(`Send reset password mail - started: ${job.attrs.data}`);
|
||||
Logger.info(`[send_reset_password] started: ${job.attrs.data}`);
|
||||
|
||||
try {
|
||||
await this.authService.mailMessages.sendResetPasswordMessage();
|
||||
Logger.info(`Send reset password mail - finished: ${job.attrs.data}`);
|
||||
await authService.mailMessages.sendResetPasswordMessage(user, token);
|
||||
Logger.info(`[send_reset_password] finished: ${job.attrs.data}`);
|
||||
done()
|
||||
} catch (error) {
|
||||
Logger.info(`Send reset password mail - error: ${job.attrs.data}, error: ${error}`);
|
||||
Logger.info(`[send_reset_password] error: ${job.attrs.data}, error: ${error}`);
|
||||
done(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,18 @@ import { Container, Inject } from 'typedi';
|
||||
import AuthenticationService from '@/services/Authentication';
|
||||
|
||||
export default class WelcomeEmailJob {
|
||||
@Inject()
|
||||
authService: AuthenticationService;
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {Agenda} agenda -
|
||||
*/
|
||||
constructor(agenda) {
|
||||
// Welcome mail and SMS message.
|
||||
agenda.define(
|
||||
'welcome-email',
|
||||
{ priority: 'high' },
|
||||
this.handler.bind(this),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle send welcome mail job.
|
||||
@@ -11,17 +21,18 @@ export default class WelcomeEmailJob {
|
||||
* @param {Function} done
|
||||
*/
|
||||
public async handler(job, done: Function): Promise<void> {
|
||||
const { email, organizationName, firstName } = job.attrs.data;
|
||||
const { organizationName, user } = job.attrs.data;
|
||||
const Logger = Container.get('logger');
|
||||
const authService = Container.get(AuthenticationService);
|
||||
|
||||
Logger.info(`[welcome_mail] send welcome mail message - started: ${job.attrs.data}`);
|
||||
|
||||
Logger.info(`Send welcome mail message - started: ${job.attrs.data}`);
|
||||
|
||||
try {
|
||||
await this.authService.mailMessages.sendWelcomeMessage();
|
||||
Logger.info(`Send welcome mail message - finished: ${job.attrs.data}`);
|
||||
done()
|
||||
await authService.mailMessages.sendWelcomeMessage(user, organizationName);
|
||||
Logger.info(`[welcome_mail] send welcome mail message - finished: ${job.attrs.data}`);
|
||||
done();
|
||||
} catch (error) {
|
||||
Logger.info(`Send welcome mail message - error: ${job.attrs.data}, error: ${error}`);
|
||||
Logger.info(`[welcome_mail] send welcome mail message - error: ${job.attrs.data}, error: ${error}`);
|
||||
done(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,23 +13,15 @@ import SendMailNotificationTrialEnd from '@/jobs/MailNotificationTrialEnd';
|
||||
import UserInviteMailJob from '@/jobs/UserInviteMail';
|
||||
|
||||
export default ({ agenda }: { agenda: Agenda }) => {
|
||||
// Welcome mail and SMS message.
|
||||
agenda.define(
|
||||
'welcome-email',
|
||||
{ priority: 'high' },
|
||||
new WelcomeEmailJob().handler,
|
||||
);
|
||||
new WelcomeEmailJob(agenda);
|
||||
new ResetPasswordMailJob(agenda);
|
||||
|
||||
agenda.define(
|
||||
'welcome-sms',
|
||||
{ priority: 'high' },
|
||||
new WelcomeSMSJob().handler
|
||||
);
|
||||
// Reset password mail.
|
||||
agenda.define(
|
||||
'reset-password-mail',
|
||||
{ priority: 'high' },
|
||||
new ResetPasswordMailJob().handler,
|
||||
);
|
||||
|
||||
// User invite mail.
|
||||
agenda.define(
|
||||
'user-invite-mail',
|
||||
|
||||
@@ -1,35 +1,78 @@
|
||||
import { Service } from "typedi";
|
||||
import fs from 'fs';
|
||||
import { Service, Container } from "typedi";
|
||||
import Mustache from 'mustache';
|
||||
import path from 'path';
|
||||
import { ISystemUser } from '@/interfaces';
|
||||
import config from '@/../config/config';
|
||||
import { ISystemUser } from 'src/interfaces';
|
||||
|
||||
@Service()
|
||||
export default class AuthenticationMailMesssages {
|
||||
|
||||
sendWelcomeMessage() {
|
||||
const Logger = Container.get('logger');
|
||||
/**
|
||||
* Sends welcome message.
|
||||
* @param {ISystemUser} user - The system user.
|
||||
* @param {string} organizationName -
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
sendWelcomeMessage(user: ISystemUser, organizationName: string): Promise<void> {
|
||||
const Mail = Container.get('mail');
|
||||
|
||||
|
||||
const filePath = path.join(global.rootPath, 'views/mail/Welcome.html');
|
||||
const template = fs.readFileSync(filePath, 'utf8');
|
||||
const rendered = Mustache.render(template, {
|
||||
email, organizationName, firstName,
|
||||
email: user.email,
|
||||
firstName: user.firstName,
|
||||
organizationName,
|
||||
});
|
||||
const mailOptions = {
|
||||
to: email,
|
||||
to: user.email,
|
||||
from: `${process.env.MAIL_FROM_NAME} ${process.env.MAIL_FROM_ADDRESS}`,
|
||||
subject: 'Welcome to Bigcapital',
|
||||
html: rendered,
|
||||
};
|
||||
Mail.sendMail(mailOptions, (error) => {
|
||||
if (error) {
|
||||
Logger.error('Failed send welcome mail', { error, form });
|
||||
done(error);
|
||||
return;
|
||||
}
|
||||
Logger.info('User has been sent welcome email successfuly.', { form });
|
||||
done();
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
Mail.sendMail(mailOptions, (error) => {
|
||||
if (error) {
|
||||
resolve(error);
|
||||
return;
|
||||
}
|
||||
reject();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
sendResetPasswordMessage() {
|
||||
/**
|
||||
* Sends reset password message.
|
||||
*
|
||||
* @param {ISystemUser} user - The system user.
|
||||
* @param {string} token - Reset password token.
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
sendResetPasswordMessage(user: ISystemUser, token: string): Promise<void> {
|
||||
const Mail = Container.get('mail');
|
||||
|
||||
const filePath = path.join(global.rootPath, 'views/mail/ResetPassword.html');
|
||||
const template = fs.readFileSync(filePath, 'utf8');
|
||||
const rendered = Mustache.render(template, {
|
||||
resetPasswordUrl: `${config.baseURL}/reset/${token}`,
|
||||
first_name: user.firstName,
|
||||
last_name: user.lastName,
|
||||
contact_us_email: config.contactUsMail,
|
||||
});
|
||||
const mailOptions = {
|
||||
to: user.email,
|
||||
from: `${process.env.MAIL_FROM_NAME} ${process.env.MAIL_FROM_ADDRESS}`,
|
||||
subject: 'Bigcapital - Password Reset',
|
||||
html: rendered,
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
Mail.sendMail(mailOptions, (error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ export default class AuthenticationService {
|
||||
|
||||
this.logger.info('[login] check password validation.');
|
||||
if (!user.verifyPassword(password)) {
|
||||
throw new ServiceError('password_invalid');
|
||||
throw new ServiceError('invalid_password');
|
||||
}
|
||||
|
||||
if (!user.active) {
|
||||
@@ -133,8 +133,10 @@ export default class AuthenticationService {
|
||||
tenant_id: tenant.id,
|
||||
});
|
||||
|
||||
this.eventDispatcher.dispatch(events.auth.register, { registerDTO });
|
||||
|
||||
// Triggers `onRegister` event.
|
||||
this.eventDispatcher.dispatch(events.auth.register, {
|
||||
registerDTO, user: registeredUser
|
||||
});
|
||||
return registeredUser;
|
||||
}
|
||||
|
||||
@@ -194,9 +196,12 @@ export default class AuthenticationService {
|
||||
await this.validateEmailExistance(email);
|
||||
|
||||
// Delete all stored tokens of reset password that associate to the give email.
|
||||
this.logger.info('[send_reset_password] trying to delete all tokens by email.');
|
||||
await PasswordReset.query().where('email', email).delete();
|
||||
|
||||
const token = uniqid();
|
||||
|
||||
this.logger.info('[send_reset_password] insert the generated token.');
|
||||
const passwordReset = await PasswordReset.query().insert({ email, token });
|
||||
const user = await SystemUser.query().findOne('email', email);
|
||||
|
||||
|
||||
@@ -2,7 +2,11 @@ import { Service } from "typedi";
|
||||
|
||||
@Service()
|
||||
export default class SubscriptionMailMessages {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param phoneNumber
|
||||
* @param remainingDays
|
||||
*/
|
||||
public async sendRemainingSubscriptionPeriod(phoneNumber: string, remainingDays: number) {
|
||||
const message: string = `
|
||||
Your remaining subscription is ${remainingDays} days,
|
||||
@@ -11,6 +15,11 @@ export default class SubscriptionMailMessages {
|
||||
this.smsClient.sendMessage(phoneNumber, message);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param phoneNumber
|
||||
* @param remainingDays
|
||||
*/
|
||||
public async sendRemainingTrialPeriod(phoneNumber: string, remainingDays: number) {
|
||||
const message: string = `
|
||||
Your remaining free trial is ${remainingDays} days,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Inject } from 'typedi';
|
||||
import { Tenant, Plan } from '@/system/models';
|
||||
import { IPaymentContext } from '@/interfaces';
|
||||
import { NotAllowedChangeSubscriptionPlan } from '@/exceptions';
|
||||
@@ -5,6 +6,9 @@ import { NotAllowedChangeSubscriptionPlan } from '@/exceptions';
|
||||
export default class Subscription<PaymentModel> {
|
||||
paymentContext: IPaymentContext|null;
|
||||
|
||||
@Inject('logger')
|
||||
logger: any;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {IPaymentContext}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { pick } from 'lodash';
|
||||
import { EventSubscriber, On } from 'event-dispatch';
|
||||
import events from '@/subscribers/events';
|
||||
|
||||
|
||||
@EventSubscriber()
|
||||
export class AuthenticationSubscriber {
|
||||
|
||||
@@ -14,13 +13,14 @@ export class AuthenticationSubscriber {
|
||||
|
||||
@On(events.auth.register)
|
||||
public onRegister(payload) {
|
||||
const { registerDTO } = payload;
|
||||
const { registerDTO, user } = payload;
|
||||
|
||||
const agenda = Container.get('agenda');
|
||||
|
||||
// Send welcome mail to the user.
|
||||
agenda.now('welcome-email', {
|
||||
...pick(registerDTO, ['email', 'organizationName', 'firstName']),
|
||||
...pick(registerDTO, ['organizationName']),
|
||||
user,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ export class AuthenticationSubscriber {
|
||||
@On(events.auth.sendResetPassword)
|
||||
public onSendResetPassword (payload) {
|
||||
const { user, token } = payload;
|
||||
|
||||
const agenda = Container.get('agenda');
|
||||
|
||||
// Send reset password mail.
|
||||
|
||||
16
server/src/subscribers/organization.ts
Normal file
16
server/src/subscribers/organization.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Container } from 'typedi';
|
||||
import { On, EventSubscriber } from "event-dispatch";
|
||||
import events from '@/subscribers/events';
|
||||
|
||||
@EventSubscriber()
|
||||
export class OrganizationSubscriber {
|
||||
|
||||
@On(events.organization.build)
|
||||
public onBuild(payload) {
|
||||
const agenda = Container.get('agenda');
|
||||
|
||||
agenda.now('welcome-sms', {
|
||||
email, organizationName, firstName,
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user