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}")