[DÚVIDA] Implementando um Registro de Histórico de Alterações para Auditoria em uma Aplicação Web de Gestão de Carros
Tecnologias Utilizadas:
- Frontend: NEXT
- API: NEST
- ORM: PRISMA
- Banco de Dados: POSTRESQL
Descrição:
Estou desenvolvendo uma aplicação web para gestão de carros e gostaria de implementar um registro de histórico de alterações para fins de auditoria futura.
Cenário:
Na minha aplicação, cada carro possui uma página correspondente acessível através da rota "localhost/cars/5" (onde o número 5 representa o ID do carro). Essa página é dividida em várias abas, incluindo "HISTÓRICO", onde desejo armazenar informações sobre as alterações feitas no veículo.
Exemplo de Tabela de Histórico:
A tabela a seguir representa um exemplo de como as informações podem ser registradas:
DATA | USUÁRIO | DETALHES | IP |
---|---|---|---|
24/05/2023 | LUCAS | ALTEROU A PLACA DE XYZET5 PARA ZBR3DA | 192.168.0.125 |
22/05/2023 | ROBERT | CRIOU O CARRO | 192.168.0.125 |
Perguntas:
- Como posso implementar o registro de histórico de alterações na minha aplicação?
- Quais tecnologias devo utilizar para alcançar esse objetivo?
- Existe algum framework ou biblioteca recomendada que possa me auxiliar nesse processo?
- Como tratar essas alterações para mostrar nos detalhes e comparar, por exemplo, se o usuário além de editar a placa, editar a filial do carro, "ALTEROU A PLACA DE XYZET5 PARA ZBR3DA E FILIAL DE SÃO PAULO PARA RIO DE JANEIRO".
Exemplo em Outra Linguagem:
Para ajudar na compreensão do registro de histórico de alterações, você pode conferir um exemplo já implementado em outra linguagem. Acesse o seguinte link público:
https://demo.sgp.net.br/admin/cliente/41/historico/
Credenciais de acesso:
Usuário: demo Senha: demo
Agradeço antecipadamente pela ajuda da comunidade!
-
Para implementar o registro de histórico de alterações, você pode criar uma tabela de histórico que terá um relacionamento direto com a tabela principal (neste caso, a tabela de carros). Sempre que uma alteração for feita, você adicionará um registro no histórico.
-
As tecnologias que você já está usando (Next, Nest, Prisma, PostgreSQL) são suficientes para alcançar esse objetivo. Você não precisa de nenhuma tecnologia adicional.
-
Não há necessidade de um framework ou biblioteca específica para isso. Você pode conseguir isso com as tecnologias que já está usando.
-
Para tratar essas alterações e mostrar nos detalhes, você pode fazer uma lógica manual para verificar quais são as alterações. Por exemplo, antes de fazer a atualização, você pode comparar os dados antigos com os novos e registrar as diferenças na tabela de histórico. Aqui está um exemplo de como você pode fazer isso com o Prisma:
async update(id: number, data: UpdateCarDto, userId: number) {
// Get the old data
const oldData = await this.prisma.cars.findUnique({ where: { id } });
// Compare the old data with the new one and create a description of the changes
let description = '';
if (oldData.plate !== data.plate) {
description += `ALTEROU A PLACA DE ${oldData.plate} PARA ${data.plate} `;
}
if (oldData.branch !== data.branch) {
description += `E FILIAL DE ${oldData.branch} PARA ${data.branch}`;
}
// Update the car
await this.prisma.cars.update({
where: { id },
data,
});
// Add a record to the history
await this.prisma.history.create({
data: {
carId: id,
userId,
details: description,
ip: '192.168.0.125', // You should get the real IP here
},
});
}
Vc até consegue ir pro caminho de criar algo genérico no Prisma com os Middlewares, mas pode dar um pouco de dor de cabeça. No entanto, acho que vale uma POC (Proof of Concept) pra vc experimentar o Middleware do Prisma e ver se resolveria pra vc
Referência: https://www.prisma.io/docs/concepts/components/prisma-client/middleware
Colocando o middleware em um service do Nest, ficaria algo assim:
import { Injectable } from '@nestjs/common';
import { PrismaService } from './prisma.service';
@Injectable()
export class AuditService {
constructor(private prisma: PrismaService) {
this.prisma.$use(async (params, next) => {
if (params.model !== 'Car' || params.action !== 'update') {
return next(params);
}
const oldData = await this.prisma.car.findUnique({ where: { id: params.args.where.id } });
const result = await next(params);
let description = '';
if (oldData.plate !== result.plate) {
description += `ALTEROU A PLACA DE ${oldData.plate} PARA ${result.plate} `;
}
if (oldData.branch !== result.branch) {
description += `E FILIAL DE ${oldData.branch} PARA ${result.branch}`;
}
await this.prisma.history.create({
data: {
carId: params.args.where.id,
userId: userId, // You should get the real user ID here
details: description,
ip: '192.168.0.125', // You should get the real IP here
},
});
return result;
});
}
}
OBS: não testei nenhum código, então é mais pra ter uma ideia de caminho
Meu comentário não responde suas perguntas, mas passa uma visão de uma implementação que já fiz e que talvez possa ser útil para você.
Nessa implementação, considerei que todas as tabelas do sistema poderiam ser auditadas, ou seja, não seria legal criar relacionamento entre tabela de auditoria e demais. Para tal, minha tabela de auditoria possuía um campo do tipo varchar que receberia a tabela auditada, o registro antigo em formato json, o registro atualizado em formato json e uma leitura humana.
Ficando mais ou menos assim:
Tabela auditoria:
Id | Tabela | RegistroAntigo | RegistroNovo | LeituraHumana | CriadoEm |
---|---|---|---|---|---|
1 | Cars | {"id": 133, "modelo": "gol", "ano": 2010} | {"id": 133, "modelo": "gol", "ano": 2023} | Campo "ano" alterado de "2010" para "2023" | 2022-03-04 22:33:00 |
no gerador de crud (phpcrud.ceuvago.com) que fiz ao gerar os códigos com login ativado, você verá que no arquivo API quando o sistema faz requisição de update/, realiza a consulta na base pelo id do item e salva seus dados na tabela auditoria com acrescento de datatime e tipo de mudança o mesmo para requisição delete. recomendo dar uma olhada, quando gerar o codigo é mostrado no browser sem necessidade de baixar
Eu estou justamente implementando algumas POC´s para poder validar qual a melhor solução. No meu caso a necessidade é manter além do histórico da modificação registrar dados do usuário autenticado no sistema, dito isso criei uma classe que permita registrar via campos do tipo JSON as informações, como a ideia é vários sistemas utilizarem tal solução implemente essa poc utilizando Fiber (Golang), usando banco de dados PostgreSQL.
Segue link do repositório https://github.com/guilhermecarvalhocarneiro/go-estudo-fiber-auditoria
Esse é meu primeiro projeto em Go, inclusive nem está com arquitetura limpa.
Como o uriel comentou, basicamente você criaria uma tabela de histórico (o nome que preferir) que teria um relacionamento direto com a tabela principal (carros imagino) a partir dai sempre que o status for alterado você adiciona um registro no histórico
vc usa qual banco de dados? Posso dar um exemplo com algum banco relacional!
Cria uma tabela chamada: Alterações Campos:
id(primary key), data, id_carro(foreign key da tabela carro) id_pessoa(foreign key da tabela de pessoas cadastradas), descrição(da alteração), ip
Este é um exemplo simples que da pra fazer facilmente! O banco pode ser PostgreSQL, MariaDB ou outro relacional!
Com o banco mongoDB da pra fazer isso tbm! Embora ele não seja um banco relacional da pra fazr relações com ele. Desde que não sejam muito complexas!
Ou usar o documento carro e dentro dele colocar as alterações. Lembrando um documento cabe 16 mega(o que é bastante)