api-admim/app/multi_tenant/criar_tenant.py

118 lines
4.5 KiB
Python

from alembic.runtime.migration import MigrationContext
from alembic.config import Config
from alembic.script import ScriptDirectory
from sqlalchemy.ext.asyncio import AsyncEngine
from sqlalchemy import select, insert
from app.database.models import Inquilino, RbacUser, RbacPapel, rbac_papeis_usuario
from app.database.session import sessionmanager
from app.rbac.auth import get_user_db, get_user_manager
from app.rbac.schemas import UserCreate
from fastapi_users.exceptions import UserAlreadyExists
async def check_migrations(engine: AsyncEngine):
"""
Verifica se todas as migrações foram aplicadas.
"""
alembic_config = Config("alembic.ini")
script = ScriptDirectory.from_config(alembic_config)
async with engine.begin() as conn:
def sync_check_migrations(sync_conn):
context = MigrationContext.configure(sync_conn)
current_revision = context.get_current_revision()
latest_revision = script.get_current_head()
if current_revision != latest_revision:
raise RuntimeError(
f"Database is not up-to-date. Current: {current_revision}, Latest: {latest_revision}. "
"Execute migrations before adding new tenants."
)
await conn.run_sync(sync_check_migrations)
async def create_user(session, fk_inquilino_uuid, email, password, is_superuser=False):
"""
Cria um usuário no sistema utilizando o gerenciador de usuários do FastAPI Users.
"""
try:
user_db = await get_user_db(session).__anext__()
user_manager = await get_user_manager(user_db).__anext__()
try:
user = await user_manager.create(
UserCreate(
email=email,
password=password,
is_superuser=is_superuser,
is_active=True,
fk_inquilino_uuid=fk_inquilino_uuid,
)
)
return user.id
except UserAlreadyExists:
result_user = await session.execute(select(RbacUser).filter_by(email=email))
existing_user = result_user.scalars().first()
raise RuntimeError(f"Usuário '{email}' já existe, seu ID é {existing_user.id}")
except Exception as e:
raise RuntimeError(f"Erro ao criar usuário '{email}': {e}")
async def tenant_create(nome: str, email: str, password: str, cpf_cnpj: str):
"""
Cria um novo tenant (inquilino) no sistema, configura o schema específico
e registra um usuário inicial relacionado ao inquilino.
"""
async with sessionmanager.session() as db:
try:
# Verificar se o tenant já existe
result = await db.execute(select(Inquilino).filter_by(cpf_cnpj=cpf_cnpj))
existing_tenant = result.scalars().first()
if existing_tenant:
raise RuntimeError(
f"Tenant com CPF/CNPJ '{cpf_cnpj}' já existe. Nome: {existing_tenant.nome}, "
f"UUID: {existing_tenant.uuid}"
)
# Criar o registro do tenant
tenant = Inquilino(nome=nome, cpf_cnpj=cpf_cnpj)
db.add(tenant)
await db.commit()
await db.refresh(tenant)
# Criar o usuário inicial
user_id = await create_user(
session=db,
fk_inquilino_uuid=tenant.uuid,
email=email,
password=password,
is_superuser=True,
)
# Nova sessão para associar o papel ao usuário
async with sessionmanager.session() as new_db:
# Buscar o papel "Super Administrador"
result_papel = await new_db.execute(select(RbacPapel).filter_by(nome="Super Administrador"))
papel = result_papel.scalars().first()
if not papel:
raise RuntimeError("Papel 'Super Administrador' não encontrado.")
# Relacionar o papel ao usuário
await new_db.execute(
insert(rbac_papeis_usuario).values(
user_uuid=user_id,
papel_uuid=papel.uuid,
)
)
await new_db.commit()
return tenant.uuid
except RuntimeError as e:
raise RuntimeError(f"Erro inesperado durante a criação do cliente: '{nome}': {e}")
except Exception as e:
raise RuntimeError(f"Erro inesperado durante a criação do cliente: '{nome}': {e}")