From 5b6e7ee363f66b632d83022dc225e131971fa4b7 Mon Sep 17 00:00:00 2001 From: hailin Date: Sat, 21 Feb 2026 23:58:01 -0800 Subject: [PATCH] fix: add TenantContextMiddleware to initialize tenant context from X-Tenant-Id header All services using TenantAwareRepository require AsyncLocalStorage tenant context to set the correct PostgreSQL search_path. The middleware reads X-Tenant-Id from request headers and wraps the request with TenantContextService.run(), using schema naming convention it0_t_{tenantId}. Co-Authored-By: Claude Opus 4.6 --- .../shared/database/src/database.module.ts | 9 +++++-- packages/shared/database/src/index.ts | 1 + .../database/src/tenant-context.middleware.ts | 24 +++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 packages/shared/database/src/tenant-context.middleware.ts diff --git a/packages/shared/database/src/database.module.ts b/packages/shared/database/src/database.module.ts index 52755ec..5878e3e 100644 --- a/packages/shared/database/src/database.module.ts +++ b/packages/shared/database/src/database.module.ts @@ -1,9 +1,14 @@ -import { DynamicModule, Module } from '@nestjs/common'; +import { DynamicModule, MiddlewareConsumer, Module, NestModule } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { ConfigService } from '@nestjs/config'; +import { TenantContextMiddleware } from './tenant-context.middleware'; @Module({}) -export class DatabaseModule { +export class DatabaseModule implements NestModule { + configure(consumer: MiddlewareConsumer) { + consumer.apply(TenantContextMiddleware).forRoutes('*'); + } + static forRoot(): DynamicModule { return { module: DatabaseModule, diff --git a/packages/shared/database/src/index.ts b/packages/shared/database/src/index.ts index b80b2db..232512f 100644 --- a/packages/shared/database/src/index.ts +++ b/packages/shared/database/src/index.ts @@ -1,3 +1,4 @@ export * from './tenant-aware.repository'; export * from './database.module'; export * from './tenant-provisioning.service'; +export * from './tenant-context.middleware'; diff --git a/packages/shared/database/src/tenant-context.middleware.ts b/packages/shared/database/src/tenant-context.middleware.ts new file mode 100644 index 0000000..534000b --- /dev/null +++ b/packages/shared/database/src/tenant-context.middleware.ts @@ -0,0 +1,24 @@ +import { Injectable, NestMiddleware } from '@nestjs/common'; +import { Request, Response, NextFunction } from 'express'; +import { TenantContextService } from '@it0/common'; +import { TenantInfo } from '@it0/common'; + +@Injectable() +export class TenantContextMiddleware implements NestMiddleware { + use(req: Request, res: Response, next: NextFunction) { + const tenantId = req.headers['x-tenant-id'] as string; + + if (!tenantId) { + return next(); + } + + const tenantInfo: TenantInfo = { + tenantId, + tenantName: tenantId, + plan: 'free', + schemaName: `it0_t_${tenantId}`, + }; + + TenantContextService.run(tenantInfo, () => next()); + } +}