import { CanActivate, ExecutionContext, Inject, Injectable, SetMetadata, UnauthorizedException, } from '@nestjs/common'; import { TenancyContext } from './TenancyContext.service'; import { Reflector } from '@nestjs/core'; import { IS_PUBLIC_ROUTE } from '../Auth/Auth.constants'; import { IS_TENANT_AGNOSTIC } from './TenancyGlobal.guard'; export const IS_IGNORE_TENANT_SEEDED = 'IS_IGNORE_TENANT_SEEDED'; export const IgnoreTenantSeededRoute = () => SetMetadata(IS_IGNORE_TENANT_SEEDED, true); @Injectable() export class EnsureTenantIsSeededGuard implements CanActivate { constructor( private readonly tenancyContext: TenancyContext, private reflector: Reflector, ) { } /** * Validate the tenant of the current request is seeded. * @param {ExecutionContext} context * @returns {Promise} */ async canActivate(context: ExecutionContext): Promise { const isPublic = this.reflector.getAllAndOverride( IS_PUBLIC_ROUTE, [context.getHandler(), context.getClass()], ); const isIgnoreEnsureTenantSeeded = this.reflector.getAllAndOverride(IS_IGNORE_TENANT_SEEDED, [ context.getHandler(), context.getClass(), ]); const isTenantAgnostic = this.reflector.getAllAndOverride( IS_TENANT_AGNOSTIC, [context.getHandler(), context.getClass()], ); // Skip the guard early if the route marked as public, tenant agnostic or ignored. if (isPublic || isIgnoreEnsureTenantSeeded || isTenantAgnostic) { return true; } const tenant = await this.tenancyContext.getTenant(); if (!tenant) { throw new UnauthorizedException({ message: 'Tenant not found.', errors: [{ type: 'TENANT.NOT.FOUND' }], }); } if (!tenant.seededAt) { throw new UnauthorizedException({ message: 'Tenant database is not seeded with initial data yet.', errors: [{ type: 'TENANT.DATABASE.NOT.SEED' }], }); } return true; } }