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.
This commit is contained in:
poduck
2025-12-01 02:41:25 -05:00
parent 980b5d36aa
commit fa3195b3b3
2 changed files with 25 additions and 0 deletions

View File

@@ -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',

View File

@@ -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: