refactor(nestjs): banking modules

This commit is contained in:
Ahmed Bouhuolia
2025-06-03 21:42:09 +02:00
parent 5595478e19
commit f87bd341e9
33 changed files with 516 additions and 138 deletions

View File

@@ -5,6 +5,7 @@ import { EnsureTenantIsSeededGuard } from "./EnsureTenantIsSeeded.guards";
import { APP_GUARD } from "@nestjs/core";
import { TenancyContext } from "./TenancyContext.service";
import { TenantController } from "./Tenant.controller";
import { TenancyInitializeModelsGuard } from "./TenancyInitializeModels.guard";
@Module({
@@ -23,6 +24,10 @@ import { TenantController } from "./Tenant.controller";
{
provide: APP_GUARD,
useClass: EnsureTenantIsSeededGuard
},
{
provide: APP_GUARD,
useClass: TenancyInitializeModelsGuard
}
]
})

View File

@@ -0,0 +1,54 @@
import {
CanActivate,
ExecutionContext,
Inject,
Injectable,
SetMetadata,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { IS_PUBLIC_ROUTE } from '../Auth/Auth.constants';
import { TENANT_MODELS_INIT } from './TenantModelsInitialize.module';
export const IGNORE_TENANT_MODELS_INITIALIZE =
'IGNORE_TENANT_MODELS_INITIALIZE';
export const IgnoreTenantModelsInitialize = () =>
SetMetadata(IGNORE_TENANT_MODELS_INITIALIZE, true);
@Injectable()
export class TenancyInitializeModelsGuard implements CanActivate {
constructor(
@Inject(TENANT_MODELS_INIT)
private readonly tenantModelsInit: () => Promise<boolean>,
private reflector: Reflector,
) {}
/**
* Initialize tenant models if the route is decorated with TriggerTenantModelsInitialize.
* @param {ExecutionContext} context
* @returns {Promise<boolean>}
*/
async canActivate(context: ExecutionContext): Promise<boolean> {
const isPublic = this.reflector.getAllAndOverride<boolean>(
IS_PUBLIC_ROUTE,
[context.getHandler(), context.getClass()],
);
// Skip initialization for public routes
if (isPublic) {
return true;
}
const shouldIgnoreInitialization =
this.reflector.getAllAndOverride<boolean>(
IGNORE_TENANT_MODELS_INITIALIZE,
[context.getHandler(), context.getClass()],
);
// Initialize models unless the route is decorated with IgnoreTenantModelsInitialize
if (!shouldIgnoreInitialization) {
try {
await this.tenantModelsInit();
} catch (error) {
console.error('Failed to initialize tenant models:', error);
}
}
return true;
}
}

View File

@@ -0,0 +1,54 @@
import { ContextIdFactory, ModuleRef } from '@nestjs/core';
import { ClsModule } from 'nestjs-cls';
import { Global, Module } from '@nestjs/common';
import { Knex } from 'knex';
import { initialize } from 'objection';
import { TENANCY_DB_CONNECTION } from './TenancyDB/TenancyDB.constants';
const RegisteredModels = [
'SaleInvoice',
'Bill',
'Expense',
'BankTransaction',
'MatchedBankTransaction',
'ManualJournalEntry',
'Account',
'UncategorizedBankTransaction',
'RecognizedBankTransaction',
];
export const TENANT_MODELS_INIT = 'TENANT_MODELS_INIT';
const provider = ClsModule.forFeatureAsync({
provide: TENANT_MODELS_INIT,
inject: [TENANCY_DB_CONNECTION, ModuleRef],
useFactory: (tenantKnex: () => Knex, moduleRef: ModuleRef) => async () => {
const knexInstance = tenantKnex();
const contextId = ContextIdFactory.create();
const models = await Promise.all(
RegisteredModels.map((model) => {
return moduleRef.resolve(model, contextId, { strict: false });
}),
);
const modelsInstances = models.map((model) => model());
if (modelsInstances.length > 0) {
try {
// Initialize all models with the knex instance
await initialize(knexInstance, modelsInstances);
} catch (error) {
console.error('Error initializing models:', error);
throw error;
}
}
return true;
},
strict: true,
type: 'function',
});
@Module({
imports: [provider],
exports: [provider],
})
@Global()
export class TenantModelsInitializeModule {}