Jun 26 MT-1 phase
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
"""
|
||||
app/tenancy/resolver.py
|
||||
-----------------------
|
||||
Resolve an incoming Host header to a tenant by querying the control plane.
|
||||
|
||||
The `control` package is imported lazily inside the function so that the data
|
||||
plane carries no import-time dependency on the control plane when
|
||||
multi-tenancy is disabled.
|
||||
|
||||
Resolution rules:
|
||||
* exact match on tenant_domains.domain (host, lowercased, port stripped)
|
||||
* tenant must be status='active'
|
||||
* custom domains must be verified; subdomains we issue are trusted
|
||||
Returns a detached TenantContext or None.
|
||||
"""
|
||||
|
||||
from app.tenancy.context import TenantContext
|
||||
|
||||
|
||||
def resolve_tenant(host):
|
||||
if not host:
|
||||
return None
|
||||
|
||||
# Lazy import — keeps control plane optional when MT is disabled.
|
||||
from control.base import control_session
|
||||
from control.models import TenantDomain
|
||||
|
||||
with control_session() as s:
|
||||
domain = (
|
||||
s.query(TenantDomain)
|
||||
.filter(TenantDomain.domain == host)
|
||||
.first()
|
||||
)
|
||||
if domain is None:
|
||||
return None
|
||||
if domain.kind == 'custom' and not domain.verified:
|
||||
return None
|
||||
|
||||
tenant = domain.tenant
|
||||
if tenant is None or tenant.status != 'active':
|
||||
return None
|
||||
|
||||
# Materialise everything needed while the session is still open
|
||||
# (db_uri decrypts the stored credential).
|
||||
return TenantContext(
|
||||
id=tenant.id,
|
||||
slug=tenant.slug,
|
||||
name=tenant.name,
|
||||
plan_id=tenant.plan_id,
|
||||
db_uri=tenant.db_uri,
|
||||
)
|
||||
Reference in New Issue
Block a user