Multitenancy

This commit is contained in:
2026-03-19 18:06:44 +01:00
parent 106ed50361
commit e594defc00
15 changed files with 1090 additions and 37 deletions
+63 -2
View File
@@ -4,14 +4,15 @@ Servizio tenant gestione organizzazioni (solo super_admin).
import uuid
from sqlalchemy import select
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.exceptions import ConflictError, NotFoundError
from app.core.security import hash_password
from app.models.mailbox import Mailbox
from app.models.tenant import Tenant
from app.models.user import User
from app.schemas.tenant import TenantCreateRequest, TenantUpdateRequest
from app.schemas.tenant import TenantCreateRequest, TenantResponse, TenantUpdateRequest
class TenantService:
@@ -56,12 +57,72 @@ class TenantService:
raise NotFoundError("tenant")
return tenant
async def get_tenant_with_stats(self, tenant_id: uuid.UUID) -> TenantResponse:
"""Restituisce il tenant con conteggi utenti e caselle."""
tenant = await self.get_tenant(tenant_id)
user_count = (
await self.db.execute(
select(func.count(User.id)).where(
User.tenant_id == tenant_id,
User.is_active == True, # noqa: E712
)
)
).scalar_one()
mailbox_count = (
await self.db.execute(
select(func.count(Mailbox.id)).where(
Mailbox.tenant_id == tenant_id,
Mailbox.status != "deleted",
)
)
).scalar_one()
resp = TenantResponse.model_validate(tenant)
resp.user_count = user_count
resp.mailbox_count = mailbox_count
return resp
async def list_tenants(self) -> list[Tenant]:
result = await self.db.execute(
select(Tenant).order_by(Tenant.created_at.desc())
)
return list(result.scalars().all())
async def list_tenants_with_stats(self) -> list[TenantResponse]:
"""
Restituisce tutti i tenant con conteggi utenti e caselle in una
singola query efficiente (LEFT JOIN con GROUP BY).
"""
stmt = (
select(
Tenant,
func.count(User.id.distinct()).label("user_count"),
func.count(Mailbox.id.distinct()).label("mailbox_count"),
)
.outerjoin(
User,
(User.tenant_id == Tenant.id) & (User.is_active == True), # noqa: E712
)
.outerjoin(
Mailbox,
(Mailbox.tenant_id == Tenant.id) & (Mailbox.status != "deleted"),
)
.group_by(Tenant.id)
.order_by(Tenant.created_at.desc())
)
rows = (await self.db.execute(stmt)).all()
result = []
for row in rows:
tenant_obj, user_count, mailbox_count = row
resp = TenantResponse.model_validate(tenant_obj)
resp.user_count = user_count or 0
resp.mailbox_count = mailbox_count or 0
result.append(resp)
return result
async def update_tenant(
self, tenant_id: uuid.UUID, data: TenantUpdateRequest
) -> Tenant: