api-admim/app/database/audit_log.py

469 lines
21 KiB
Python

from sqlalchemy import inspect
from app.database.models import HistoricoAlteracoes, HistoricoDelete, HistoricoUpdate
from datetime import datetime, timezone
# def audit_log(func):
# async def wrapper(*args, **kwargs):
# self = args[0] # O primeiro argumento será 'self', que é a instância do repositório
# session = self.session
#
# # Verificar se o modelo está habilitado para auditoria
# if not getattr(self.model, 'log_auditoria_habilitado', False):
# # Executa a função original sem auditoria
# result = await func(*args, **kwargs)
#
# # Realiza o commit e refresh
# await session.commit()
#
# # Verificar se é uma operação de UPDATE ou DELETE
# db_models = result.get("db_models")
# operation = result.get("operation")
# rowcount = result.get("rowcount")
#
# # Atualiza o db_model com refresh em caso de UPDATE
# if operation == "UPDATE" and db_models:
# for db_model in db_models:
# await session.refresh(db_model)
#
# # Retorna o que a rota espera: o db_model para update ou rowcount para delete
# if operation == "UPDATE":
# if len(db_models) == 1:
# return db_models[0].__dict__ # Retorna o único modelo como dicionário
# else:
# return db_models # Retorna a lista de modelos
# else:
# return rowcount # Retorna rowcount no caso de DELETE
#
# # Caso a auditoria esteja habilitada, continuar com o processo de auditoria
# # Tenta executar a função original e captura o retorno
# result = await func(*args, **kwargs)
#
# # Captura o db_model e a operação realizada
# db_models = result.get("db_models")
# original_models = result.get("original_models")
# operation = result.get("operation")
# rowcount = result.get("rowcount")
#
# # Para operações de UPDATE
# log_entries = []
# if operation == "UPDATE" and db_models:
# timestamp = datetime.now(timezone.utc).replace(tzinfo=None)
#
# # Iterar pelos modelos atualizados
# for db_model, original_model in zip(db_models, original_models):
# inspector = inspect(db_model)
# primary_key = str(inspect(db_model).identity[0])
#
# # Verificar alterações em cada atributo
# for attr in inspector.attrs:
# old_value = original_model.get(attr.key, None)
# new_value = getattr(db_model, attr.key, None)
#
# # Confirmar se houve mudança nos valores
# if old_value != new_value:
# log_entry = Historico_Alteracoes(
# tabela=db_model.__tablename__,
# coluna=attr.key,
# valor_antigo=str(old_value) if old_value else None,
# valor_novo=str(new_value) if new_value else None,
# data_modificacao=timestamp,
# action='UPDATE',
# usuario_id=kwargs.get('user_id'),
# registro_id=primary_key
# )
# log_entries.append(log_entry)
#
# # Verificar se é uma operação de DELETE
# elif operation == "DELETE":
# timestamp_naive = datetime.now(timezone.utc).replace(tzinfo=None)
#
# if isinstance(db_models, list): # Caso seja uma lista de modelos deletados (delete_many)
# for db_model in db_models:
# primary_key = str(inspect(db_model).identity[0])
# deletado = {attr: value for attr, value in db_model.__dict__.items()
# if not isinstance(value, list) and not attr.startswith('_')}
#
# json_deletado = json.dumps(deletado, default=str)
#
# log_entry = Historico_Alteracoes(
# tabela=db_model.__tablename__,
# coluna='N/A', # Especificar que esta é uma operação de DELETE
# valor_antigo=str(deletado), # Salvar o estado completo do modelo deletado
# valor_novo=None, # Valor novo é None porque o registro foi deletado
# data_modificacao=timestamp_naive,
# action='DELETE',
# usuario_id=kwargs.get('user_id'),
# registro_id=primary_key
# )
# log_entries.append(log_entry)
#
# else: # Caso seja apenas um modelo (delete_one)
# primary_key = str(inspect(db_models).identity[0])
# deletado = {attr: value for attr, value in db_models.__dict__.items()
# if not isinstance(value, list) and not attr.startswith('_')}
#
# log_entry = Historico_Alteracoes(
# tabela=db_models.__tablename__,
# coluna='N/A', # Especificar que esta é uma operação de DELETE
# valor_antigo=str(deletado), # Salvar o estado completo do modelo deletado
# valor_novo=None, # Valor novo é None porque o registro foi deletado
# data_modificacao=timestamp_naive,
# action='DELETE',
# usuario_id=kwargs.get('user_id'),
# registro_id=primary_key
# )
# log_entries.append(log_entry)
#
# # Se houver log_entries a serem salvas (no caso de UPDATE ou DELETE)
# if log_entries:
# session.add_all(log_entries)
#
# # Realizar o commit no final, para ambos UPDATE e DELETE
# await session.commit()
#
# # Se for um update, atualiza o db_model com refresh
# if operation == "UPDATE" and db_models:
# for db_model in db_models:
# await session.refresh(db_model)
#
# # Retorna o que a rota espera: o db_model para update ou rowcount para delete
# if operation == "UPDATE":
# if len(db_models) == 1:
# return db_models[0].__dict__ # Retorna o único modelo como dicionário
# else:
# return db_models # Retorna a lista de modelos
# else:
# return rowcount # Retorna rowcount no caso de DELETE
#
# return wrapper
# def audit_log(func):
# async def wrapper(*args, **kwargs):
# self = args[0] # O primeiro argumento será 'self', que é a instância do repositório
# session = self.session
#
# # Verificar se o modelo está habilitado para auditoria
# if not getattr(self.model, 'log_auditoria_habilitado', False):
# # Executa a função original sem auditoria
# result = await func(*args, **kwargs)
#
# # Realiza o commit e refresh
# await session.commit()
#
# # Verificar se é uma operação de UPDATE ou DELETE
# db_models = result.get("db_models")
# operation = result.get("operation")
# rowcount = result.get("rowcount")
#
# # Atualiza o db_model com refresh em caso de UPDATE
# if operation == "UPDATE" and db_models:
# for db_model in db_models:
# await session.refresh(db_model)
# print("refreshing relaizado")
#
# # Retorna o que a rota espera: o db_model para update ou rowcount para delete
# if operation == "UPDATE":
# if len(db_models) == 1:
# print(db_models[0].__dict__)
# return db_models[0].__dict__ # Retorna o único modelo como dicionário
# else:
# return db_models # Retorna a lista de modelos
# else:
# return rowcount # Retorna rowcount no caso de DELETE
#
# # Caso a auditoria esteja habilitada, continuar com o processo de auditoria
# # Tenta executar a função original e captura o retorno
# result = await func(*args, **kwargs)
#
# # Captura o db_model e a operação realizada
# db_models = result.get("db_models")
# original_models = result.get("original_models")
# operation = result.get("operation")
# rowcount = result.get("rowcount")
#
# # Para operações de UPDATE
# log_entries = []
# timestamp = datetime.now(timezone.utc).replace(tzinfo=None)
# if operation == "UPDATE" and db_models:
# # timestamp = datetime.now(timezone.utc).replace(tzinfo=None)
#
# # Iterar pelos modelos atualizados
# for db_model, original_model in zip(db_models, original_models):
# inspector = inspect(db_model)
# primary_key = str(inspect(db_model).identity[0])
#
# # Registro na tabela HistoricoAlteracoes
# log_entry_update = HistoricoAlteracoes(
# tabela=db_model.__tablename__,
# data_modificacao=timestamp,
# action='UPDATE',
# usuario_id=kwargs.get('user_id'),
# registro_id=primary_key
# )
# session.add(log_entry_update)
#
# # Verificar alterações em cada atributo
# for attr in inspector.attrs:
# old_value = original_model.get(attr.key, None)
# new_value = getattr(db_model, attr.key, None)
#
# # Confirmar se houve mudança nos valores
# if old_value != new_value:
# log_update = HistoricoUpdate(
# coluna=attr.key,
# valor_antigo=str(old_value) if old_value else None,
# valor_novo=str(new_value) if new_value else None,
# alteracao=log_entry_update # Relacionando ao log de alteração principal
# )
# session.add(log_update)
#
# # Verificar se é uma operação de DELETE
# elif operation == "DELETE":
# _timestamp_naive = datetime.now(timezone.utc).replace(tzinfo=None)
#
# if isinstance(db_models, list): # Caso seja uma lista de modelos deletados (delete_many)
# for db_model in db_models:
# primary_key = str(inspect(db_model).identity[0])
#
# # Filtra os campos que não são relationship
#
# def is_relationship(attr_name, model):
# """
# Função que verifica se um atributo é do tipo relacionamento no SQLAlchemy
# """
# # Inspeciona o modelo SQLAlchemy
# mapper = inspect(model.__class__)
#
# # Acessa todas as relationships do modelo
# relationships = mapper.relationships
#
# # Verifica se o atributo atual é uma relationship
# return attr_name in relationships
#
# deletado = {attr: value for attr, value in db_model.__dict__.items()
# if
# not isinstance(value, list) and not attr.startswith('_') and not
# is_relationship(attr, db_model)}
#
# # Registro na tabela HistoricoAlteracoes
# log_entry_delete = HistoricoAlteracoes(
# tabela=db_model.__tablename__,
# data_modificacao=timestamp,
# action='DELETE',
# usuario_id=kwargs.get('user_id'),
# registro_id=primary_key
# )
# session.add(log_entry_delete)
#
# log_delete = HistoricoDelete(
# registro_deletado=str(deletado), # Serializar o registro deletado
# alteracao=log_entry_delete # Relacionando ao log de alteração principal
# )
# session.add(log_delete)
#
# else: # Caso seja apenas um modelo (delete_one)
# primary_key = str(inspect(db_models).identity[0])
#
# def is_relationship(attr_name, model):
# """
# Função que verifica se um atributo é do tipo relacionamento no SQLAlchemy
# """
# # Inspeciona o modelo SQLAlchemy
# mapper = inspect(model.__class__)
#
# # Acessa todas as relationships do modelo
# relationships = mapper.relationships
#
# # Verifica se o atributo atual é uma relationship
# return attr_name in relationships
#
# # Filtra os campos que não são relationship
# deletado = {attr: value for attr, value in db_models.__dict__.items()
# if not isinstance(value, list) and not attr.startswith('_') and not
# is_relationship(attr, db_models)}
#
# # Registro na tabela HistoricoAlteracoes
# log_entry_delete = HistoricoAlteracoes(
# tabela=db_models.__tablename__,
# data_modificacao=timestamp,
# action='DELETE',
# usuario_id=kwargs.get('user_id'),
# registro_id=primary_key
# )
# session.add(log_entry_delete)
#
# log_delete = HistoricoDelete(
# registro_deletado=str(deletado), # Serializar o registro deletado
# alteracao=log_entry_delete # Relacionando ao log de alteração principal
# )
# session.add(log_delete)
#
# # Se houver log_entries a serem salvas (no caso de UPDATE ou DELETE)
# if log_entries:
# session.add_all(log_entries)
#
# # Realizar o commit no final, para ambos UPDATE e DELETE
# await session.commit()
#
# # Se for um update, atualiza o db_model com refresh
# if operation == "UPDATE" and db_models:
# for db_model in db_models:
# await session.refresh(db_model)
#
# # Retorna o que a rota espera: o db_model para update ou rowcount para delete
# if operation == "UPDATE":
# if len(db_models) == 1:
# return db_models[0].__dict__ # Retorna o único modelo como dicionário
# else:
# return db_models # Retorna a lista de modelos
# else:
# return rowcount # Retorna rowcount no caso de DELETE
#
# return wrapper
def audit_log(func):
async def wrapper(*args, **kwargs):
self = args[0] # 'self' é a instância do repositório
session = self.session
# Se auditoria não estiver habilitada, processa sem auditoria
if not getattr(self.model, 'log_auditoria_habilitado', False):
result = await func(*args, **kwargs)
await session.commit()
db_models = result.get("db_models")
operation = result.get("operation")
rowcount = result.get("rowcount")
if operation == "UPDATE" and db_models:
if isinstance(db_models, list):
for db_model in db_models:
await session.refresh(db_model)
print("refreshing realizado")
else:
await session.refresh(db_models)
# Retorna exatamente o que a função original produziu
if operation == "UPDATE":
return db_models
else:
return rowcount
# Auditoria habilitada: chama a função original e captura o retorno
result = await func(*args, **kwargs)
db_models = result.get("db_models")
original_models = result.get("original_models")
operation = result.get("operation")
rowcount = result.get("rowcount")
# Variável de controle: se o retorno for um objeto único, single_update será True.
single_update = False
original_db_model = None
if not isinstance(db_models, list):
single_update = True
original_db_model = db_models # Guarda o objeto único original
db_models = [db_models] # Encapsula para processamento da auditoria
original_models = [original_models]
# Processamento da auditoria para UPDATE
if operation == "UPDATE" and db_models:
timestamp = datetime.now(timezone.utc).replace(tzinfo=None)
for db_model, original_model in zip(db_models, original_models):
inspector = inspect(db_model)
primary_key = str(inspect(db_model).identity[0])
log_entry_update = HistoricoAlteracoes(
tabela=db_model.__tablename__,
data_modificacao=timestamp,
action='UPDATE',
usuario_id=kwargs.get('user_id'),
registro_id=primary_key
)
session.add(log_entry_update)
# Itera pelos atributos mapeados e registra alterações
for attr in inspector.attrs:
old_value = original_model.get(attr.key, None)
new_value = getattr(db_model, attr.key, None)
if old_value != new_value:
log_update = HistoricoUpdate(
coluna=attr.key,
valor_antigo=str(old_value) if old_value is not None else None,
valor_novo=str(new_value) if new_value is not None else None,
alteracao=log_entry_update
)
session.add(log_update)
# Processamento da auditoria para DELETE
elif operation == "DELETE":
_timestamp_naive = datetime.now(timezone.utc).replace(tzinfo=None)
if isinstance(db_models, list): # Caso seja delete_many
for db_model in db_models:
primary_key = str(inspect(db_model).identity[0])
def is_relationship(attr_name, model):
mapper = inspect(model.__class__)
return attr_name in mapper.relationships
deletado = {
attr: value
for attr, value in db_model.__dict__.items()
if
not isinstance(value, list) and not attr.startswith('_') and not is_relationship(attr, db_model)
}
log_entry_delete = HistoricoAlteracoes(
tabela=db_model.__tablename__,
data_modificacao=_timestamp_naive,
action='DELETE',
usuario_id=kwargs.get('user_id'),
registro_id=primary_key
)
session.add(log_entry_delete)
log_delete = HistoricoDelete(
registro_deletado=str(deletado),
alteracao=log_entry_delete
)
session.add(log_delete)
else: # Caso delete_one
primary_key = str(inspect(db_models).identity[0])
def is_relationship(attr_name, model):
mapper = inspect(model.__class__)
return attr_name in mapper.relationships
deletado = {
attr: value
for attr, value in db_models.__dict__.items()
if not isinstance(value, list) and not attr.startswith('_') and not is_relationship(attr, db_models)
}
log_entry_delete = HistoricoAlteracoes(
tabela=db_models.__tablename__,
data_modificacao=_timestamp_naive,
action='DELETE',
usuario_id=kwargs.get('user_id'),
registro_id=primary_key
)
session.add(log_entry_delete)
log_delete = HistoricoDelete(
registro_deletado=str(deletado),
alteracao=log_entry_delete
)
session.add(log_delete)
# Realiza o commit final após registrar a auditoria
await session.commit()
# Para operações de UPDATE, faz refresh dos objetos
if operation == "UPDATE" and db_models:
for db_model in db_models:
await session.refresh(db_model)
# Retorno final: se for update e se for update_by_id (single_update=True),
# retorna o objeto único; caso contrário, retorna a lista, mantendo o contrato original.
if operation == "UPDATE":
return original_db_model if single_update else db_models
else:
return rowcount
return wrapper