mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 12:20:31 +00:00
refactor(nestjs): hook up auth endpoints
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
import {
|
||||
CallHandler,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NestInterceptor,
|
||||
} from '@nestjs/common';
|
||||
import { Observable, map } from 'rxjs';
|
||||
import { mapValues, mapValuesDeep } from '@/utils/deepdash';
|
||||
|
||||
@Injectable()
|
||||
export class ToJsonInterceptor implements NestInterceptor {
|
||||
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
||||
return next.handle().pipe(
|
||||
map((data) => {
|
||||
return mapValuesDeep(data, (value) => {
|
||||
if (value && typeof value.toJSON === 'function') {
|
||||
return value.toJSON();
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import './utils/moment-mysql';
|
||||
import { AppModule } from './modules/App/App.module';
|
||||
import { ServiceErrorFilter } from './common/filters/service-error.filter';
|
||||
import { ValidationPipe } from './common/pipes/ClassValidation.pipe';
|
||||
import { ToJsonInterceptor } from './common/interceptors/to-json.interceptor';
|
||||
|
||||
global.__static_dirname = path.join(__dirname, '../static');
|
||||
global.__views_dirname = path.join(global.__static_dirname, '/views');
|
||||
@@ -18,6 +19,8 @@ async function bootstrap() {
|
||||
// create and mount the middleware manually here
|
||||
app.use(new ClsMiddleware({}).use);
|
||||
|
||||
app.useGlobalInterceptors(new ToJsonInterceptor());
|
||||
|
||||
// use the validation pipe globally
|
||||
app.useGlobalPipes(new ValidationPipe());
|
||||
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
// @ts-nocheck
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Inject,
|
||||
Param,
|
||||
Post,
|
||||
Request,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiBody, ApiParam } from '@nestjs/swagger';
|
||||
import { JwtAuthGuard, PublicRoute } from './guards/jwt.guard';
|
||||
import { PublicRoute } from './guards/jwt.guard';
|
||||
import { AuthenticationApplication } from './AuthApplication.sevice';
|
||||
import { AuthSignupDto } from './dtos/AuthSignup.dto';
|
||||
import { AuthSigninDto } from './dtos/AuthSignin.dto';
|
||||
import { LocalAuthGuard } from './guards/local.guard';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { AuthSigninService } from './commands/AuthSignin.service';
|
||||
import { TenantModel } from '../System/models/TenantModel';
|
||||
import { SystemUser } from '../System/models/SystemUser';
|
||||
|
||||
@Controller('/auth')
|
||||
@ApiTags('Auth')
|
||||
@@ -24,15 +25,25 @@ export class AuthController {
|
||||
constructor(
|
||||
private readonly authApp: AuthenticationApplication,
|
||||
private readonly authSignin: AuthSigninService,
|
||||
|
||||
@Inject(TenantModel.name)
|
||||
private readonly tenantModel: typeof TenantModel,
|
||||
) {}
|
||||
|
||||
@Post('/signin')
|
||||
@UseGuards(LocalAuthGuard)
|
||||
@ApiOperation({ summary: 'Sign in a user' })
|
||||
@ApiBody({ type: AuthSigninDto })
|
||||
signin(@Request() req: Request, @Body() signinDto: AuthSigninDto) {
|
||||
async signin(@Request() req: Request & { user: SystemUser }, @Body() signinDto: AuthSigninDto) {
|
||||
const { user } = req;
|
||||
return { access_token: this.authSignin.signToken(user) };
|
||||
const tenant = await this.tenantModel.query().findById(user.tenantId);
|
||||
|
||||
return {
|
||||
accessToken: this.authSignin.signToken(user),
|
||||
organizationId: tenant.organizationId,
|
||||
tenantId: tenant.id,
|
||||
userId: user.id,
|
||||
};
|
||||
}
|
||||
|
||||
@Post('/signup')
|
||||
|
||||
@@ -28,11 +28,14 @@ import { MailModule } from '../Mail/Mail.module';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { InjectSystemModel } from '../System/SystemModels/SystemModels.module';
|
||||
import { GetAuthMetaService } from './queries/GetAuthMeta.service';
|
||||
import { AuthedController } from './Authed.controller';
|
||||
import { GetAuthenticatedAccount } from './queries/GetAuthedAccount.service';
|
||||
import { TenancyModule } from '../Tenancy/Tenancy.module';
|
||||
|
||||
const models = [InjectSystemModel(PasswordReset)];
|
||||
|
||||
@Module({
|
||||
controllers: [AuthController],
|
||||
controllers: [AuthController, AuthedController],
|
||||
imports: [
|
||||
MailModule,
|
||||
PassportModule.register({ defaultStrategy: 'jwt' }),
|
||||
@@ -45,9 +48,9 @@ const models = [InjectSystemModel(PasswordReset)];
|
||||
}),
|
||||
}),
|
||||
TenantDBManagerModule,
|
||||
TenancyModule,
|
||||
BullModule.registerQueue({ name: SendResetPasswordMailQueue }),
|
||||
BullModule.registerQueue({ name: SendSignupVerificationMailQueue }),
|
||||
|
||||
],
|
||||
exports: [...models],
|
||||
providers: [
|
||||
@@ -65,6 +68,7 @@ const models = [InjectSystemModel(PasswordReset)];
|
||||
SendResetPasswordMailProcessor,
|
||||
SendSignupVerificationMailProcessor,
|
||||
GetAuthMetaService,
|
||||
GetAuthenticatedAccount,
|
||||
{
|
||||
provide: APP_GUARD,
|
||||
useClass: JwtAuthGuard,
|
||||
|
||||
23
packages/server/src/modules/Auth/Authed.controller.ts
Normal file
23
packages/server/src/modules/Auth/Authed.controller.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { GetAuthenticatedAccount } from './queries/GetAuthedAccount.service';
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { IgnoreTenantSeededRoute } from '../Tenancy/EnsureTenantIsSeeded.guards';
|
||||
import { IgnoreTenantInitializedRoute } from '../Tenancy/EnsureTenantIsInitialized.guard';
|
||||
|
||||
@Controller('/auth')
|
||||
@ApiTags('Auth')
|
||||
@IgnoreTenantSeededRoute()
|
||||
@IgnoreTenantInitializedRoute()
|
||||
export class AuthedController {
|
||||
constructor(
|
||||
private readonly getAuthedAccountService: GetAuthenticatedAccount,
|
||||
) {}
|
||||
|
||||
@Get('/account')
|
||||
@ApiOperation({ summary: 'Retrieve the authenticated account' })
|
||||
async getAuthedAcccount() {
|
||||
const data = await this.getAuthedAccountService.getAccount();
|
||||
|
||||
return { data };
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ export class AuthSigninService {
|
||||
}
|
||||
if (!user.verified) {
|
||||
throw new UnauthorizedException(
|
||||
`The user is not verified yet, check out your mail inbox.`
|
||||
`The user is not verified yet, check out your mail inbox.`,
|
||||
);
|
||||
}
|
||||
return user;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||
import { GetAuthedAccountTransformer } from './GetAuthedAccount.transformer';
|
||||
|
||||
@Injectable()
|
||||
export class GetAuthenticatedAccount {
|
||||
constructor(
|
||||
private readonly tenancyContext: TenancyContext,
|
||||
private readonly transformer: TransformerInjectable,
|
||||
) {}
|
||||
|
||||
async getAccount() {
|
||||
const account = await this.tenancyContext.getSystemUser();
|
||||
|
||||
return this.transformer.transform(
|
||||
account,
|
||||
new GetAuthedAccountTransformer(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Transformer } from '@/modules/Transformer/Transformer';
|
||||
|
||||
export class GetAuthedAccountTransformer extends Transformer {
|
||||
/**
|
||||
* Include these attributes to sale invoice object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'firstName',
|
||||
'lastName',
|
||||
'email',
|
||||
'active',
|
||||
'language',
|
||||
'tenantId',
|
||||
'verified',
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { DashboardService } from './Dashboard.service';
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { DashboardService } from './Dashboard.service';
|
||||
|
||||
@ApiTags('dashboard')
|
||||
@Controller('dashboard')
|
||||
|
||||
@@ -29,8 +29,6 @@ export class DashboardService {
|
||||
|
||||
/**
|
||||
* Retrieve dashboard meta.
|
||||
* @param {number} tenantId
|
||||
* @param {number} authorizedUser
|
||||
*/
|
||||
public getBootMeta = async (): Promise<IDashboardBootMeta> => {
|
||||
// Retrieves all orgnaization abilities.
|
||||
@@ -60,17 +58,19 @@ export class DashboardService {
|
||||
|
||||
/**
|
||||
* Retrieve the boot abilities.
|
||||
* @returns
|
||||
* @returns {Promise<IRoleAbility[]>}
|
||||
*/
|
||||
private getBootAbilities = async (): Promise<IRoleAbility[]> => {
|
||||
const authorizedUser = await this.tenancyContext.getSystemUser();
|
||||
|
||||
const tenantUser = await this.tenantUserModel().query()
|
||||
const tenantUser = await this.tenantUserModel()
|
||||
.query()
|
||||
.findOne('systemUserId', authorizedUser.id)
|
||||
.withGraphFetched('role.permissions');
|
||||
.withGraphFetched('role.permissions')
|
||||
.throwIfNotFound();
|
||||
|
||||
return tenantUser.role.slug === 'admin'
|
||||
? [{ subject: 'all', action: 'manage' }]
|
||||
? [{ subject: 'all', ability: 'manage' }]
|
||||
: this.transformRoleAbility(tenantUser.role.permissions);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
Req,
|
||||
Res,
|
||||
Next,
|
||||
HttpCode,
|
||||
} from '@nestjs/common';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { BuildOrganizationService } from './commands/BuildOrganization.service';
|
||||
@@ -32,60 +33,52 @@ export class OrganizationController {
|
||||
) {}
|
||||
|
||||
@Post('build')
|
||||
@HttpCode(200)
|
||||
@ApiOperation({ summary: 'Build organization database' })
|
||||
@ApiBody({ type: BuildOrganizationDto })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'The organization database has been initialized',
|
||||
})
|
||||
async build(
|
||||
@Body() buildDTO: BuildOrganizationDto,
|
||||
@Req() req: Request,
|
||||
@Res() res: Response,
|
||||
) {
|
||||
async build(@Body() buildDTO: BuildOrganizationDto) {
|
||||
const result = await this.buildOrganizationService.buildRunJob(buildDTO);
|
||||
|
||||
return res.status(200).send({
|
||||
return {
|
||||
type: 'success',
|
||||
code: 'ORGANIZATION.DATABASE.INITIALIZED',
|
||||
message: 'The organization database has been initialized.',
|
||||
data: result,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@Get('current')
|
||||
@HttpCode(200)
|
||||
@ApiOperation({ summary: 'Get current organization' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'Returns the current organization',
|
||||
})
|
||||
async currentOrganization(
|
||||
@Req() req: Request,
|
||||
@Res() res: Response,
|
||||
@Next() next: NextFunction,
|
||||
) {
|
||||
async currentOrganization() {
|
||||
const organization =
|
||||
await this.getCurrentOrgService.getCurrentOrganization();
|
||||
|
||||
return res.status(200).send({ organization });
|
||||
return { organization };
|
||||
}
|
||||
|
||||
@Put()
|
||||
@HttpCode(200)
|
||||
@ApiOperation({ summary: 'Update organization information' })
|
||||
@ApiBody({ type: UpdateOrganizationDto })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'Organization information has been updated successfully',
|
||||
})
|
||||
async updateOrganization(
|
||||
@Body() updateDTO: UpdateOrganizationDto,
|
||||
@Res() res: Response,
|
||||
) {
|
||||
async updateOrganization(@Body() updateDTO: UpdateOrganizationDto) {
|
||||
await this.updateOrganizationService.execute(updateDTO);
|
||||
|
||||
return res.status(200).send({
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Organization information has been updated successfully.',
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import { CommandOrganizationValidators } from './commands/CommandOrganizationVal
|
||||
import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
||||
import { TenantDBManagerModule } from '../TenantDBManager/TenantDBManager.module';
|
||||
import { OrganizationBaseCurrencyLocking } from './Organization/OrganizationBaseCurrencyLocking.service';
|
||||
import { SyncSystemUserToTenantService } from './commands/SyncSystemUserToTenant.service';
|
||||
import { SyncSystemUserToTenantSubscriber } from './subscribers/SyncSystemUserToTenant.subscriber';
|
||||
|
||||
@Module({
|
||||
providers: [
|
||||
@@ -20,6 +22,8 @@ import { OrganizationBaseCurrencyLocking } from './Organization/OrganizationBase
|
||||
OrganizationBuildProcessor,
|
||||
CommandOrganizationValidators,
|
||||
OrganizationBaseCurrencyLocking,
|
||||
SyncSystemUserToTenantService,
|
||||
SyncSystemUserToTenantSubscriber
|
||||
],
|
||||
imports: [
|
||||
BullModule.registerQueue({ name: OrganizationBuildQueue }),
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { pick } from 'lodash';
|
||||
import { Role } from '@/modules/Roles/models/Role.model';
|
||||
import { SystemUser } from '@/modules/System/models/SystemUser';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { TenantUser } from '@/modules/Tenancy/TenancyModels/models/TenantUser.model';
|
||||
|
||||
@Injectable()
|
||||
export class SyncSystemUserToTenantService {
|
||||
constructor(
|
||||
@Inject(TenantUser.name)
|
||||
private readonly tenantUserModel: TenantModelProxy<typeof TenantUser>,
|
||||
|
||||
@Inject(Role.name)
|
||||
private readonly roleModel: TenantModelProxy<typeof Role>,
|
||||
|
||||
@Inject(SystemUser.name)
|
||||
private readonly systemUserModel: typeof SystemUser,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Syncs system user to tenant user.
|
||||
* @param {number} systemUserId - System user id.
|
||||
*/
|
||||
public async syncSystemUserToTenant(systemUserId: number) {
|
||||
const adminRole = await this.roleModel().query().findOne('slug', 'admin');
|
||||
const systemUser = await this.systemUserModel
|
||||
.query()
|
||||
.findById(systemUserId);
|
||||
|
||||
await this.tenantUserModel()
|
||||
.query()
|
||||
.insert({
|
||||
...pick(systemUser, [
|
||||
'firstName',
|
||||
'lastName',
|
||||
'phoneNumber',
|
||||
'email',
|
||||
'active',
|
||||
'inviteAcceptedAt',
|
||||
]),
|
||||
systemUserId: systemUser.id,
|
||||
roleId: adminRole.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||
import { throwIfTenantNotExists } from '../Organization/_utils';
|
||||
import { TenantModel } from '@/modules/System/models/TenantModel';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ModelObject } from 'objection';
|
||||
|
||||
@Injectable()
|
||||
export class GetCurrentOrganizationService {
|
||||
@@ -12,7 +13,7 @@ export class GetCurrentOrganizationService {
|
||||
* @param {number} tenantId
|
||||
* @returns {Promise<ITenant[]>}
|
||||
*/
|
||||
async getCurrentOrganization(): Promise<TenantModel> {
|
||||
async getCurrentOrganization(): Promise<ModelObject<TenantModel>> {
|
||||
const tenant = await this.tenancyContext
|
||||
.getTenant()
|
||||
.withGraphFetched('subscriptions')
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { SyncSystemUserToTenantService } from '../commands/SyncSystemUserToTenant.service';
|
||||
import { events } from '@/common/events/events';
|
||||
import { IOrganizationBuildEventPayload } from '../Organization.types';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class SyncSystemUserToTenantSubscriber {
|
||||
constructor(
|
||||
private readonly syncSystemUserToTenantService: SyncSystemUserToTenantService,
|
||||
) {}
|
||||
|
||||
@OnEvent(events.organization.build)
|
||||
async onOrgBuildSyncSystemUser({ systemUser }: IOrganizationBuildEventPayload) {
|
||||
await this.syncSystemUserToTenantService.syncSystemUserToTenant(
|
||||
systemUser.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,15 @@ import { Reflector } from '@nestjs/core';
|
||||
import { IS_PUBLIC_ROUTE } from '../Auth/Auth.constants';
|
||||
|
||||
export const IS_IGNORE_TENANT_INITIALIZED = 'IS_IGNORE_TENANT_INITIALIZED';
|
||||
export const IgnoreTenantInitializedRoute = () => SetMetadata(IS_IGNORE_TENANT_INITIALIZED, true);
|
||||
export const IgnoreTenantInitializedRoute = () =>
|
||||
SetMetadata(IS_IGNORE_TENANT_INITIALIZED, true);
|
||||
|
||||
@Injectable()
|
||||
export class EnsureTenantIsInitializedGuard implements CanActivate {
|
||||
constructor(private readonly tenancyContext: TenancyContext, private reflector: Reflector) {}
|
||||
constructor(
|
||||
private readonly tenancyContext: TenancyContext,
|
||||
private reflector: Reflector,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Validate the tenant of the current request is initialized..
|
||||
@@ -22,10 +26,11 @@ export class EnsureTenantIsInitializedGuard implements CanActivate {
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const isIgnoreEnsureTenantInitialized = this.reflector.getAllAndOverride<boolean>(
|
||||
IS_IGNORE_TENANT_INITIALIZED,
|
||||
[context.getHandler(), context.getClass()],
|
||||
);
|
||||
const isIgnoreEnsureTenantInitialized =
|
||||
this.reflector.getAllAndOverride<boolean>(IS_IGNORE_TENANT_INITIALIZED, [
|
||||
context.getHandler(),
|
||||
context.getClass(),
|
||||
]);
|
||||
const isPublic = this.reflector.getAllAndOverride<boolean>(
|
||||
IS_PUBLIC_ROUTE,
|
||||
[context.getHandler(), context.getClass()],
|
||||
|
||||
@@ -11,11 +11,15 @@ import { Reflector } from '@nestjs/core';
|
||||
import { IS_PUBLIC_ROUTE } from '../Auth/Auth.constants';
|
||||
|
||||
export const IS_IGNORE_TENANT_SEEDED = 'IS_IGNORE_TENANT_SEEDED';
|
||||
export const IgnoreTenantSeededRoute = () => SetMetadata(IS_IGNORE_TENANT_SEEDED, true);
|
||||
export const IgnoreTenantSeededRoute = () =>
|
||||
SetMetadata(IS_IGNORE_TENANT_SEEDED, true);
|
||||
|
||||
@Injectable()
|
||||
export class EnsureTenantIsSeededGuard implements CanActivate {
|
||||
constructor(private readonly tenancyContext: TenancyContext, private reflector: Reflector) {}
|
||||
constructor(
|
||||
private readonly tenancyContext: TenancyContext,
|
||||
private reflector: Reflector,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Validate the tenant of the current request is seeded.
|
||||
@@ -27,15 +31,16 @@ export class EnsureTenantIsSeededGuard implements CanActivate {
|
||||
IS_PUBLIC_ROUTE,
|
||||
[context.getHandler(), context.getClass()],
|
||||
);
|
||||
const isIgnoreEnsureTenantSeeded = this.reflector.getAllAndOverride<boolean>(
|
||||
IS_IGNORE_TENANT_SEEDED,
|
||||
[context.getHandler(), context.getClass()],
|
||||
);
|
||||
const isIgnoreEnsureTenantSeeded =
|
||||
this.reflector.getAllAndOverride<boolean>(IS_IGNORE_TENANT_SEEDED, [
|
||||
context.getHandler(),
|
||||
context.getClass(),
|
||||
]);
|
||||
if (isPublic || isIgnoreEnsureTenantSeeded) {
|
||||
return true;
|
||||
}
|
||||
const tenant = await this.tenancyContext.getTenant();
|
||||
|
||||
|
||||
if (!tenant.seededAt) {
|
||||
throw new UnauthorizedException({
|
||||
message: 'Tenant database is not seeded with initial data yet.',
|
||||
|
||||
@@ -49,6 +49,6 @@ export class TenancyContext {
|
||||
// Get the user from the request headers.
|
||||
const userId = this.cls.get('userId');
|
||||
|
||||
return this.systemUserModel.query().findOne({ id: userId });
|
||||
return this.systemUserModel.query().findById(userId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ export class TenantUser extends TenantBaseModel {
|
||||
* Relationship mapping.
|
||||
*/
|
||||
static get relationMappings() {
|
||||
const Role = require('models/Role');
|
||||
const { Role } = require('../../../Roles/models/Role.model');
|
||||
|
||||
return {
|
||||
/**
|
||||
@@ -57,7 +57,7 @@ export class TenantUser extends TenantBaseModel {
|
||||
*/
|
||||
role: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: Role.default,
|
||||
modelClass: Role,
|
||||
join: {
|
||||
from: 'users.roleId',
|
||||
to: 'roles.id',
|
||||
|
||||
@@ -36,7 +36,7 @@ export class TransformerInjectable {
|
||||
*/
|
||||
async getTenantDateFormat() {
|
||||
const tenant = await this.tenancyContext.getTenant(true);
|
||||
return tenant.metadata.dateFormat;
|
||||
return tenant.metadata?.dateFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,7 +55,8 @@ export class TransformerInjectable {
|
||||
transformer.setContext(context);
|
||||
|
||||
const dateFormat = await this.getTenantDateFormat();
|
||||
transformer.setDateFormat(dateFormat);
|
||||
|
||||
transformer.setDateFormat(dateFormat || 'DD-MM-YYYY');
|
||||
transformer.setOptions(options);
|
||||
|
||||
return transformer.work(object);
|
||||
|
||||
Reference in New Issue
Block a user