From fa3195b3b3652a9f7da75765622e631589dd43d7 Mon Sep 17 00:00:00 2001 From: poduck Date: Mon, 1 Dec 2025 02:41:25 -0500 Subject: [PATCH] feat(multitenancy): Add TenantHeaderMiddleware to support centralized API with tenant switching - Implemented TenantHeaderMiddleware to switch the database tenant schema based on the 'X-Business-Subdomain' request header. - This allows API requests directed to the centralized 'api' subdomain (e.g., api.lvh.me) to correctly access tenant-specific data when the header is present. - Registered the middleware in multitenancy settings after TenantMainMiddleware. --- .../config/settings/multitenancy.py | 1 + smoothschedule/core/middleware.py | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/smoothschedule/config/settings/multitenancy.py b/smoothschedule/config/settings/multitenancy.py index acf40d3..65c50cd 100644 --- a/smoothschedule/config/settings/multitenancy.py +++ b/smoothschedule/config/settings/multitenancy.py @@ -94,6 +94,7 @@ MIDDLEWARE = [ # 1. Tenant resolution 'django_tenants.middleware.main.TenantMainMiddleware', + 'core.middleware.TenantHeaderMiddleware', # Support tenant switching via header # 2. Security middleware 'django.middleware.security.SecurityMiddleware', diff --git a/smoothschedule/core/middleware.py b/smoothschedule/core/middleware.py index 922a47f..aa53cc1 100644 --- a/smoothschedule/core/middleware.py +++ b/smoothschedule/core/middleware.py @@ -8,11 +8,35 @@ import json from django.utils.deprecation import MiddlewareMixin from django.utils import timezone from django.db import connection +from django_tenants.utils import get_tenant_model logger = logging.getLogger('smoothschedule.security.masquerade') sandbox_logger = logging.getLogger('smoothschedule.sandbox') +class TenantHeaderMiddleware(MiddlewareMixin): + """ + Middleware to switch tenant based on 'X-Business-Subdomain' header. + Allows centralized API domain (e.g., api.smoothschedule.com) to serve tenant-specific data. + Must be placed AFTER TenantMainMiddleware. + """ + def process_request(self, request): + # Check for header + subdomain = request.META.get('HTTP_X_BUSINESS_SUBDOMAIN') + if subdomain: + Tenant = get_tenant_model() + try: + tenant = Tenant.objects.get(schema_name=subdomain) + # Only switch if different from current tenant (which might be 'public') + if request.tenant.schema_name != tenant.schema_name: + request.tenant = tenant + connection.set_tenant(request.tenant) + # sandbox_logger.debug(f"Switched to tenant '{subdomain}' via header") + except Tenant.DoesNotExist: + # Invalid subdomain in header - ignore or could raise 400 + pass + + class SandboxModeMiddleware(MiddlewareMixin): """ Middleware to switch between live and sandbox schemas based on: