mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 14:50:32 +00:00
fix: invite user to the system.
fix: soft delete system user.
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject } from 'typedi';
|
||||||
import { Router, Request, Response } from 'express';
|
import { Router, Request, Response } from 'express';
|
||||||
import { check, body, param } from 'express-validator';
|
import { check, body, param } from 'express-validator';
|
||||||
|
import { IInviteUserInput } from 'interfaces';
|
||||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||||
import InviteUserService from 'services/InviteUsers';
|
import InviteUserService from 'services/InviteUsers';
|
||||||
import { ServiceErrors, ServiceError } from 'exceptions';
|
import { ServiceErrors, ServiceError } from 'exceptions';
|
||||||
@@ -22,7 +23,7 @@ export default class InviteUsersController extends BaseController {
|
|||||||
[body('email').exists().trim().escape()],
|
[body('email').exists().trim().escape()],
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
asyncMiddleware(this.sendInvite.bind(this)),
|
asyncMiddleware(this.sendInvite.bind(this)),
|
||||||
this.handleServicesError,
|
this.handleServicesError
|
||||||
);
|
);
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
@@ -38,14 +39,14 @@ export default class InviteUsersController extends BaseController {
|
|||||||
[...this.inviteUserDTO],
|
[...this.inviteUserDTO],
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
asyncMiddleware(this.accept.bind(this)),
|
asyncMiddleware(this.accept.bind(this)),
|
||||||
this.handleServicesError,
|
this.handleServicesError
|
||||||
);
|
);
|
||||||
router.get(
|
router.get(
|
||||||
'/invited/:token',
|
'/invited/:token',
|
||||||
[param('token').exists().trim().escape()],
|
[param('token').exists().trim().escape()],
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
asyncMiddleware(this.invited.bind(this)),
|
asyncMiddleware(this.invited.bind(this)),
|
||||||
this.handleServicesError,
|
this.handleServicesError
|
||||||
);
|
);
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
@@ -76,8 +77,11 @@ export default class InviteUsersController extends BaseController {
|
|||||||
const { user } = req;
|
const { user } = req;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.inviteUsersService.sendInvite(tenantId, email, user);
|
const { invite } = await this.inviteUsersService.sendInvite(
|
||||||
|
tenantId,
|
||||||
|
email,
|
||||||
|
user
|
||||||
|
);
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
code: 'INVITE.SENT.SUCCESSFULLY',
|
code: 'INVITE.SENT.SUCCESSFULLY',
|
||||||
@@ -104,6 +108,7 @@ export default class InviteUsersController extends BaseController {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.inviteUsersService.acceptInvite(token, inviteUserInput);
|
await this.inviteUsersService.acceptInvite(token, inviteUserInput);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
code: 'USER.INVITE.ACCEPTED',
|
code: 'USER.INVITE.ACCEPTED',
|
||||||
@@ -144,19 +149,40 @@ export default class InviteUsersController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
handleServicesError(error, req: Request, res: Response, next: Function) {
|
handleServicesError(error, req: Request, res: Response, next: Function) {
|
||||||
if (error instanceof ServiceError) {
|
if (error instanceof ServiceError) {
|
||||||
|
if (error.errorType === 'EMAIL_EXISTS') {
|
||||||
|
return res.status(400).send({
|
||||||
|
errors: [{
|
||||||
|
type: 'EMAIL.ALREADY.EXISTS',
|
||||||
|
code: 100,
|
||||||
|
message: 'Email already exists in the users.'
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
}
|
||||||
if (error.errorType === 'EMAIL_ALREADY_INVITED') {
|
if (error.errorType === 'EMAIL_ALREADY_INVITED') {
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
errors: [{ type: 'EMAIL.ALREADY.INVITED' }],
|
errors: [{
|
||||||
|
type: 'EMAIL.ALREADY.INVITED',
|
||||||
|
code: 200,
|
||||||
|
message: 'Email already invited.',
|
||||||
|
}],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (error.errorType === 'INVITE_TOKEN_INVALID') {
|
if (error.errorType === 'INVITE_TOKEN_INVALID') {
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
errors: [{ type: 'INVITE.TOKEN.INVALID' }],
|
errors: [{
|
||||||
|
type: 'INVITE.TOKEN.INVALID',
|
||||||
|
code: 300,
|
||||||
|
message: 'Invite token is invalid, please try another one.',
|
||||||
|
}],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (error.errorType === 'PHONE_NUMBER_EXISTS') {
|
if (error.errorType === 'PHONE_NUMBER_EXISTS') {
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
errors: [{ type: 'PHONE_NUMBER.EXISTS' }],
|
errors: [{
|
||||||
|
type: 'PHONE_NUMBER.EXISTS',
|
||||||
|
code: 400,
|
||||||
|
message: 'Phone number is already invited, please try another unique one.'
|
||||||
|
}],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Router, Request, Response } from 'express';
|
|||||||
|
|
||||||
export default class Ping {
|
export default class Ping {
|
||||||
/**
|
/**
|
||||||
* Router constur
|
* Router constructor.
|
||||||
*/
|
*/
|
||||||
router() {
|
router() {
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ export default class UsersController extends BaseController{
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.usersService.deleteUser(tenantId, id);
|
await this.usersService.deleteUser(tenantId, id);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
id,
|
id,
|
||||||
message: 'The user has been deleted successfully.'
|
message: 'The user has been deleted successfully.'
|
||||||
@@ -225,10 +226,10 @@ export default class UsersController extends BaseController{
|
|||||||
if (error instanceof ServiceErrors) {
|
if (error instanceof ServiceErrors) {
|
||||||
const errorReasons = [];
|
const errorReasons = [];
|
||||||
|
|
||||||
if (error.errorType === 'email_already_exists') {
|
if (error.errorType === 'EMAIL_ALREADY_EXISTS') {
|
||||||
errorReasons.push({ type: 'EMAIL_ALREADY_EXIST', code: 100 });
|
errorReasons.push({ type: 'EMAIL_ALREADY_EXIST', code: 100 });
|
||||||
}
|
}
|
||||||
if (error.errorType === 'phone_number_already_exist') {
|
if (error.errorType === 'PHONE_NUMBER_ALREADY_EXIST') {
|
||||||
errorReasons.push({ type: 'PHONE_NUMBER_ALREADY_EXIST', code: 200 });
|
errorReasons.push({ type: 'PHONE_NUMBER_ALREADY_EXIST', code: 200 });
|
||||||
}
|
}
|
||||||
if (errorReasons.length > 0) {
|
if (errorReasons.length > 0) {
|
||||||
@@ -236,30 +237,36 @@ export default class UsersController extends BaseController{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (error instanceof ServiceError) {
|
if (error instanceof ServiceError) {
|
||||||
if (error.errorType === 'user_not_found') {
|
if (error.errorType === 'USER_NOT_FOUND') {
|
||||||
return res.boom.badRequest(
|
return res.boom.badRequest(
|
||||||
'User not found.',
|
'User not found.',
|
||||||
{ errors: [{ type: 'USER.NOT.FOUND', code: 100 }] }
|
{ errors: [{ type: 'USER.NOT.FOUND', code: 100 }] }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (error.errorType === 'user_already_active') {
|
if (error.errorType === 'USER_ALREADY_ACTIVE') {
|
||||||
return res.boom.badRequest(
|
return res.boom.badRequest(
|
||||||
'User is already active.',
|
'User is already active.',
|
||||||
{ errors: [{ type: 'USER.ALREADY.ACTIVE', code: 200 }] },
|
{ errors: [{ type: 'USER.ALREADY.ACTIVE', code: 200 }] },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (error.errorType === 'user_already_inactive') {
|
if (error.errorType === 'USER_ALREADY_INACTIVE') {
|
||||||
return res.boom.badRequest(
|
return res.boom.badRequest(
|
||||||
'User is already inactive.',
|
'User is already inactive.',
|
||||||
{ errors: [{ type: 'USER.ALREADY.INACTIVE', code: 200 }] },
|
{ errors: [{ type: 'USER.ALREADY.INACTIVE', code: 200 }] },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (error.errorType === 'user_same_the_authorized_user') {
|
if (error.errorType === 'USER_SAME_THE_AUTHORIZED_USER') {
|
||||||
return res.boom.badRequest(
|
return res.boom.badRequest(
|
||||||
'You could not activate/inactivate the same authorized user.',
|
'You could not activate/inactivate the same authorized user.',
|
||||||
{ errors: [{ type: 'CANNOT.TOGGLE.ACTIVATE.AUTHORIZED.USER', code: 300 }] },
|
{ errors: [{ type: 'CANNOT.TOGGLE.ACTIVATE.AUTHORIZED.USER', code: 300 }] },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (error.errorType === 'CANNOT_DELETE_LAST_USER') {
|
||||||
|
return res.boom.badRequest(
|
||||||
|
'Cannot delete last user in the organization.',
|
||||||
|
{ errors: [{ type: 'CANNOT_DELETE_LAST_USER', code: 400 }] },
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ const attachCurrentUser = async (req: Request, res: Response, next: Function) =>
|
|||||||
try {
|
try {
|
||||||
Logger.info('[attach_user_middleware] finding system user by id.');
|
Logger.info('[attach_user_middleware] finding system user by id.');
|
||||||
const user = await systemUserRepository.findOneById(req.token.id);
|
const user = await systemUserRepository.findOneById(req.token.id);
|
||||||
console.log(user);
|
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
Logger.info('[attach_user_middleware] the system user not found.');
|
Logger.info('[attach_user_middleware] the system user not found.');
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ export default (req: Request, res: Response, next: Function) => {
|
|||||||
throw new Error('Should load this middleware after `TenancyMiddleware`.');
|
throw new Error('Should load this middleware after `TenancyMiddleware`.');
|
||||||
}
|
}
|
||||||
if (!req.tenant.seededAt) {
|
if (!req.tenant.seededAt) {
|
||||||
Logger.info('[ensure_tenant_initialized_middleware] tenant databae not seeded.');
|
Logger.info(
|
||||||
|
'[ensure_tenant_initialized_middleware] tenant databae not seeded.'
|
||||||
|
);
|
||||||
return res.boom.badRequest(
|
return res.boom.badRequest(
|
||||||
'Tenant database is not seeded with initial data yet.',
|
'Tenant database is not seeded with initial data yet.',
|
||||||
{ errors: [{ type: 'TENANT.DATABASE.NOT.SEED' }] },
|
{ errors: [{ type: 'TENANT.DATABASE.NOT.SEED' }] }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from 'express';
|
||||||
import { Container } from 'typedi';
|
import { Container } from 'typedi';
|
||||||
|
|
||||||
export default (subscriptionSlug = 'main') => async (req: Request, res: Response, next: NextFunction) => {
|
export default (subscriptionSlug = 'main') => async (
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
) => {
|
||||||
const { tenant, tenantId } = req;
|
const { tenant, tenantId } = req;
|
||||||
const Logger = Container.get('logger');
|
const Logger = Container.get('logger');
|
||||||
const { subscriptionRepository } = Container.get('repositories');
|
const { subscriptionRepository } = Container.get('repositories');
|
||||||
@@ -10,19 +14,25 @@ export default (subscriptionSlug = 'main') => async (req: Request, res: Response
|
|||||||
throw new Error('Should load `TenancyMiddlware` before this middleware.');
|
throw new Error('Should load `TenancyMiddlware` before this middleware.');
|
||||||
}
|
}
|
||||||
Logger.info('[subscription_middleware] trying get tenant main subscription.');
|
Logger.info('[subscription_middleware] trying get tenant main subscription.');
|
||||||
const subscription = await subscriptionRepository.getBySlugInTenant(subscriptionSlug, tenantId);
|
const subscription = await subscriptionRepository.getBySlugInTenant(
|
||||||
|
subscriptionSlug,
|
||||||
|
tenantId
|
||||||
|
);
|
||||||
// Validate in case there is no any already subscription.
|
// Validate in case there is no any already subscription.
|
||||||
if (!subscription) {
|
if (!subscription) {
|
||||||
Logger.info('[subscription_middleware] tenant has no subscription.', { tenantId });
|
Logger.info('[subscription_middleware] tenant has no subscription.', {
|
||||||
return res.boom.badRequest(
|
tenantId,
|
||||||
'Tenant has no subscription.',
|
});
|
||||||
{ errors: [{ type: 'TENANT.HAS.NO.SUBSCRIPTION' }] }
|
return res.boom.badRequest('Tenant has no subscription.', {
|
||||||
);
|
errors: [{ type: 'TENANT.HAS.NO.SUBSCRIPTION' }],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// Validate in case the subscription is inactive.
|
// Validate in case the subscription is inactive.
|
||||||
else if (subscription.inactive()) {
|
else if (subscription.inactive()) {
|
||||||
Logger.info('[subscription_middleware] tenant main subscription is expired.', { tenantId });
|
Logger.info(
|
||||||
|
'[subscription_middleware] tenant main subscription is expired.',
|
||||||
|
{ tenantId }
|
||||||
|
);
|
||||||
return res.boom.badRequest(null, {
|
return res.boom.badRequest(null, {
|
||||||
errors: [{ type: 'ORGANIZATION.SUBSCRIPTION.INACTIVE' }],
|
errors: [{ type: 'ORGANIZATION.SUBSCRIPTION.INACTIVE' }],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import logger from "src/loaders/logger";
|
|
||||||
|
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from 'express';
|
||||||
import { Container } from 'typedi';
|
import { Container } from 'typedi';
|
||||||
|
|
||||||
|
|||||||
73
server/src/collection/SoftDeleteQueryBuilder.js
Normal file
73
server/src/collection/SoftDeleteQueryBuilder.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import moment from 'moment';
|
||||||
|
import { Model } from 'objection';
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
columnName: 'deleted_at',
|
||||||
|
deletedValue: moment().format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
notDeletedValue: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class SoftDeleteQueryBuilder extends Model.QueryBuilder {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args);
|
||||||
|
|
||||||
|
this.onBuild((builder) => {
|
||||||
|
if (builder.isFind() || builder.isDelete() || builder.isUpdate()) {
|
||||||
|
builder.whereNotDeleted();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* override the normal delete function with one that patches the row's "deleted" column
|
||||||
|
*/
|
||||||
|
delete() {
|
||||||
|
this.context({
|
||||||
|
softDelete: true,
|
||||||
|
});
|
||||||
|
const patch = {};
|
||||||
|
patch[options.columnName] = options.deletedValue;
|
||||||
|
return this.patch(patch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a way to actually delete the row if necessary
|
||||||
|
*/
|
||||||
|
hardDelete() {
|
||||||
|
return super.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a way to undo the delete
|
||||||
|
*/
|
||||||
|
undelete() {
|
||||||
|
this.context({
|
||||||
|
undelete: true,
|
||||||
|
});
|
||||||
|
const patch = {};
|
||||||
|
patch[options.columnName] = options.notDeletedValue;
|
||||||
|
return this.patch(patch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a way to filter to ONLY deleted records without having to remember the column name
|
||||||
|
*/
|
||||||
|
whereDeleted() {
|
||||||
|
const prefix = this.modelClass().tableName;
|
||||||
|
|
||||||
|
// this if is for backwards compatibility, to protect those that used a nullable `deleted` field
|
||||||
|
if (options.deletedValue === true) {
|
||||||
|
return this.where(`${prefix}.${options.columnName}`, options.deletedValue);
|
||||||
|
}
|
||||||
|
// qualify the column name
|
||||||
|
return this.whereNot(`${prefix}.${options.columnName}`, options.notDeletedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// provide a way to filter out deleted records without having to remember the column name
|
||||||
|
whereNotDeleted() {
|
||||||
|
const prefix = this.modelClass().tableName;
|
||||||
|
|
||||||
|
// qualify the column name
|
||||||
|
return this.where(`${prefix}.${options.columnName}`, options.notDeletedValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { Model } from 'objection';
|
||||||
|
|
||||||
|
export interface ISystemUser extends Model {
|
||||||
export interface ISystemUser {
|
|
||||||
id: number,
|
id: number,
|
||||||
firstName: string,
|
firstName: string,
|
||||||
lastName: string,
|
lastName: string,
|
||||||
@@ -34,4 +34,12 @@ export interface IInviteUserInput {
|
|||||||
lastName: string,
|
lastName: string,
|
||||||
phoneNumber: string,
|
phoneNumber: string,
|
||||||
password: string,
|
password: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IUserInvite {
|
||||||
|
id: number,
|
||||||
|
email: string,
|
||||||
|
token: string,
|
||||||
|
tenantId: number,
|
||||||
|
createdAt?: Date,
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { cloneDeep, cloneDeepWith, forOwn, isString } from 'lodash';
|
import { cloneDeep, forOwn, isString } from 'lodash';
|
||||||
import ModelEntityNotFound from 'exceptions/ModelEntityNotFound';
|
import ModelEntityNotFound from 'exceptions/ModelEntityNotFound';
|
||||||
|
|
||||||
export default class EntityRepository {
|
export default class EntityRepository {
|
||||||
@@ -38,8 +38,7 @@ export default class EntityRepository {
|
|||||||
* @returns {Promise<Object[]>} - query builder. You can chain additional methods to it or call "await" or then() on it to execute
|
* @returns {Promise<Object[]>} - query builder. You can chain additional methods to it or call "await" or then() on it to execute
|
||||||
*/
|
*/
|
||||||
find(attributeValues = {}, withRelations?) {
|
find(attributeValues = {}, withRelations?) {
|
||||||
return this.model
|
return this.model.query()
|
||||||
.query()
|
|
||||||
.where(attributeValues)
|
.where(attributeValues)
|
||||||
.withGraphFetched(withRelations);
|
.withGraphFetched(withRelations);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import AuthenticationMailMessages from 'services/Authentication/AuthenticationMa
|
|||||||
import AuthenticationSMSMessages from 'services/Authentication/AuthenticationSMSMessages';
|
import AuthenticationSMSMessages from 'services/Authentication/AuthenticationSMSMessages';
|
||||||
import TenantsManager from 'services/Tenancy/TenantsManager';
|
import TenantsManager from 'services/Tenancy/TenantsManager';
|
||||||
|
|
||||||
|
|
||||||
const ERRORS = {
|
const ERRORS = {
|
||||||
INVALID_DETAILS: 'INVALID_DETAILS',
|
INVALID_DETAILS: 'INVALID_DETAILS',
|
||||||
USER_INACTIVE: 'USER_INACTIVE',
|
USER_INACTIVE: 'USER_INACTIVE',
|
||||||
@@ -32,7 +31,7 @@ const ERRORS = {
|
|||||||
USER_NOT_FOUND: 'USER_NOT_FOUND',
|
USER_NOT_FOUND: 'USER_NOT_FOUND',
|
||||||
TOKEN_EXPIRED: 'TOKEN_EXPIRED',
|
TOKEN_EXPIRED: 'TOKEN_EXPIRED',
|
||||||
PHONE_NUMBER_EXISTS: 'PHONE_NUMBER_EXISTS',
|
PHONE_NUMBER_EXISTS: 'PHONE_NUMBER_EXISTS',
|
||||||
EMAIL_EXISTS: 'EMAIL_EXISTS'
|
EMAIL_EXISTS: 'EMAIL_EXISTS',
|
||||||
};
|
};
|
||||||
@Service()
|
@Service()
|
||||||
export default class AuthenticationService implements IAuthenticationService {
|
export default class AuthenticationService implements IAuthenticationService {
|
||||||
@@ -136,6 +135,7 @@ export default class AuthenticationService implements IAuthenticationService {
|
|||||||
*/
|
*/
|
||||||
private async validateEmailAndPhoneUniqiness(registerDTO: IRegisterDTO) {
|
private async validateEmailAndPhoneUniqiness(registerDTO: IRegisterDTO) {
|
||||||
const { systemUserRepository } = this.sysRepositories;
|
const { systemUserRepository } = this.sysRepositories;
|
||||||
|
|
||||||
const isEmailExists = await systemUserRepository.findOneByEmail(
|
const isEmailExists = await systemUserRepository.findOneByEmail(
|
||||||
registerDTO.email
|
registerDTO.email
|
||||||
);
|
);
|
||||||
@@ -279,7 +279,10 @@ export default class AuthenticationService implements IAuthenticationService {
|
|||||||
const hashedPassword = await hashPassword(password);
|
const hashedPassword = await hashPassword(password);
|
||||||
|
|
||||||
this.logger.info('[reset_password] saving a new hashed password.');
|
this.logger.info('[reset_password] saving a new hashed password.');
|
||||||
await systemUserRepository.update({ password: hashedPassword }, { id: user.id });
|
await systemUserRepository.update(
|
||||||
|
{ password: hashedPassword },
|
||||||
|
{ id: user.id }
|
||||||
|
);
|
||||||
|
|
||||||
// Deletes the used token.
|
// Deletes the used token.
|
||||||
await this.deletePasswordResetToken(tokenModel.email);
|
await this.deletePasswordResetToken(tokenModel.email);
|
||||||
|
|||||||
@@ -12,13 +12,14 @@ import { hashPassword } from 'utils';
|
|||||||
import TenancyService from 'services/Tenancy/TenancyService';
|
import TenancyService from 'services/Tenancy/TenancyService';
|
||||||
import InviteUsersMailMessages from 'services/InviteUsers/InviteUsersMailMessages';
|
import InviteUsersMailMessages from 'services/InviteUsers/InviteUsersMailMessages';
|
||||||
import events from 'subscribers/events';
|
import events from 'subscribers/events';
|
||||||
import { ISystemUser, IInviteUserInput } from 'interfaces';
|
import { ISystemUser, IInviteUserInput, IUserInvite } from 'interfaces';
|
||||||
import TenantsManagerService from 'services/Tenancy/TenantsManager';
|
import TenantsManagerService from 'services/Tenancy/TenantsManager';
|
||||||
|
|
||||||
const ERRORS = {
|
const ERRORS = {
|
||||||
EMAIL_ALREADY_INVITED: 'EMAIL_ALREADY_INVITED',
|
EMAIL_ALREADY_INVITED: 'EMAIL_ALREADY_INVITED',
|
||||||
INVITE_TOKEN_INVALID: 'INVITE_TOKEN_INVALID',
|
INVITE_TOKEN_INVALID: 'INVITE_TOKEN_INVALID',
|
||||||
PHONE_NUMBER_EXISTS: 'PHONE_NUMBER_EXISTS'
|
PHONE_NUMBER_EXISTS: 'PHONE_NUMBER_EXISTS',
|
||||||
|
EMAIL_EXISTS: 'EMAIL_EXISTS'
|
||||||
};
|
};
|
||||||
@Service()
|
@Service()
|
||||||
export default class InviteUserService {
|
export default class InviteUserService {
|
||||||
@@ -66,12 +67,16 @@ export default class InviteUserService {
|
|||||||
const user = await systemUserRepository.findOneByEmail(inviteToken.email);
|
const user = await systemUserRepository.findOneByEmail(inviteToken.email);
|
||||||
|
|
||||||
// Sets the invited user details after invite accepting.
|
// Sets the invited user details after invite accepting.
|
||||||
const updateUserOper = systemUserRepository.update({
|
const systemUserOper = systemUserRepository.create(
|
||||||
...inviteUserInput,
|
{
|
||||||
active: 1,
|
...inviteUserInput,
|
||||||
inviteAcceptedAt: moment().format('YYYY-MM-DD'),
|
email: inviteToken.email,
|
||||||
password: hashedPassword,
|
tenantId: inviteToken.tenantId,
|
||||||
}, { id: user.id });
|
active: 1,
|
||||||
|
inviteAcceptedAt: moment().format('YYYY-MM-DD'),
|
||||||
|
password: hashedPassword,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
this.logger.info('[accept_invite] trying to delete the given token.');
|
this.logger.info('[accept_invite] trying to delete the given token.');
|
||||||
const deleteInviteTokenOper = Invite.query()
|
const deleteInviteTokenOper = Invite.query()
|
||||||
@@ -79,14 +84,14 @@ export default class InviteUserService {
|
|||||||
.delete();
|
.delete();
|
||||||
|
|
||||||
// Await all async operations.
|
// Await all async operations.
|
||||||
const [updatedUser] = await Promise.all([
|
const [systemUser] = await Promise.all([
|
||||||
updateUserOper,
|
systemUserOper,
|
||||||
deleteInviteTokenOper,
|
deleteInviteTokenOper,
|
||||||
]);
|
]);
|
||||||
// Triggers `onUserAcceptInvite` event.
|
// Triggers `onUserAcceptInvite` event.
|
||||||
this.eventDispatcher.dispatch(events.inviteUser.acceptInvite, {
|
this.eventDispatcher.dispatch(events.inviteUser.acceptInvite, {
|
||||||
inviteToken,
|
inviteToken,
|
||||||
user: updatedUser,
|
user: systemUser,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,21 +101,21 @@ export default class InviteUserService {
|
|||||||
* @param {string} email -
|
* @param {string} email -
|
||||||
* @param {IUser} authorizedUser -
|
* @param {IUser} authorizedUser -
|
||||||
*
|
*
|
||||||
* @return {Promise<IInvite>}
|
* @return {Promise<IUserInvite>}
|
||||||
*/
|
*/
|
||||||
public async sendInvite(
|
public async sendInvite(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
email: string,
|
email: string,
|
||||||
authorizedUser: ISystemUser
|
authorizedUser: ISystemUser
|
||||||
): Promise<{
|
): Promise<{
|
||||||
invite: IInvite,
|
invite: IUserInvite;
|
||||||
user: ISystemUser
|
|
||||||
}> {
|
}> {
|
||||||
const { systemUserRepository } = this.sysRepositories;
|
|
||||||
|
|
||||||
// Throw error in case user email exists.
|
// Throw error in case user email exists.
|
||||||
await this.throwErrorIfUserEmailExists(email);
|
await this.throwErrorIfUserEmailExists(email);
|
||||||
|
|
||||||
|
// Throws service error in case the user already invited.
|
||||||
|
await this.throwErrorIfUserInvited(email);
|
||||||
|
|
||||||
this.logger.info('[send_invite] trying to store invite token.');
|
this.logger.info('[send_invite] trying to store invite token.');
|
||||||
const invite = await Invite.query().insert({
|
const invite = await Invite.query().insert({
|
||||||
email,
|
email,
|
||||||
@@ -121,16 +126,13 @@ export default class InviteUserService {
|
|||||||
this.logger.info(
|
this.logger.info(
|
||||||
'[send_invite] trying to store user with email and tenant.'
|
'[send_invite] trying to store user with email and tenant.'
|
||||||
);
|
);
|
||||||
const user = await systemUserRepository.create({
|
|
||||||
email,
|
|
||||||
tenant_id: authorizedUser.tenantId,
|
|
||||||
active: 1,
|
|
||||||
});
|
|
||||||
// Triggers `onUserSendInvite` event.
|
// Triggers `onUserSendInvite` event.
|
||||||
this.eventDispatcher.dispatch(events.inviteUser.sendInvite, {
|
this.eventDispatcher.dispatch(events.inviteUser.sendInvite, {
|
||||||
invite, authorizedUser, tenantId
|
invite,
|
||||||
|
authorizedUser,
|
||||||
|
tenantId,
|
||||||
});
|
});
|
||||||
return { invite, user };
|
return { invite };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -140,7 +142,7 @@ export default class InviteUserService {
|
|||||||
*/
|
*/
|
||||||
public async checkInvite(
|
public async checkInvite(
|
||||||
token: string
|
token: string
|
||||||
): Promise<{ inviteToken: string; orgName: object }> {
|
): Promise<{ inviteToken: IUserInvite; orgName: object }> {
|
||||||
const inviteToken = await this.getInviteOrThrowError(token);
|
const inviteToken = await this.getInviteOrThrowError(token);
|
||||||
|
|
||||||
// Find the tenant that associated to the given token.
|
// Find the tenant that associated to the given token.
|
||||||
@@ -170,14 +172,27 @@ export default class InviteUserService {
|
|||||||
*/
|
*/
|
||||||
private async throwErrorIfUserEmailExists(
|
private async throwErrorIfUserEmailExists(
|
||||||
email: string
|
email: string
|
||||||
): Promise<ISystemUser> {
|
): Promise<void> {
|
||||||
const { systemUserRepository } = this.sysRepositories;
|
const { systemUserRepository } = this.sysRepositories;
|
||||||
const foundUser = await systemUserRepository.findOneByEmail(email);
|
const foundUser = await systemUserRepository.findOneByEmail(email);
|
||||||
|
|
||||||
if (foundUser) {
|
if (foundUser) {
|
||||||
|
throw new ServiceError(ERRORS.EMAIL_EXISTS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws service error if the user already invited.
|
||||||
|
* @param {string} email -
|
||||||
|
*/
|
||||||
|
private async throwErrorIfUserInvited(
|
||||||
|
email: string,
|
||||||
|
): Promise<void> {
|
||||||
|
const inviteToken = await Invite.query().findOne('email', email);
|
||||||
|
|
||||||
|
if (inviteToken) {
|
||||||
throw new ServiceError(ERRORS.EMAIL_ALREADY_INVITED);
|
throw new ServiceError(ERRORS.EMAIL_ALREADY_INVITED);
|
||||||
}
|
}
|
||||||
return foundUser;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -186,7 +201,7 @@ export default class InviteUserService {
|
|||||||
* @throws {ServiceError}
|
* @throws {ServiceError}
|
||||||
* @returns {Invite}
|
* @returns {Invite}
|
||||||
*/
|
*/
|
||||||
private async getInviteOrThrowError(token: string) {
|
private async getInviteOrThrowError(token: string): Promise<IUserInvite> {
|
||||||
const inviteToken = await Invite.query().findOne('token', token);
|
const inviteToken = await Invite.query().findOne('token', token);
|
||||||
|
|
||||||
if (!inviteToken) {
|
if (!inviteToken) {
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import TenancyService from 'services/Tenancy/TenancyService';
|
import TenancyService from 'services/Tenancy/TenancyService';
|
||||||
import { SystemUser } from 'system/models';
|
import { Inject, Service } from 'typedi';
|
||||||
import { ServiceError, ServiceErrors } from 'exceptions';
|
import { ServiceError, ServiceErrors } from 'exceptions';
|
||||||
import { ISystemUser, ISystemUserDTO } from 'interfaces';
|
import { ISystemUser, ISystemUserDTO } from 'interfaces';
|
||||||
import systemRepositories from 'loaders/systemRepositories';
|
|
||||||
|
const ERRORS = {
|
||||||
|
CANNOT_DELETE_LAST_USER: 'CANNOT_DELETE_LAST_USER',
|
||||||
|
USER_ALREADY_ACTIVE: 'USER_ALREADY_ACTIVE',
|
||||||
|
USER_ALREADY_INACTIVE: 'USER_ALREADY_INACTIVE',
|
||||||
|
EMAIL_ALREADY_EXISTS: 'EMAIL_ALREADY_EXISTS',
|
||||||
|
PHONE_NUMBER_ALREADY_EXIST: 'PHONE_NUMBER_ALREADY_EXIST',
|
||||||
|
USER_NOT_FOUND: 'USER_NOT_FOUND',
|
||||||
|
USER_SAME_THE_AUTHORIZED_USER: 'USER_SAME_THE_AUTHORIZED_USER',
|
||||||
|
};
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class UsersService {
|
export default class UsersService {
|
||||||
@@ -23,7 +31,7 @@ export default class UsersService {
|
|||||||
* @param {IUserDTO} userDTO
|
* @param {IUserDTO} userDTO
|
||||||
* @return {Promise<ISystemUser>}
|
* @return {Promise<ISystemUser>}
|
||||||
*/
|
*/
|
||||||
async editUser(
|
public async editUser(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
userId: number,
|
userId: number,
|
||||||
userDTO: ISystemUserDTO
|
userDTO: ISystemUserDTO
|
||||||
@@ -36,49 +44,24 @@ export default class UsersService {
|
|||||||
});
|
});
|
||||||
const userByPhoneNumber = await systemUserRepository.findOne({
|
const userByPhoneNumber = await systemUserRepository.findOne({
|
||||||
phoneNumber: userDTO.phoneNumber,
|
phoneNumber: userDTO.phoneNumber,
|
||||||
id: userId
|
id: userId,
|
||||||
});
|
});
|
||||||
const serviceErrors: ServiceError[] = [];
|
const serviceErrors: ServiceError[] = [];
|
||||||
|
|
||||||
if (userByEmail) {
|
if (userByEmail) {
|
||||||
serviceErrors.push(new ServiceError('email_already_exists'));
|
serviceErrors.push(new ServiceError(ERRORS.EMAIL_ALREADY_EXISTS));
|
||||||
}
|
}
|
||||||
if (userByPhoneNumber) {
|
if (userByPhoneNumber) {
|
||||||
serviceErrors.push(new ServiceError('phone_number_already_exist'));
|
serviceErrors.push(new ServiceError(ERRORS.PHONE_NUMBER_ALREADY_EXIST));
|
||||||
}
|
}
|
||||||
if (serviceErrors.length > 0) {
|
if (serviceErrors.length > 0) {
|
||||||
throw new ServiceErrors(serviceErrors);
|
throw new ServiceErrors(serviceErrors);
|
||||||
}
|
}
|
||||||
const updateSystemUser = await systemUserRepository
|
const updateSystemUser = await systemUserRepository.update(
|
||||||
.update({ ...userDTO, }, { id: userId });
|
{ ...userDTO },
|
||||||
|
{ id: userId }
|
||||||
return updateSystemUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate user existance throw error in case user was not found.,
|
|
||||||
* @param {number} tenantId -
|
|
||||||
* @param {number} userId -
|
|
||||||
* @returns {ISystemUser}
|
|
||||||
*/
|
|
||||||
async getUserOrThrowError(
|
|
||||||
tenantId: number,
|
|
||||||
userId: number
|
|
||||||
): Promise<ISystemUser> {
|
|
||||||
const { systemUserRepository } = this.repositories;
|
|
||||||
const user = await systemUserRepository.findOneByIdAndTenant(
|
|
||||||
userId,
|
|
||||||
tenantId
|
|
||||||
);
|
);
|
||||||
|
return updateSystemUser;
|
||||||
if (!user) {
|
|
||||||
this.logger.info('[users] the given user not found.', {
|
|
||||||
tenantId,
|
|
||||||
userId,
|
|
||||||
});
|
|
||||||
throw new ServiceError('user_not_found');
|
|
||||||
}
|
|
||||||
return user;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,14 +69,20 @@ export default class UsersService {
|
|||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {number} userId
|
* @param {number} userId
|
||||||
*/
|
*/
|
||||||
async deleteUser(tenantId: number, userId: number): Promise<void> {
|
public async deleteUser(tenantId: number, userId: number): Promise<void> {
|
||||||
const { systemUserRepository } = this.repositories;
|
const { systemUserRepository } = this.repositories;
|
||||||
await this.getUserOrThrowError(tenantId, userId);
|
|
||||||
|
// Retrieve user details or throw not found service error.
|
||||||
|
const oldUser = await this.getUserOrThrowError(tenantId, userId);
|
||||||
|
|
||||||
this.logger.info('[users] trying to delete the given user.', {
|
this.logger.info('[users] trying to delete the given user.', {
|
||||||
tenantId,
|
tenantId,
|
||||||
userId,
|
userId,
|
||||||
});
|
});
|
||||||
|
// Validate the delete user should not be the last user.
|
||||||
|
await this.validateNotLastUserDelete(tenantId);
|
||||||
|
|
||||||
|
// Delete user from the storage.
|
||||||
await systemUserRepository.deleteById(userId);
|
await systemUserRepository.deleteById(userId);
|
||||||
|
|
||||||
this.logger.info('[users] the given user deleted successfully.', {
|
this.logger.info('[users] the given user deleted successfully.', {
|
||||||
@@ -104,18 +93,24 @@ export default class UsersService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Activate the given user id.
|
* Activate the given user id.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId - Tenant id.
|
||||||
* @param {number} userId
|
* @param {number} userId - User id.
|
||||||
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async activateUser(
|
public async activateUser(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
userId: number,
|
userId: number,
|
||||||
authorizedUser: ISystemUser
|
authorizedUser: ISystemUser
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
this.throwErrorIfUserIdSameAuthorizedUser(userId, authorizedUser);
|
|
||||||
const { systemUserRepository } = this.repositories;
|
const { systemUserRepository } = this.repositories;
|
||||||
|
|
||||||
|
// Throw service error if the given user is equals the authorized user.
|
||||||
|
this.throwErrorIfUserSameAuthorizedUser(userId, authorizedUser);
|
||||||
|
|
||||||
|
// Retrieve the user or throw not found service error.
|
||||||
const user = await this.getUserOrThrowError(tenantId, userId);
|
const user = await this.getUserOrThrowError(tenantId, userId);
|
||||||
|
|
||||||
|
// Throw serivce error if the user is already activated.
|
||||||
this.throwErrorIfUserActive(user);
|
this.throwErrorIfUserActive(user);
|
||||||
|
|
||||||
await systemUserRepository.activateUser(userId);
|
await systemUserRepository.activateUser(userId);
|
||||||
@@ -127,15 +122,20 @@ export default class UsersService {
|
|||||||
* @param {number} userId
|
* @param {number} userId
|
||||||
* @return {Promise<void>}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async inactivateUser(
|
public async inactivateUser(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
userId: number,
|
userId: number,
|
||||||
authorizedUser: ISystemUser
|
authorizedUser: ISystemUser
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
this.throwErrorIfUserIdSameAuthorizedUser(userId, authorizedUser);
|
|
||||||
const { systemUserRepository } = this.repositories;
|
const { systemUserRepository } = this.repositories;
|
||||||
|
|
||||||
|
// Throw service error if the given user is equals the authorized user.
|
||||||
|
this.throwErrorIfUserSameAuthorizedUser(userId, authorizedUser);
|
||||||
|
|
||||||
|
// Retrieve the user or throw not found service error.
|
||||||
const user = await this.getUserOrThrowError(tenantId, userId);
|
const user = await this.getUserOrThrowError(tenantId, userId);
|
||||||
|
|
||||||
|
// Throw serivce error if the user is already inactivated.
|
||||||
this.throwErrorIfUserInactive(user);
|
this.throwErrorIfUserInactive(user);
|
||||||
|
|
||||||
await systemUserRepository.inactivateById(userId);
|
await systemUserRepository.inactivateById(userId);
|
||||||
@@ -146,10 +146,10 @@ export default class UsersService {
|
|||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {object} filter
|
* @param {object} filter
|
||||||
*/
|
*/
|
||||||
async getList(tenantId: number) {
|
public async getList(tenantId: number) {
|
||||||
const users = await SystemUser.query()
|
const { systemUserRepository } = this.repositories;
|
||||||
.whereNotDeleted()
|
|
||||||
.where('tenant_id', tenantId);
|
const users = await systemUserRepository.find({ tenantId });
|
||||||
|
|
||||||
return users;
|
return users;
|
||||||
}
|
}
|
||||||
@@ -159,18 +159,58 @@ export default class UsersService {
|
|||||||
* @param {number} tenantId - Tenant id.
|
* @param {number} tenantId - Tenant id.
|
||||||
* @param {number} userId - User id.
|
* @param {number} userId - User id.
|
||||||
*/
|
*/
|
||||||
async getUser(tenantId: number, userId: number) {
|
public async getUser(tenantId: number, userId: number) {
|
||||||
return this.getUserOrThrowError(tenantId, userId);
|
return this.getUserOrThrowError(tenantId, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate user existance throw error in case user was not found.,
|
||||||
|
* @param {number} tenantId -
|
||||||
|
* @param {number} userId -
|
||||||
|
* @returns {ISystemUser}
|
||||||
|
*/
|
||||||
|
async getUserOrThrowError(
|
||||||
|
tenantId: number,
|
||||||
|
userId: number
|
||||||
|
): Promise<ISystemUser> {
|
||||||
|
const { systemUserRepository } = this.repositories;
|
||||||
|
|
||||||
|
const user = await systemUserRepository.findOneByIdAndTenant(
|
||||||
|
userId,
|
||||||
|
tenantId
|
||||||
|
);
|
||||||
|
if (!user) {
|
||||||
|
this.logger.info('[users] the given user not found.', {
|
||||||
|
tenantId,
|
||||||
|
userId,
|
||||||
|
});
|
||||||
|
throw new ServiceError(ERRORS.USER_NOT_FOUND);
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the delete user should not be the last user.
|
||||||
|
* @param {number} tenantId
|
||||||
|
*/
|
||||||
|
private async validateNotLastUserDelete(tenantId: number) {
|
||||||
|
const { systemUserRepository } = this.repositories;
|
||||||
|
|
||||||
|
const usersFound = await systemUserRepository.find({ tenantId });
|
||||||
|
|
||||||
|
if (usersFound.length === 1) {
|
||||||
|
throw new ServiceError(ERRORS.CANNOT_DELETE_LAST_USER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throws service error in case the user was already active.
|
* Throws service error in case the user was already active.
|
||||||
* @param {ISystemUser} user
|
* @param {ISystemUser} user
|
||||||
* @throws {ServiceError}
|
* @throws {ServiceError}
|
||||||
*/
|
*/
|
||||||
throwErrorIfUserActive(user: ISystemUser) {
|
private throwErrorIfUserActive(user: ISystemUser) {
|
||||||
if (user.active) {
|
if (user.active) {
|
||||||
throw new ServiceError('user_already_active');
|
throw new ServiceError(ERRORS.USER_ALREADY_ACTIVE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,9 +219,9 @@ export default class UsersService {
|
|||||||
* @param {ISystemUser} user
|
* @param {ISystemUser} user
|
||||||
* @throws {ServiceError}
|
* @throws {ServiceError}
|
||||||
*/
|
*/
|
||||||
throwErrorIfUserInactive(user: ISystemUser) {
|
private throwErrorIfUserInactive(user: ISystemUser) {
|
||||||
if (!user.active) {
|
if (!user.active) {
|
||||||
throw new ServiceError('user_already_inactive');
|
throw new ServiceError(ERRORS.USER_ALREADY_INACTIVE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,12 +230,12 @@ export default class UsersService {
|
|||||||
* @param {number} userId
|
* @param {number} userId
|
||||||
* @param {ISystemUser} authorizedUser
|
* @param {ISystemUser} authorizedUser
|
||||||
*/
|
*/
|
||||||
throwErrorIfUserIdSameAuthorizedUser(
|
private throwErrorIfUserSameAuthorizedUser(
|
||||||
userId: number,
|
userId: number,
|
||||||
authorizedUser: ISystemUser
|
authorizedUser: ISystemUser
|
||||||
) {
|
) {
|
||||||
if (userId === authorizedUser.id) {
|
if (userId === authorizedUser.id) {
|
||||||
throw new ServiceError('user_same_the_authorized_user');
|
throw new ServiceError(ERRORS.USER_SAME_THE_AUTHORIZED_USER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ export default class SaleInvoiceSubscriber {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles customer balance decrement once sale invoice deleted.
|
* Handles customer balance decrement once sale invoice deleted.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ exports.up = function (knex) {
|
|||||||
table.increments();
|
table.increments();
|
||||||
table.string('first_name');
|
table.string('first_name');
|
||||||
table.string('last_name');
|
table.string('last_name');
|
||||||
table.string('email').unique().index();
|
table.string('email').index();
|
||||||
table.string('phone_number').unique().index();
|
table.string('phone_number').index();
|
||||||
table.string('password');
|
table.string('password');
|
||||||
table.boolean('active').index();
|
table.boolean('active').index();
|
||||||
table.string('language');
|
table.string('language');
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
import { Model, mixin } from 'objection';
|
import { Model } from 'objection';
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import SoftDelete from 'objection-soft-delete';
|
|
||||||
import SystemModel from 'system/models/SystemModel';
|
import SystemModel from 'system/models/SystemModel';
|
||||||
import moment from 'moment';
|
import SoftDeleteQueryBuilder from 'collection/SoftDeleteQueryBuilder';
|
||||||
|
|
||||||
export default class SystemUser extends mixin(SystemModel, [SoftDelete({
|
export default class SystemUser extends SystemModel {
|
||||||
columnName: 'deleted_at',
|
|
||||||
deletedValue: moment().format('YYYY-MM-DD HH:mm:ss'),
|
|
||||||
notDeletedValue: null,
|
|
||||||
})]) {
|
|
||||||
/**
|
/**
|
||||||
* Table name.
|
* Table name.
|
||||||
*/
|
*/
|
||||||
@@ -16,6 +11,13 @@ export default class SystemUser extends mixin(SystemModel, [SoftDelete({
|
|||||||
return 'users';
|
return 'users';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Soft delete query builder.
|
||||||
|
*/
|
||||||
|
static get QueryBuilder() {
|
||||||
|
return SoftDeleteQueryBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timestamps columns.
|
* Timestamps columns.
|
||||||
*/
|
*/
|
||||||
@@ -23,10 +25,16 @@ export default class SystemUser extends mixin(SystemModel, [SoftDelete({
|
|||||||
return ['createdAt', 'updatedAt'];
|
return ['createdAt', 'updatedAt'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual attributes.
|
||||||
|
*/
|
||||||
static get virtualAttributes() {
|
static get virtualAttributes() {
|
||||||
return ['fullName'];
|
return ['fullName'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full name attribute.
|
||||||
|
*/
|
||||||
get fullName() {
|
get fullName() {
|
||||||
return (this.firstName + ' ' + this.lastName).trim();
|
return (this.firstName + ' ' + this.lastName).trim();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ export default class SystemUserRepository extends SystemRepository {
|
|||||||
|
|
||||||
return this.cache.get(cacheKey, () => {
|
return this.cache.get(cacheKey, () => {
|
||||||
return this.model.query()
|
return this.model.query()
|
||||||
.whereNotDeleted()
|
|
||||||
.findOne('email', crediential)
|
.findOne('email', crediential)
|
||||||
.orWhere('phone_number', crediential);
|
.orWhere('phone_number', crediential);
|
||||||
});
|
});
|
||||||
@@ -39,7 +38,6 @@ export default class SystemUserRepository extends SystemRepository {
|
|||||||
|
|
||||||
return this.cache.get(cacheKey, () => {
|
return this.cache.get(cacheKey, () => {
|
||||||
return this.model.query()
|
return this.model.query()
|
||||||
.whereNotDeleted()
|
|
||||||
.findOne({ id: userId, tenant_id: tenantId });
|
.findOne({ id: userId, tenant_id: tenantId });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -53,7 +51,7 @@ export default class SystemUserRepository extends SystemRepository {
|
|||||||
const cacheKey = this.getCacheKey('findOneByEmail', email);
|
const cacheKey = this.getCacheKey('findOneByEmail', email);
|
||||||
|
|
||||||
return this.cache.get(cacheKey, () => {
|
return this.cache.get(cacheKey, () => {
|
||||||
return this.model.query().whereNotDeleted().findOne('email', email);
|
return this.model.query().findOne('email', email);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +65,6 @@ export default class SystemUserRepository extends SystemRepository {
|
|||||||
|
|
||||||
return this.cache.get(cacheKey, () => {
|
return this.cache.get(cacheKey, () => {
|
||||||
return this.model.query()
|
return this.model.query()
|
||||||
.whereNotDeleted()
|
|
||||||
.findOne('phoneNumber', phoneNumber);
|
.findOne('phoneNumber', phoneNumber);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user