Lint errors
Signed-off-by: Rodrigo Martins <rodrigo.sousa.577@gmail.com>
This commit is contained in:
1 parent
9742d62ba2
commit
5acec6b71e
196 files changed
+10885
-6655
No files matched your search
+21
-19
@@ -1,13 +1,15 @@
|
||||
import express from 'express';
|
||||
import bodyParser from 'body-parser';
|
||||
import cors from 'cors';
|
||||
import dotenv from 'dotenv';
|
||||
import path from 'path';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import provasRoutes from './routes/provas';
|
||||
import calculosJudiciaisRoutes from './routes/calculosJudiciais';
|
||||
import indicesMonetariosRoutes from './routes/indicesMonetarios';
|
||||
import pensaoAlimenticiaRoutes from './routes/pensaoAlimenticia';
|
||||
import path from "path";
|
||||
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import bodyParser from "body-parser";
|
||||
import cors from "cors";
|
||||
import dotenv from "dotenv";
|
||||
import express from "express";
|
||||
|
||||
import calculosJudiciaisRoutes from "./routes/calculosJudiciais";
|
||||
import indicesMonetariosRoutes from "./routes/indicesMonetarios";
|
||||
import pensaoAlimenticiaRoutes from "./routes/pensaoAlimenticia";
|
||||
import provasRoutes from "./routes/provas";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
@@ -15,21 +17,21 @@ const app = express();
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
// Configurar para confiar em proxies, permitindo a leitura de X-Forwarded-For
|
||||
// Isso é necessário se sua aplicação estiver atrás de um balanceador de carga,
|
||||
// Isso é necessário se sua aplicação estiver atrás de um balanceador de carga,
|
||||
// proxy reverso ou Cloudflare
|
||||
app.set('trust proxy', true);
|
||||
app.set("trust proxy", true);
|
||||
|
||||
app.use(cors());
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
|
||||
app.use('/api/provas', provasRoutes);
|
||||
app.use('/api/calculos', calculosJudiciaisRoutes);
|
||||
app.use('/api/indices', indicesMonetariosRoutes);
|
||||
app.use('/api/pensao-alimenticia', pensaoAlimenticiaRoutes);
|
||||
app.use("/api/provas", provasRoutes);
|
||||
app.use("/api/calculos", calculosJudiciaisRoutes);
|
||||
app.use("/api/indices", indicesMonetariosRoutes);
|
||||
app.use("/api/pensao-alimenticia", pensaoAlimenticiaRoutes);
|
||||
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
app.set('trust proxy', true); // Importante para obter o IP real em ambientes com proxy
|
||||
app.use(express.static(path.join(__dirname, "public")));
|
||||
app.set("trust proxy", true); // Importante para obter o IP real em ambientes com proxy
|
||||
|
||||
const PORT = process.env.PORT || 5000;
|
||||
|
||||
@@ -37,4 +39,4 @@ app.listen(PORT, () => {
|
||||
console.log(`Server is running on port ${PORT}`);
|
||||
});
|
||||
|
||||
export { app, prisma };
|
||||
export { app, prisma };
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import jwt from "jsonwebtoken";
|
||||
|
||||
declare global {
|
||||
namespace Express {
|
||||
@@ -13,18 +13,25 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
export const extractUser = (req: Request, res: Response, next: NextFunction) => {
|
||||
export const extractUser = (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction,
|
||||
) => {
|
||||
try {
|
||||
const token = req.headers.authorization?.replace('Bearer ', '');
|
||||
const token = req.headers.authorization?.replace("Bearer ", "");
|
||||
if (!token) {
|
||||
return res.status(401).json({ error: "Token não fornecido" });
|
||||
}
|
||||
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'default-secret') as jwt.JwtPayload;
|
||||
const decoded = jwt.verify(
|
||||
token,
|
||||
process.env.JWT_SECRET || "default-secret",
|
||||
) as jwt.JwtPayload;
|
||||
req.user = {
|
||||
id: decoded.id,
|
||||
email: decoded.email,
|
||||
perfil: decoded.perfil
|
||||
perfil: decoded.perfil,
|
||||
};
|
||||
next();
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
export const prisma = new PrismaClient({
|
||||
log: process.env.PRISMA_LOG_LEVEL === 'debug'
|
||||
? ['query', 'info', 'warn', 'error']
|
||||
: ['error'],
|
||||
log:
|
||||
process.env.PRISMA_LOG_LEVEL === "debug"
|
||||
? ["query", "info", "warn", "error"]
|
||||
: ["error"],
|
||||
});
|
||||
|
||||
// Tratar eventos de desconexão
|
||||
prisma.$on('beforeExit', async () => {
|
||||
prisma.$on("beforeExit", async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
|
||||
|
||||
+111
-85
@@ -1,30 +1,35 @@
|
||||
import express from "express";
|
||||
import cors from "cors";
|
||||
import axios from "axios";
|
||||
import express from "express";
|
||||
|
||||
import "dotenv/config";
|
||||
import { prisma } from './prismaClient'; // Corrigir o import do prisma
|
||||
import { AuthService } from "../services/AuthService";
|
||||
import { UsuarioRepository } from "../repositories/UsuarioRepository";
|
||||
import jwt from "jsonwebtoken";
|
||||
|
||||
import { authMiddleware } from "../middleware/auth";
|
||||
import { ClienteRepository } from "../repositories/ClienteRepository";
|
||||
import { ProcessoRepository } from "../repositories/ProcessoRepository";
|
||||
import { OrgaoJulgadorRepository } from "../repositories/OrgaoJulgadorRepository";
|
||||
import { CustaProcessualRepository } from "../repositories/CustaProcessualRepository";
|
||||
import { HonorarioRepository } from "../repositories/HonorarioRepository";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { authMiddleware } from '../middleware/auth';
|
||||
|
||||
import bcrypt from "bcryptjs";
|
||||
import { TipoAtendimento } from "@prisma/client"; // Adicionar esta importação
|
||||
import tarefasRoutes from './routes/tarefas';
|
||||
import dashboardRoutes from './routes/dashboard';
|
||||
import atendimentosRoutes from './routes/atendimentos';
|
||||
import anexosRoutes from './routes/anexos';
|
||||
import documentosRouter from './routes/documentos';
|
||||
import documentosModelosRouter from './routes/documentos-modelos';
|
||||
import processosAdministrativosRoutes from './routes/processosAdministrativos';
|
||||
import provasRouter from './routes/provas';
|
||||
import calculosJudiciaisRoutes from './routes/calculosJudiciais';
|
||||
import indicesMonetariosRoutes from './routes/indicesMonetarios';
|
||||
import pensaoAlimenticiaRoutes from './routes/pensaoAlimenticia';
|
||||
import axios from "axios";
|
||||
|
||||
import { OrgaoJulgadorRepository } from "../repositories/OrgaoJulgadorRepository";
|
||||
import { ProcessoRepository } from "../repositories/ProcessoRepository";
|
||||
import { UsuarioRepository } from "../repositories/UsuarioRepository";
|
||||
import { AuthService } from "../services/AuthService";
|
||||
|
||||
import { prisma } from "./prismaClient"; // Corrigir o import do prisma
|
||||
import anexosRoutes from "./routes/anexos";
|
||||
import atendimentosRoutes from "./routes/atendimentos";
|
||||
import calculosJudiciaisRoutes from "./routes/calculosJudiciais";
|
||||
import dashboardRoutes from "./routes/dashboard";
|
||||
import documentosRouter from "./routes/documentos";
|
||||
import documentosModelosRouter from "./routes/documentos-modelos";
|
||||
import indicesMonetariosRoutes from "./routes/indicesMonetarios";
|
||||
import pensaoAlimenticiaRoutes from "./routes/pensaoAlimenticia";
|
||||
import processosAdministrativosRoutes from "./routes/processosAdministrativos";
|
||||
import provasRouter from "./routes/provas";
|
||||
import tarefasRoutes from "./routes/tarefas";
|
||||
|
||||
const app = express();
|
||||
const port = process.env.PORT || 5000;
|
||||
@@ -106,9 +111,11 @@ app.get("/auth/verify", async (req, res) => {
|
||||
});
|
||||
|
||||
// Redirecionar para página pública de verificação (atualizar esta rota)
|
||||
app.get('/verificar/:hash', (req, res) => {
|
||||
app.get("/verificar/:hash", (req, res) => {
|
||||
// Redirecionar para a verificação pública (sem login)
|
||||
res.redirect(`${process.env.FRONTEND_URL || 'http://localhost:3000'}/public/verificar/${req.params.hash}`);
|
||||
res.redirect(
|
||||
`${process.env.FRONTEND_URL || "http://localhost:3000"}/public/verificar/${req.params.hash}`,
|
||||
);
|
||||
});
|
||||
|
||||
// Rota para buscar perfil do usuário logado (ajuste do caminho)
|
||||
@@ -127,7 +134,8 @@ app.get("/api/auth/me", async (req, res) => {
|
||||
) as any;
|
||||
console.log("Token decodificado:", decoded);
|
||||
|
||||
const user = await prisma.usuario.findUnique({ // usar o prisma importado
|
||||
const user = await prisma.usuario.findUnique({
|
||||
// usar o prisma importado
|
||||
where: { id: decoded.id },
|
||||
select: {
|
||||
id: true,
|
||||
@@ -285,23 +293,23 @@ app.get("/api/processos/orgaos", async (req, res) => {
|
||||
});
|
||||
|
||||
// Adicionar este endpoint antes das outras rotas de processos
|
||||
app.patch('/api/processos/:id/resultado', async (req, res) => {
|
||||
app.patch("/api/processos/:id/resultado", async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { resultado } = req.body;
|
||||
|
||||
const processo = await prisma.processo.update({
|
||||
where: { id },
|
||||
data: {
|
||||
data: {
|
||||
resultado,
|
||||
dataAtualizacao: new Date()
|
||||
}
|
||||
dataAtualizacao: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
res.json(processo);
|
||||
} catch (error) {
|
||||
console.error('Erro ao atualizar resultado:', error);
|
||||
res.status(500).json({ error: 'Erro ao atualizar resultado do processo' });
|
||||
console.error("Erro ao atualizar resultado:", error);
|
||||
res.status(500).json({ error: "Erro ao atualizar resultado do processo" });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -940,7 +948,7 @@ app.get("/api/atendimentos", async (req, res) => {
|
||||
anexos: true,
|
||||
},
|
||||
orderBy: {
|
||||
dataAtendimento: 'desc',
|
||||
dataAtendimento: "desc",
|
||||
},
|
||||
});
|
||||
res.json(atendimentos);
|
||||
@@ -952,8 +960,9 @@ app.get("/api/atendimentos", async (req, res) => {
|
||||
|
||||
app.post("/api/atendimentos", authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const { clienteId, processoId, tipo, titulo, conteudo, agendamentoId } = req.body;
|
||||
|
||||
const { clienteId, processoId, tipo, titulo, conteudo, agendamentoId } =
|
||||
req.body;
|
||||
|
||||
if (!req.user?.id) {
|
||||
return res.status(401).json({ error: "Usuário não autenticado" });
|
||||
}
|
||||
@@ -964,7 +973,7 @@ app.post("/api/atendimentos", authMiddleware, async (req, res) => {
|
||||
|
||||
// Verificar se o cliente existe
|
||||
const clienteExiste = await prisma.cliente.findUnique({
|
||||
where: { id: clienteId }
|
||||
where: { id: clienteId },
|
||||
});
|
||||
|
||||
if (!clienteExiste) {
|
||||
@@ -980,14 +989,14 @@ app.post("/api/atendimentos", authMiddleware, async (req, res) => {
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
protocolo: 'desc',
|
||||
protocolo: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
const numeroSequencial = ultimoProtocolo
|
||||
? parseInt(ultimoProtocolo.protocolo.split('/')[1]) + 1
|
||||
const numeroSequencial = ultimoProtocolo
|
||||
? parseInt(ultimoProtocolo.protocolo.split("/")[1]) + 1
|
||||
: 1;
|
||||
const protocolo = `${ano}/${numeroSequencial.toString().padStart(6, '0')}`;
|
||||
const protocolo = `${ano}/${numeroSequencial.toString().padStart(6, "0")}`;
|
||||
|
||||
// Criar atendimento usando transaction
|
||||
const atendimento = await prisma.$transaction(async (tx) => {
|
||||
@@ -1001,7 +1010,7 @@ app.post("/api/atendimentos", authMiddleware, async (req, res) => {
|
||||
cliente: { connect: { id: clienteId } },
|
||||
usuario: { connect: { id: req.user!.id } },
|
||||
...(processoId && { processo: { connect: { id: processoId } } }),
|
||||
status: 'ATIVO',
|
||||
status: "ATIVO",
|
||||
dataAtualizacao: new Date(),
|
||||
},
|
||||
include: {
|
||||
@@ -1021,7 +1030,7 @@ app.post("/api/atendimentos", authMiddleware, async (req, res) => {
|
||||
await tx.agendamentoAtendimento.update({
|
||||
where: { id: agendamentoId },
|
||||
data: {
|
||||
status: 'REALIZADO',
|
||||
status: "REALIZADO",
|
||||
atendimento: { connect: { id: atendimento.id } },
|
||||
},
|
||||
});
|
||||
@@ -1033,9 +1042,9 @@ app.post("/api/atendimentos", authMiddleware, async (req, res) => {
|
||||
res.status(201).json(atendimento);
|
||||
} catch (error) {
|
||||
console.error("Erro ao criar atendimento:", error);
|
||||
res.status(400).json({
|
||||
res.status(400).json({
|
||||
error: "Erro ao criar atendimento",
|
||||
details: error instanceof Error ? error.message : "Erro desconhecido"
|
||||
details: error instanceof Error ? error.message : "Erro desconhecido",
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1050,7 +1059,7 @@ app.get("/api/atendimentos/iniciais/:clienteId", async (req, res) => {
|
||||
tipo: TipoAtendimento.INICIAL,
|
||||
},
|
||||
orderBy: {
|
||||
dataAtendimento: 'desc',
|
||||
dataAtendimento: "desc",
|
||||
},
|
||||
});
|
||||
res.json(atendimentosIniciais);
|
||||
@@ -1064,17 +1073,17 @@ app.get("/api/atendimentos/iniciais/:clienteId", async (req, res) => {
|
||||
app.get("/api/clientes/search", async (req, res) => {
|
||||
try {
|
||||
const { q } = req.query;
|
||||
if (!q || typeof q !== 'string' || q.length < 3) {
|
||||
if (!q || typeof q !== "string" || q.length < 3) {
|
||||
return res.status(400).json({ error: "Termo de busca inválido" });
|
||||
}
|
||||
|
||||
const clientes = await prisma.cliente.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
{ nome: { contains: q, mode: 'insensitive' } },
|
||||
{ nome: { contains: q, mode: "insensitive" } },
|
||||
{ cpfCnpj: { contains: q } },
|
||||
{ email: { contains: q, mode: 'insensitive' } }
|
||||
]
|
||||
{ email: { contains: q, mode: "insensitive" } },
|
||||
],
|
||||
},
|
||||
take: 10,
|
||||
});
|
||||
@@ -1089,8 +1098,15 @@ app.get("/api/clientes/search", async (req, res) => {
|
||||
// Modificar a rota de criação de atendimento para incluir atendimentoInicialId
|
||||
app.post("/api/atendimentos", authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const { clienteId, processoId, tipo, titulo, conteudo, atendimentoInicialId } = req.body;
|
||||
|
||||
const {
|
||||
clienteId,
|
||||
processoId,
|
||||
tipo,
|
||||
titulo,
|
||||
conteudo,
|
||||
atendimentoInicialId,
|
||||
} = req.body;
|
||||
|
||||
if (!req.user?.id) {
|
||||
return res.status(401).json({ error: "Usuário não autenticado" });
|
||||
}
|
||||
@@ -1101,19 +1117,22 @@ app.post("/api/atendimentos", authMiddleware, async (req, res) => {
|
||||
|
||||
// Validar se existe atendimentoInicialId quando não for INICIAL
|
||||
if (tipo !== TipoAtendimento.INICIAL && !atendimentoInicialId) {
|
||||
return res.status(400).json({ error: "Atendimento inicial é obrigatório para retornos e encaminhamentos" });
|
||||
return res.status(400).json({
|
||||
error:
|
||||
"Atendimento inicial é obrigatório para retornos e encaminhamentos",
|
||||
});
|
||||
}
|
||||
|
||||
const ano = new Date().getFullYear();
|
||||
const ultimoProtocolo = await prisma.atendimento.findFirst({
|
||||
where: { protocolo: { startsWith: `${ano}/` } },
|
||||
orderBy: { protocolo: 'desc' },
|
||||
orderBy: { protocolo: "desc" },
|
||||
});
|
||||
|
||||
const numeroSequencial = ultimoProtocolo
|
||||
? parseInt(ultimoProtocolo.protocolo.split('/')[1]) + 1
|
||||
const numeroSequencial = ultimoProtocolo
|
||||
? parseInt(ultimoProtocolo.protocolo.split("/")[1]) + 1
|
||||
: 1;
|
||||
const protocolo = `${ano}/${numeroSequencial.toString().padStart(6, '0')}`;
|
||||
const protocolo = `${ano}/${numeroSequencial.toString().padStart(6, "0")}`;
|
||||
|
||||
const data = {
|
||||
protocolo,
|
||||
@@ -1143,9 +1162,9 @@ app.post("/api/atendimentos", authMiddleware, async (req, res) => {
|
||||
res.status(201).json(atendimento);
|
||||
} catch (error) {
|
||||
console.error("Erro ao criar atendimento:", error);
|
||||
res.status(400).json({
|
||||
res.status(400).json({
|
||||
error: "Erro ao criar atendimento",
|
||||
details: error instanceof Error ? error.message : "Erro desconhecido"
|
||||
details: error instanceof Error ? error.message : "Erro desconhecido",
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1173,10 +1192,10 @@ app.get("/api/atendimentos/:id", async (req, res) => {
|
||||
select: {
|
||||
id: true,
|
||||
nome: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1195,12 +1214,18 @@ app.get("/api/atendimentos/:id", async (req, res) => {
|
||||
app.get("/api/agendamentos", async (req, res) => {
|
||||
try {
|
||||
const { data } = req.query;
|
||||
const where = data ? {
|
||||
data: {
|
||||
gte: new Date(data as string),
|
||||
lt: new Date(new Date(data as string).setDate(new Date(data as string).getDate() + 1))
|
||||
}
|
||||
} : {};
|
||||
const where = data
|
||||
? {
|
||||
data: {
|
||||
gte: new Date(data as string),
|
||||
lt: new Date(
|
||||
new Date(data as string).setDate(
|
||||
new Date(data as string).getDate() + 1,
|
||||
),
|
||||
),
|
||||
},
|
||||
}
|
||||
: {};
|
||||
|
||||
const agendamentos = await prisma.agendamentoAtendimento.findMany({
|
||||
where,
|
||||
@@ -1221,7 +1246,7 @@ app.get("/api/agendamentos", async (req, res) => {
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
data: 'asc',
|
||||
data: "asc",
|
||||
},
|
||||
});
|
||||
res.json(agendamentos);
|
||||
@@ -1234,7 +1259,7 @@ app.get("/api/agendamentos", async (req, res) => {
|
||||
app.post("/api/agendamentos", authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const { clienteId, data, observacoes } = req.body;
|
||||
|
||||
|
||||
if (!req.user?.id) {
|
||||
return res.status(401).json({ error: "Usuário não autenticado" });
|
||||
}
|
||||
@@ -1268,7 +1293,7 @@ app.patch("/api/agendamentos/:id/status", authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { status } = req.body;
|
||||
|
||||
|
||||
const agendamento = await prisma.agendamentoAtendimento.update({
|
||||
where: { id },
|
||||
data: { status },
|
||||
@@ -1295,7 +1320,7 @@ app.put("/api/agendamentos/:id", authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { clienteId, data, observacoes, tipo } = req.body;
|
||||
|
||||
|
||||
const agendamento = await prisma.agendamentoAtendimento.update({
|
||||
where: { id },
|
||||
data: {
|
||||
@@ -1325,7 +1350,7 @@ app.put("/api/agendamentos/:id", authMiddleware, async (req, res) => {
|
||||
app.delete("/api/agendamentos/:id", authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
|
||||
await prisma.agendamentoAtendimento.delete({
|
||||
where: { id },
|
||||
});
|
||||
@@ -1362,13 +1387,14 @@ app.patch("/api/atendimentos/:id/status", authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { status, justificativa } = req.body;
|
||||
|
||||
|
||||
const atendimento = await prisma.atendimento.update({
|
||||
where: { id },
|
||||
data: {
|
||||
data: {
|
||||
status,
|
||||
justificativaCancelamento: status === 'CANCELADO' ? justificativa : null,
|
||||
dataAtualizacao: new Date()
|
||||
justificativaCancelamento:
|
||||
status === "CANCELADO" ? justificativa : null,
|
||||
dataAtualizacao: new Date(),
|
||||
},
|
||||
include: {
|
||||
cliente: true,
|
||||
@@ -1389,15 +1415,15 @@ app.patch("/api/atendimentos/:id/status", authMiddleware, async (req, res) => {
|
||||
});
|
||||
|
||||
// Mover estas rotas para o início, antes das outras
|
||||
app.use('/api/anexos', anexosRoutes);
|
||||
app.use('/api/atendimentos', atendimentosRoutes);
|
||||
app.use('/api/provas', provasRouter);
|
||||
app.use("/api/anexos", anexosRoutes);
|
||||
app.use("/api/atendimentos", atendimentosRoutes);
|
||||
app.use("/api/provas", provasRouter);
|
||||
|
||||
app.use('/api/tarefas', tarefasRoutes);
|
||||
app.use('/api/dashboard', authMiddleware, dashboardRoutes);
|
||||
app.use('/api/documentos', documentosRouter);
|
||||
app.use('/api/documentos-modelos', documentosModelosRouter);
|
||||
app.use('/api/processos-administrativos', processosAdministrativosRoutes);
|
||||
app.use('/api/calculos', calculosJudiciaisRoutes);
|
||||
app.use('/api/indices', indicesMonetariosRoutes);
|
||||
app.use('/api/pensao-alimenticia', pensaoAlimenticiaRoutes);
|
||||
app.use("/api/tarefas", tarefasRoutes);
|
||||
app.use("/api/dashboard", authMiddleware, dashboardRoutes);
|
||||
app.use("/api/documentos", documentosRouter);
|
||||
app.use("/api/documentos-modelos", documentosModelosRouter);
|
||||
app.use("/api/processos-administrativos", processosAdministrativosRoutes);
|
||||
app.use("/api/calculos", calculosJudiciaisRoutes);
|
||||
app.use("/api/indices", indicesMonetariosRoutes);
|
||||
app.use("/api/pensao-alimenticia", pensaoAlimenticiaRoutes);
|
||||
@@ -1,45 +1,49 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
|
||||
export const ipDetectionMiddleware = (req: Request, res: Response, next: NextFunction) => {
|
||||
export const ipDetectionMiddleware = (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction,
|
||||
) => {
|
||||
// Extrair o endereço IP real
|
||||
req.realIP = determineRealIP(req);
|
||||
|
||||
|
||||
// Registrar a detecção para debug
|
||||
console.log(`Requisição de ${req.realIP} (original: ${req.ip})`);
|
||||
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
function determineRealIP(req: Request): string {
|
||||
// Array de possíveis fontes de IP, em ordem de preferência
|
||||
const ipSources = [
|
||||
req.headers['x-forwarded-for'],
|
||||
req.headers['x-real-ip'],
|
||||
req.headers['cf-connecting-ip'],
|
||||
req.headers["x-forwarded-for"],
|
||||
req.headers["x-real-ip"],
|
||||
req.headers["cf-connecting-ip"],
|
||||
req.ip,
|
||||
req.socket?.remoteAddress
|
||||
req.socket?.remoteAddress,
|
||||
];
|
||||
|
||||
|
||||
// Filtrar valores nulos e processar x-forwarded-for que pode conter múltiplos IPs
|
||||
for (const source of ipSources) {
|
||||
if (source) {
|
||||
// x-forwarded-for pode ter múltiplos IPs separados por vírgula
|
||||
if (typeof source === 'string' && source.includes(',')) {
|
||||
const firstIP = source.split(',')[0].trim();
|
||||
if (typeof source === "string" && source.includes(",")) {
|
||||
const firstIP = source.split(",")[0].trim();
|
||||
if (firstIP && isValidIP(firstIP)) return firstIP;
|
||||
} else if (isValidIP(source as string)) {
|
||||
return source as string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Fallback para um valor padrão se nenhum IP válido for encontrado
|
||||
return '0.0.0.0';
|
||||
return "0.0.0.0";
|
||||
}
|
||||
|
||||
function isValidIP(ip: string): boolean {
|
||||
// Função simples para verificar se uma string parece um IP válido
|
||||
return !!ip && ip !== 'unknown' && ip !== '::1' && ip !== 'localhost';
|
||||
return !!ip && ip !== "unknown" && ip !== "::1" && ip !== "localhost";
|
||||
}
|
||||
|
||||
// Estender a interface Request para incluir a propriedade realIP
|
||||
@@ -49,4 +53,4 @@ declare global {
|
||||
realIP: string;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
const prisma = new PrismaClient({
|
||||
log: process.env.PRISMA_LOG_LEVEL === 'debug'
|
||||
? ['query', 'info', 'warn', 'error']
|
||||
: ['error'],
|
||||
log:
|
||||
process.env.PRISMA_LOG_LEVEL === "debug"
|
||||
? ["query", "info", "warn", "error"]
|
||||
: ["error"],
|
||||
});
|
||||
|
||||
export { prisma };
|
||||
+29
-21
@@ -1,9 +1,12 @@
|
||||
import { Router } from 'express';
|
||||
import multer from 'multer';
|
||||
import { prisma } from '../prismaClient';
|
||||
import { authMiddleware } from '../../middleware/auth';
|
||||
const { NextCloudService } = require('../../services/nextcloud');
|
||||
import { randomUUID } from 'crypto';
|
||||
import { randomUUID } from "crypto";
|
||||
|
||||
import { Router } from "express";
|
||||
import multer from "multer";
|
||||
|
||||
import { authMiddleware } from "../../middleware/auth";
|
||||
import { prisma } from "../prismaClient";
|
||||
|
||||
const { NextCloudService } = require("../../services/nextcloud");
|
||||
|
||||
const upload = multer({ storage: multer.memoryStorage() });
|
||||
const router = Router();
|
||||
@@ -11,7 +14,7 @@ const router = Router();
|
||||
router.use(authMiddleware);
|
||||
|
||||
// Criar registro de anexo
|
||||
router.post('/', async (req, res) => {
|
||||
router.post("/", async (req, res) => {
|
||||
try {
|
||||
const { nome, tamanho, url, tipo } = req.body;
|
||||
|
||||
@@ -21,39 +24,44 @@ router.post('/', async (req, res) => {
|
||||
nome,
|
||||
tipo,
|
||||
tamanho,
|
||||
url
|
||||
}
|
||||
url,
|
||||
},
|
||||
});
|
||||
|
||||
res.json(anexo);
|
||||
} catch (error) {
|
||||
console.error('Erro ao criar anexo:', error);
|
||||
res.status(500).json({ error: 'Erro ao criar anexo' });
|
||||
console.error("Erro ao criar anexo:", error);
|
||||
res.status(500).json({ error: "Erro ao criar anexo" });
|
||||
}
|
||||
});
|
||||
|
||||
// Deletar anexo
|
||||
router.delete('/:id', async (req, res) => {
|
||||
router.delete("/:id", async (req, res) => {
|
||||
try {
|
||||
await prisma.anexo.delete({
|
||||
where: { id: req.params.id }
|
||||
where: { id: req.params.id },
|
||||
});
|
||||
res.status(204).send();
|
||||
} catch (error) {
|
||||
console.error('Erro ao deletar anexo:', error);
|
||||
res.status(500).json({ error: 'Erro ao deletar anexo' });
|
||||
console.error("Erro ao deletar anexo:", error);
|
||||
res.status(500).json({ error: "Erro ao deletar anexo" });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/upload', upload.single('file'), async (req, res) => {
|
||||
router.post("/upload", upload.single("file"), async (req, res) => {
|
||||
try {
|
||||
if (!req.file || !req.body.clienteNome) {
|
||||
return res.status(400).json({ error: 'Arquivo e nome do cliente são obrigatórios' });
|
||||
return res
|
||||
.status(400)
|
||||
.json({ error: "Arquivo e nome do cliente são obrigatórios" });
|
||||
}
|
||||
|
||||
// Preparar caminho do arquivo
|
||||
const pastaCliente = req.body.clienteNome.toUpperCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
||||
const fileName = `${Date.now()}_${req.file.originalname.replace(/[^a-zA-Z0-9.-]/g, '_')}`;
|
||||
const pastaCliente = req.body.clienteNome
|
||||
.toUpperCase()
|
||||
.normalize("NFD")
|
||||
.replace(/[\u0300-\u036f]/g, "");
|
||||
const fileName = `${Date.now()}_${req.file.originalname.replace(/[^a-zA-Z0-9.-]/g, "_")}`;
|
||||
const filePath = `${pastaCliente}/${fileName}`;
|
||||
|
||||
// Upload para o Nextcloud
|
||||
@@ -61,8 +69,8 @@ router.post('/upload', upload.single('file'), async (req, res) => {
|
||||
|
||||
res.json({ url: filePath });
|
||||
} catch (error) {
|
||||
console.error('Erro no upload:', error);
|
||||
res.status(500).json({ error: 'Erro no upload do arquivo' });
|
||||
console.error("Erro no upload:", error);
|
||||
res.status(500).json({ error: "Erro no upload do arquivo" });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Router } from 'express';
|
||||
import { prisma } from '../prismaClient';
|
||||
import { authMiddleware } from '../../middleware/auth';
|
||||
import multer from 'multer';
|
||||
import { NextCloudService } from '../../services/nextcloud';
|
||||
import { Router } from "express";
|
||||
import multer from "multer";
|
||||
|
||||
import { authMiddleware } from "../../middleware/auth";
|
||||
import { NextCloudService } from "../../services/nextcloud";
|
||||
import { prisma } from "../prismaClient";
|
||||
|
||||
const router = Router();
|
||||
const upload = multer({ storage: multer.memoryStorage() });
|
||||
@@ -12,12 +13,12 @@ router.use(authMiddleware);
|
||||
// ...existing routes...
|
||||
|
||||
// Rota para atualizar atendimento com anexos
|
||||
router.patch('/:id', async (req, res) => {
|
||||
router.patch("/:id", async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { anexos, ...data } = req.body;
|
||||
|
||||
console.log('Atualizando atendimento:', id, { anexos, data }); // Debug log
|
||||
console.log("Atualizando atendimento:", id, { anexos, data }); // Debug log
|
||||
|
||||
const atendimento = await prisma.atendimento.update({
|
||||
where: { id },
|
||||
@@ -30,60 +31,62 @@ router.patch('/:id', async (req, res) => {
|
||||
nome: anexo.nome,
|
||||
url: anexo.url,
|
||||
tipo: anexo.tipo,
|
||||
tamanho: anexo.tamanho
|
||||
}))
|
||||
}
|
||||
}
|
||||
})
|
||||
tamanho: anexo.tamanho,
|
||||
})),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
include: {
|
||||
anexos: true,
|
||||
cliente: true
|
||||
}
|
||||
cliente: true,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('Atendimento atualizado:', atendimento); // Debug log
|
||||
console.log("Atendimento atualizado:", atendimento); // Debug log
|
||||
res.json(atendimento);
|
||||
} catch (error) {
|
||||
console.error('Erro ao atualizar atendimento:', error);
|
||||
res.status(500).json({ error: 'Erro ao atualizar atendimento' });
|
||||
console.error("Erro ao atualizar atendimento:", error);
|
||||
res.status(500).json({ error: "Erro ao atualizar atendimento" });
|
||||
}
|
||||
});
|
||||
|
||||
// Nova rota para upload de arquivo via proxy
|
||||
router.post('/upload', upload.single('file'), async (req, res) => {
|
||||
router.post("/upload", upload.single("file"), async (req, res) => {
|
||||
try {
|
||||
if (!req.file || !req.body.clienteNome) {
|
||||
return res.status(400).json({ error: 'Arquivo e nome do cliente são obrigatórios' });
|
||||
return res
|
||||
.status(400)
|
||||
.json({ error: "Arquivo e nome do cliente são obrigatórios" });
|
||||
}
|
||||
|
||||
// Enviar para o NextCloud usando a estrutura de pastas correta
|
||||
const buffer = req.file.buffer;
|
||||
Object.defineProperty(buffer, 'name', {
|
||||
Object.defineProperty(buffer, "name", {
|
||||
value: req.file.originalname,
|
||||
writable: true,
|
||||
enumerable: true
|
||||
enumerable: true,
|
||||
});
|
||||
|
||||
const filePath = await NextCloudService.uploadFile(
|
||||
buffer,
|
||||
req.body.clienteNome,
|
||||
'atendimento',
|
||||
req.body.protocolo
|
||||
"atendimento",
|
||||
req.body.protocolo,
|
||||
);
|
||||
|
||||
res.json({ url: filePath });
|
||||
} catch (error) {
|
||||
console.error('Erro no upload:', error);
|
||||
res.status(500).json({ error: 'Erro no upload do arquivo' });
|
||||
console.error("Erro no upload:", error);
|
||||
res.status(500).json({ error: "Erro no upload do arquivo" });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/:id', async (req, res) => {
|
||||
router.get("/:id", async (req, res) => {
|
||||
try {
|
||||
const atendimento = await prisma.atendimento.findUnique({
|
||||
where: {
|
||||
id: req.params.id
|
||||
where: {
|
||||
id: req.params.id,
|
||||
},
|
||||
include: {
|
||||
cliente: true,
|
||||
@@ -93,20 +96,20 @@ router.get('/:id', async (req, res) => {
|
||||
id: true,
|
||||
nome: true,
|
||||
oab: true,
|
||||
oabUF: true
|
||||
}
|
||||
}
|
||||
}
|
||||
oabUF: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!atendimento) {
|
||||
return res.status(404).json({ error: 'Atendimento não encontrado' });
|
||||
return res.status(404).json({ error: "Atendimento não encontrado" });
|
||||
}
|
||||
|
||||
res.json(atendimento);
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar atendimento:', error);
|
||||
res.status(500).json({ error: 'Erro ao buscar atendimento' });
|
||||
console.error("Erro ao buscar atendimento:", error);
|
||||
res.status(500).json({ error: "Erro ao buscar atendimento" });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,35 +1,36 @@
|
||||
import { Router } from 'express';
|
||||
import { prisma } from '../prismaClient';
|
||||
import { Router } from "express";
|
||||
|
||||
import { prisma } from "../prismaClient";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/', async (req, res) => {
|
||||
router.get("/", async (req, res) => {
|
||||
try {
|
||||
const { search } = req.query;
|
||||
const where: any = {};
|
||||
|
||||
if (search) {
|
||||
const searchTerms = String(search).split(' ');
|
||||
where.OR = searchTerms.map(term => ({
|
||||
const searchTerms = String(search).split(" ");
|
||||
where.OR = searchTerms.map((term) => ({
|
||||
OR: [
|
||||
{ nome: { contains: term, mode: 'insensitive' } },
|
||||
{ nome: { contains: term, mode: "insensitive" } },
|
||||
{ cpfCnpj: { contains: term } },
|
||||
{ email: { contains: term.toLowerCase() } }
|
||||
]
|
||||
{ email: { contains: term.toLowerCase() } },
|
||||
],
|
||||
}));
|
||||
}
|
||||
|
||||
const clientes = await prisma.cliente.findMany({
|
||||
where,
|
||||
orderBy: {
|
||||
nome: 'asc'
|
||||
}
|
||||
nome: "asc",
|
||||
},
|
||||
});
|
||||
|
||||
res.json(clientes);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Erro ao buscar clientes' });
|
||||
res.status(500).json({ error: "Erro ao buscar clientes" });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
export default router;
|
||||
+111
-105
@@ -1,6 +1,7 @@
|
||||
import { Router } from 'express';
|
||||
import { prisma } from '../prismaClient';
|
||||
import { authMiddleware } from '../../middleware/auth'; // Corrigir caminho da importação
|
||||
import { Router } from "express";
|
||||
|
||||
import { authMiddleware } from "../../middleware/auth"; // Corrigir caminho da importação
|
||||
import { prisma } from "../prismaClient";
|
||||
|
||||
const router = Router();
|
||||
|
||||
@@ -8,190 +9,192 @@ const router = Router();
|
||||
router.use(authMiddleware);
|
||||
|
||||
// Estatísticas de processos
|
||||
router.get('/processos/stats', async (req, res) => {
|
||||
router.get("/processos/stats", async (req, res) => {
|
||||
try {
|
||||
const [total, porTribunal, porStatus] = await Promise.all([
|
||||
prisma.processo.count(),
|
||||
prisma.processo.groupBy({
|
||||
by: ['tribunal'],
|
||||
by: ["tribunal"],
|
||||
_count: true,
|
||||
}),
|
||||
prisma.processo.groupBy({
|
||||
by: ['status'],
|
||||
by: ["status"],
|
||||
_count: true,
|
||||
})
|
||||
}),
|
||||
]);
|
||||
|
||||
res.json({
|
||||
total,
|
||||
porTribunal,
|
||||
porStatus
|
||||
porStatus,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Erro ao buscar estatísticas de processos' });
|
||||
res.status(500).json({ error: "Erro ao buscar estatísticas de processos" });
|
||||
}
|
||||
});
|
||||
|
||||
// Taxa de sucesso
|
||||
router.get('/processos/taxa-sucesso', async (req, res) => {
|
||||
router.get("/processos/taxa-sucesso", async (req, res) => {
|
||||
try {
|
||||
const stats = await prisma.processo.groupBy({
|
||||
by: ['resultado'],
|
||||
by: ["resultado"],
|
||||
_count: true,
|
||||
});
|
||||
|
||||
|
||||
res.json(stats);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Erro ao buscar taxa de sucesso' });
|
||||
res.status(500).json({ error: "Erro ao buscar taxa de sucesso" });
|
||||
}
|
||||
});
|
||||
|
||||
// Estatísticas financeiras
|
||||
router.get('/financeiro/stats', async (req, res) => {
|
||||
router.get("/financeiro/stats", async (req, res) => {
|
||||
try {
|
||||
const stats = await prisma.$transaction(async (tx) => {
|
||||
const [honorarios, custas] = await Promise.all([
|
||||
tx.honorario.aggregate({
|
||||
_sum: { valorTotalCausa: true }
|
||||
_sum: { valorTotalCausa: true },
|
||||
}),
|
||||
tx.custaProcessual.aggregate({
|
||||
_sum: { valor: true }
|
||||
})
|
||||
_sum: { valor: true },
|
||||
}),
|
||||
]);
|
||||
|
||||
return {
|
||||
honorarios: honorarios._sum.valorTotalCausa || 0,
|
||||
custas: custas._sum.valor || 0
|
||||
custas: custas._sum.valor || 0,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
res.json(stats);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Erro ao buscar estatísticas financeiras' });
|
||||
res.status(500).json({ error: "Erro ao buscar estatísticas financeiras" });
|
||||
}
|
||||
});
|
||||
|
||||
// Prazos próximos
|
||||
router.get('/prazos/proximos', async (req, res) => {
|
||||
router.get("/prazos/proximos", async (req, res) => {
|
||||
try {
|
||||
const prazos = await prisma.diligencia.findMany({
|
||||
where: {
|
||||
dataLimite: {
|
||||
gte: new Date(),
|
||||
lte: new Date(Date.now() + 15 * 24 * 60 * 60 * 1000) // próximos 15 dias
|
||||
}
|
||||
lte: new Date(Date.now() + 15 * 24 * 60 * 60 * 1000), // próximos 15 dias
|
||||
},
|
||||
},
|
||||
include: {
|
||||
processo: true
|
||||
processo: true,
|
||||
},
|
||||
orderBy: {
|
||||
dataLimite: 'asc'
|
||||
}
|
||||
dataLimite: "asc",
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
res.json(prazos);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Erro ao buscar prazos próximos' });
|
||||
res.status(500).json({ error: "Erro ao buscar prazos próximos" });
|
||||
}
|
||||
});
|
||||
|
||||
// Corrigir rota de atividades
|
||||
router.get('/atividades', async (req, res) => {
|
||||
router.get("/atividades", async (req, res) => {
|
||||
try {
|
||||
const atividades = await prisma.$transaction(async (tx) => {
|
||||
const [processos, movimentacoes] = await Promise.all([
|
||||
tx.processo.findMany({
|
||||
take: 10,
|
||||
orderBy: { dataAtualizacao: 'desc' },
|
||||
orderBy: { dataAtualizacao: "desc" },
|
||||
select: {
|
||||
id: true,
|
||||
numero: true,
|
||||
dataAtualizacao: true,
|
||||
cliente: {
|
||||
select: {
|
||||
nome: true
|
||||
}
|
||||
}
|
||||
}
|
||||
nome: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
tx.movimentacao.findMany({
|
||||
take: 10,
|
||||
orderBy: { data: 'desc' },
|
||||
orderBy: { data: "desc" },
|
||||
select: {
|
||||
id: true,
|
||||
descricao: true,
|
||||
data: true,
|
||||
processo: {
|
||||
select: {
|
||||
numero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
numero: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
const atividadesProcessadas = [
|
||||
...processos.map(p => ({
|
||||
...processos.map((p) => ({
|
||||
id: p.id,
|
||||
tipo: 'PROCESSO',
|
||||
tipo: "PROCESSO",
|
||||
descricao: `Processo ${p.numero} atualizado`,
|
||||
data: p.dataAtualizacao,
|
||||
detalhes: p.cliente.nome
|
||||
detalhes: p.cliente.nome,
|
||||
})),
|
||||
...movimentacoes.map(m => ({
|
||||
...movimentacoes.map((m) => ({
|
||||
id: m.id,
|
||||
tipo: 'MOVIMENTACAO',
|
||||
tipo: "MOVIMENTACAO",
|
||||
descricao: m.descricao,
|
||||
data: m.data,
|
||||
detalhes: m.processo.numero
|
||||
}))
|
||||
].sort((a, b) => b.data.getTime() - a.data.getTime())
|
||||
.slice(0, 10);
|
||||
detalhes: m.processo.numero,
|
||||
})),
|
||||
]
|
||||
.sort((a, b) => b.data.getTime() - a.data.getTime())
|
||||
.slice(0, 10);
|
||||
|
||||
return atividadesProcessadas;
|
||||
});
|
||||
|
||||
|
||||
res.json(atividades);
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar atividades:', error);
|
||||
res.status(500).json({ error: 'Erro ao buscar atividades recentes' });
|
||||
console.error("Erro ao buscar atividades:", error);
|
||||
res.status(500).json({ error: "Erro ao buscar atividades recentes" });
|
||||
}
|
||||
});
|
||||
|
||||
// Corrigir rota de estatísticas de tarefas
|
||||
router.get('/tarefas/stats', async (req, res) => {
|
||||
router.get("/tarefas/stats", async (req, res) => {
|
||||
try {
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
const [pendentes, atrasadas, concluidas] = await Promise.all([
|
||||
prisma.tarefa.count({
|
||||
where: {
|
||||
status: 'PENDENTE',
|
||||
prisma.tarefa.count({
|
||||
where: {
|
||||
status: "PENDENTE",
|
||||
dataVencimento: {
|
||||
gte: today
|
||||
}
|
||||
}
|
||||
gte: today,
|
||||
},
|
||||
},
|
||||
}),
|
||||
prisma.tarefa.count({
|
||||
where: {
|
||||
status: 'PENDENTE',
|
||||
status: "PENDENTE",
|
||||
dataVencimento: {
|
||||
lt: today
|
||||
}
|
||||
}
|
||||
lt: today,
|
||||
},
|
||||
},
|
||||
}),
|
||||
prisma.tarefa.count({
|
||||
where: {
|
||||
status: 'CONCLUIDA',
|
||||
atualizadoEm: { // Usar atualizadoEm enquanto não tiver dataConclusao
|
||||
status: "CONCLUIDA",
|
||||
atualizadoEm: {
|
||||
// Usar atualizadoEm enquanto não tiver dataConclusao
|
||||
gte: new Date(today.getFullYear(), today.getMonth(), 1),
|
||||
lt: new Date(today.getFullYear(), today.getMonth() + 1, 0)
|
||||
}
|
||||
}
|
||||
})
|
||||
]).catch(error => {
|
||||
console.error('Erro ao consultar tarefas:', error);
|
||||
lt: new Date(today.getFullYear(), today.getMonth() + 1, 0),
|
||||
},
|
||||
},
|
||||
}),
|
||||
]).catch((error) => {
|
||||
console.error("Erro ao consultar tarefas:", error);
|
||||
return [0, 0, 0];
|
||||
});
|
||||
|
||||
@@ -203,59 +206,62 @@ router.get('/tarefas/stats', async (req, res) => {
|
||||
concluidas,
|
||||
total,
|
||||
taxaConclusao: total > 0 ? Math.round((concluidas / total) * 100) : 0,
|
||||
porStatus: { PENDENTE: pendentes }
|
||||
porStatus: { PENDENTE: pendentes },
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar estatísticas de tarefas:', error);
|
||||
res.status(500).json({
|
||||
error: 'Erro ao buscar estatísticas de tarefas',
|
||||
details: error instanceof Error ? error.message : 'Erro desconhecido'
|
||||
console.error("Erro ao buscar estatísticas de tarefas:", error);
|
||||
res.status(500).json({
|
||||
error: "Erro ao buscar estatísticas de tarefas",
|
||||
details: error instanceof Error ? error.message : "Erro desconhecido",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Estatísticas de atendimentos
|
||||
router.get('/atendimentos/stats', async (req, res) => {
|
||||
router.get("/atendimentos/stats", async (req, res) => {
|
||||
try {
|
||||
const hoje = new Date();
|
||||
const inicioMes = new Date(hoje.getFullYear(), hoje.getMonth(), 1);
|
||||
const fimMes = new Date(hoje.getFullYear(), hoje.getMonth() + 1, 0);
|
||||
const proximaSemana = new Date(hoje.setDate(hoje.getDate() + 7));
|
||||
|
||||
const [atendimentosHoje, proximosAtendimentos, totalMes] = await Promise.all([
|
||||
prisma.atendimento.count({
|
||||
where: {
|
||||
dataAtendimento: {
|
||||
gte: new Date(new Date().setHours(0,0,0,0)),
|
||||
lt: new Date(new Date().setHours(23,59,59,999))
|
||||
}
|
||||
}
|
||||
}),
|
||||
prisma.atendimento.count({
|
||||
where: {
|
||||
dataAtendimento: {
|
||||
gte: new Date(),
|
||||
lt: proximaSemana
|
||||
}
|
||||
}
|
||||
}),
|
||||
prisma.atendimento.count({
|
||||
where: {
|
||||
dataAtendimento: {
|
||||
gte: inicioMes,
|
||||
lte: fimMes
|
||||
}
|
||||
}
|
||||
})
|
||||
]);
|
||||
const [atendimentosHoje, proximosAtendimentos, totalMes] =
|
||||
await Promise.all([
|
||||
prisma.atendimento.count({
|
||||
where: {
|
||||
dataAtendimento: {
|
||||
gte: new Date(new Date().setHours(0, 0, 0, 0)),
|
||||
lt: new Date(new Date().setHours(23, 59, 59, 999)),
|
||||
},
|
||||
},
|
||||
}),
|
||||
prisma.atendimento.count({
|
||||
where: {
|
||||
dataAtendimento: {
|
||||
gte: new Date(),
|
||||
lt: proximaSemana,
|
||||
},
|
||||
},
|
||||
}),
|
||||
prisma.atendimento.count({
|
||||
where: {
|
||||
dataAtendimento: {
|
||||
gte: inicioMes,
|
||||
lte: fimMes,
|
||||
},
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
res.json({
|
||||
hoje: atendimentosHoje,
|
||||
proximaSemana: proximosAtendimentos,
|
||||
totalMes: totalMes
|
||||
totalMes: totalMes,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Erro ao buscar estatísticas de atendimentos' });
|
||||
res
|
||||
.status(500)
|
||||
.json({ error: "Erro ao buscar estatísticas de atendimentos" });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,40 +1,41 @@
|
||||
import { Router } from 'express';
|
||||
import { prisma } from '../prismaClient';
|
||||
import { authMiddleware } from '../../middleware/auth';
|
||||
import { TipoDocumento } from '@prisma/client';
|
||||
import { TipoDocumento } from "@prisma/client";
|
||||
import { Router } from "express";
|
||||
|
||||
import { authMiddleware } from "../../middleware/auth";
|
||||
import { prisma } from "../prismaClient";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.use(authMiddleware);
|
||||
|
||||
// Listar modelos de documentos
|
||||
router.get('/', async (req, res) => {
|
||||
router.get("/", async (req, res) => {
|
||||
try {
|
||||
const { tipo } = req.query;
|
||||
|
||||
|
||||
const where = tipo ? { tipo: tipo as TipoDocumento } : {};
|
||||
|
||||
const modelos = await prisma.documentoModelo.findMany({
|
||||
where,
|
||||
orderBy: {
|
||||
criadoEm: 'desc'
|
||||
}
|
||||
criadoEm: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
res.json(modelos);
|
||||
} catch (error) {
|
||||
console.error('Erro ao listar modelos:', error);
|
||||
res.status(500).json({ error: 'Erro ao listar modelos de documentos' });
|
||||
console.error("Erro ao listar modelos:", error);
|
||||
res.status(500).json({ error: "Erro ao listar modelos de documentos" });
|
||||
}
|
||||
});
|
||||
|
||||
// Criar novo modelo
|
||||
router.post('/', async (req, res) => {
|
||||
router.post("/", async (req, res) => {
|
||||
try {
|
||||
const { nome, tipo, descricao, conteudo, variaveis } = req.body;
|
||||
|
||||
|
||||
if (!nome || !tipo || !conteudo) {
|
||||
return res.status(400).json({ error: 'Campos obrigatórios faltando' });
|
||||
return res.status(400).json({ error: "Campos obrigatórios faltando" });
|
||||
}
|
||||
|
||||
const modelo = await prisma.documentoModelo.create({
|
||||
@@ -43,19 +44,19 @@ router.post('/', async (req, res) => {
|
||||
tipo: tipo as TipoDocumento,
|
||||
descricao,
|
||||
conteudo,
|
||||
variaveis: Array.isArray(variaveis) ? variaveis : []
|
||||
}
|
||||
variaveis: Array.isArray(variaveis) ? variaveis : [],
|
||||
},
|
||||
});
|
||||
|
||||
res.status(201).json(modelo);
|
||||
} catch (error) {
|
||||
console.error('Erro ao criar modelo:', error);
|
||||
res.status(500).json({ error: 'Erro ao criar modelo de documento' });
|
||||
console.error("Erro ao criar modelo:", error);
|
||||
res.status(500).json({ error: "Erro ao criar modelo de documento" });
|
||||
}
|
||||
});
|
||||
|
||||
// Atualizar modelo
|
||||
router.put('/:id', async (req, res) => {
|
||||
router.put("/:id", async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const data = req.body;
|
||||
@@ -70,27 +71,27 @@ router.put('/:id', async (req, res) => {
|
||||
tipo: data.tipo,
|
||||
descricao: data.descricao,
|
||||
conteudo: data.conteudo,
|
||||
variaveis: Array.isArray(data.variaveis) ? data.variaveis : []
|
||||
}
|
||||
variaveis: Array.isArray(data.variaveis) ? data.variaveis : [],
|
||||
},
|
||||
});
|
||||
|
||||
res.json(modelo);
|
||||
} catch (error) {
|
||||
console.error('Erro ao atualizar modelo:', error);
|
||||
res.status(500).json({ error: 'Erro ao atualizar modelo de documento' });
|
||||
console.error("Erro ao atualizar modelo:", error);
|
||||
res.status(500).json({ error: "Erro ao atualizar modelo de documento" });
|
||||
}
|
||||
});
|
||||
|
||||
// Excluir modelo
|
||||
router.delete('/:id', async (req, res) => {
|
||||
router.delete("/:id", async (req, res) => {
|
||||
try {
|
||||
await prisma.documentoModelo.delete({
|
||||
where: { id: req.params.id }
|
||||
where: { id: req.params.id },
|
||||
});
|
||||
res.status(204).send();
|
||||
} catch (error) {
|
||||
console.error('Erro ao excluir modelo:', error);
|
||||
res.status(500).json({ error: 'Erro ao excluir modelo de documento' });
|
||||
console.error("Erro ao excluir modelo:", error);
|
||||
res.status(500).json({ error: "Erro ao excluir modelo de documento" });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+124
-114
@@ -1,21 +1,23 @@
|
||||
import { Router } from 'express';
|
||||
import { prisma } from '../prismaClient';
|
||||
import { authMiddleware } from '../../middleware/auth';
|
||||
import { TipoDocumento } from '@prisma/client';
|
||||
import { PdfService } from '../services/PdfService';
|
||||
import multer from 'multer';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
import { TipoDocumento } from "@prisma/client";
|
||||
import { Router } from "express";
|
||||
import multer from "multer";
|
||||
|
||||
import { authMiddleware } from "../../middleware/auth";
|
||||
import { prisma } from "../prismaClient";
|
||||
import { PdfService } from "../services/PdfService";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.use(authMiddleware);
|
||||
|
||||
// Listar documentos
|
||||
router.get('/', async (req, res) => {
|
||||
router.get("/", async (req, res) => {
|
||||
try {
|
||||
const { processoId, atendimentoId, tipo } = req.query;
|
||||
|
||||
|
||||
const where: any = {};
|
||||
if (processoId) where.processoId = processoId;
|
||||
if (atendimentoId) where.atendimentoId = atendimentoId;
|
||||
@@ -27,29 +29,29 @@ router.get('/', async (req, res) => {
|
||||
criadoPor: {
|
||||
select: {
|
||||
id: true,
|
||||
nome: true
|
||||
}
|
||||
}
|
||||
nome: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
criadoEm: 'desc'
|
||||
}
|
||||
criadoEm: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
res.json(documentos);
|
||||
} catch (error) {
|
||||
console.error('Erro ao listar documentos:', error);
|
||||
res.status(500).json({ error: 'Erro ao listar documentos' });
|
||||
console.error("Erro ao listar documentos:", error);
|
||||
res.status(500).json({ error: "Erro ao listar documentos" });
|
||||
}
|
||||
});
|
||||
|
||||
// Criar documento
|
||||
router.post('/', async (req, res) => {
|
||||
router.post("/", async (req, res) => {
|
||||
try {
|
||||
const { nome, tipo, conteudo, processoId, atendimentoId } = req.body;
|
||||
|
||||
|
||||
if (!req.user?.id) {
|
||||
return res.status(401).json({ error: 'Usuário não autenticado' });
|
||||
return res.status(401).json({ error: "Usuário não autenticado" });
|
||||
}
|
||||
|
||||
const documento = await prisma.documento.create({
|
||||
@@ -59,41 +61,41 @@ router.post('/', async (req, res) => {
|
||||
conteudo,
|
||||
processoId,
|
||||
atendimentoId,
|
||||
criadoPorId: req.user.id
|
||||
}
|
||||
criadoPorId: req.user.id,
|
||||
},
|
||||
});
|
||||
|
||||
res.status(201).json(documento);
|
||||
} catch (error) {
|
||||
console.error('Erro ao criar documento:', error);
|
||||
res.status(500).json({ error: 'Erro ao criar documento' });
|
||||
console.error("Erro ao criar documento:", error);
|
||||
res.status(500).json({ error: "Erro ao criar documento" });
|
||||
}
|
||||
});
|
||||
|
||||
// Criar documento a partir de modelo
|
||||
router.post('/from-modelo/:modeloId', async (req, res) => {
|
||||
router.post("/from-modelo/:modeloId", async (req, res) => {
|
||||
try {
|
||||
const { modeloId } = req.params;
|
||||
const { processoId, atendimentoId, variaveis } = req.body;
|
||||
|
||||
|
||||
if (!req.user?.id) {
|
||||
return res.status(401).json({ error: 'Usuário não autenticado' });
|
||||
return res.status(401).json({ error: "Usuário não autenticado" });
|
||||
}
|
||||
|
||||
// Buscar modelo
|
||||
const modelo = await prisma.documentoModelo.findUnique({
|
||||
where: { id: modeloId }
|
||||
where: { id: modeloId },
|
||||
});
|
||||
|
||||
if (!modelo) {
|
||||
return res.status(404).json({ error: 'Modelo não encontrado' });
|
||||
return res.status(404).json({ error: "Modelo não encontrado" });
|
||||
}
|
||||
|
||||
// Buscar dados adicionais se necessário
|
||||
let clienteData;
|
||||
if (variaveis?.cliente?.id) {
|
||||
const cliente = await prisma.cliente.findUnique({
|
||||
where: { id: variaveis.cliente.id }
|
||||
where: { id: variaveis.cliente.id },
|
||||
});
|
||||
if (cliente) {
|
||||
clienteData = cliente;
|
||||
@@ -104,22 +106,22 @@ router.post('/from-modelo/:modeloId', async (req, res) => {
|
||||
let conteudo = modelo.conteudo;
|
||||
if (clienteData) {
|
||||
const substituicoes = {
|
||||
'cliente.nome': clienteData.nome,
|
||||
'cliente.cpf': clienteData.cpfCnpj,
|
||||
'cliente.rg': clienteData.rg,
|
||||
'cliente.estadoCivil': clienteData.estadoCivil,
|
||||
'cliente.profissao': clienteData.profissao,
|
||||
'cliente.enderecoCompleto': clienteData.enderecoCompleto,
|
||||
'data.atualExtenso': new Date().toLocaleDateString('pt-BR', {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric'
|
||||
})
|
||||
"cliente.nome": clienteData.nome,
|
||||
"cliente.cpf": clienteData.cpfCnpj,
|
||||
"cliente.rg": clienteData.rg,
|
||||
"cliente.estadoCivil": clienteData.estadoCivil,
|
||||
"cliente.profissao": clienteData.profissao,
|
||||
"cliente.enderecoCompleto": clienteData.enderecoCompleto,
|
||||
"data.atualExtenso": new Date().toLocaleDateString("pt-BR", {
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
}),
|
||||
};
|
||||
|
||||
Object.entries(substituicoes).forEach(([key, value]) => {
|
||||
const regex = new RegExp(`{{${key}}}`, 'g');
|
||||
conteudo = conteudo.replace(regex, value || '');
|
||||
const regex = new RegExp(`{{${key}}}`, "g");
|
||||
conteudo = conteudo.replace(regex, value || "");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -132,29 +134,29 @@ router.post('/from-modelo/:modeloId', async (req, res) => {
|
||||
processoId,
|
||||
atendimentoId,
|
||||
criadoPorId: req.user.id,
|
||||
modeloId: modelo.id
|
||||
}
|
||||
modeloId: modelo.id,
|
||||
},
|
||||
});
|
||||
|
||||
res.status(201).json({
|
||||
...documento,
|
||||
clienteId: variaveis?.cliente?.id
|
||||
clienteId: variaveis?.cliente?.id,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao criar documento:', error);
|
||||
res.status(500).json({ error: 'Erro ao criar documento' });
|
||||
console.error("Erro ao criar documento:", error);
|
||||
res.status(500).json({ error: "Erro ao criar documento" });
|
||||
}
|
||||
});
|
||||
|
||||
// Atualizar a rota de configurações
|
||||
router.post('/configuracoes', async (req, res) => {
|
||||
router.post("/configuracoes", async (req, res) => {
|
||||
try {
|
||||
const escritorioId = req.user.escritorioId; // Assumindo que você tem o ID do escritório no token
|
||||
|
||||
// Usar upsert para criar ou atualizar
|
||||
const config = await prisma.configuracaoDocumento.upsert({
|
||||
where: {
|
||||
escritorioId
|
||||
escritorioId,
|
||||
},
|
||||
update: {
|
||||
...req.body,
|
||||
@@ -162,8 +164,8 @@ router.post('/configuracoes', async (req, res) => {
|
||||
top: 35,
|
||||
bottom: 29,
|
||||
left: 25,
|
||||
right: 25
|
||||
}
|
||||
right: 25,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
escritorioId,
|
||||
@@ -172,27 +174,27 @@ router.post('/configuracoes', async (req, res) => {
|
||||
top: 35,
|
||||
bottom: 29,
|
||||
left: 25,
|
||||
right: 25
|
||||
}
|
||||
}
|
||||
right: 25,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
res.json(config);
|
||||
} catch (error) {
|
||||
console.error('Erro ao salvar configuração:', error);
|
||||
res.status(500).json({ error: 'Erro ao salvar configuração' });
|
||||
console.error("Erro ao salvar configuração:", error);
|
||||
res.status(500).json({ error: "Erro ao salvar configuração" });
|
||||
}
|
||||
});
|
||||
|
||||
// Rota para buscar última configuração
|
||||
router.get('/configuracoes', async (req, res) => {
|
||||
router.get("/configuracoes", async (req, res) => {
|
||||
try {
|
||||
console.log('Buscando configurações de documento');
|
||||
|
||||
console.log("Buscando configurações de documento");
|
||||
|
||||
const config = await prisma.configuracaoDocumento.findFirst({
|
||||
orderBy: {
|
||||
criadoEm: 'desc'
|
||||
}
|
||||
criadoEm: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
if (!config) {
|
||||
@@ -207,37 +209,37 @@ router.get('/configuracoes', async (req, res) => {
|
||||
top: 113,
|
||||
bottom: 90,
|
||||
left: 35,
|
||||
right: 35
|
||||
}
|
||||
}
|
||||
right: 35,
|
||||
},
|
||||
},
|
||||
});
|
||||
return res.json(defaultConfig);
|
||||
}
|
||||
|
||||
res.json(config);
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar configurações:', error);
|
||||
res.status(500).json({ error: 'Erro ao buscar configurações' });
|
||||
console.error("Erro ao buscar configurações:", error);
|
||||
res.status(500).json({ error: "Erro ao buscar configurações" });
|
||||
}
|
||||
});
|
||||
|
||||
// Atualizar configuração do documento
|
||||
router.post('/configuracao', authMiddleware, async (req, res) => {
|
||||
router.post("/configuracao", authMiddleware, async (req, res) => {
|
||||
try {
|
||||
// Primeiro, tente encontrar uma configuração existente
|
||||
const configExistente = await prisma.configuracaoDocumento.findFirst({
|
||||
where: {
|
||||
escritorioId: req.user.escritorioId
|
||||
}
|
||||
escritorioId: req.user.escritorioId,
|
||||
},
|
||||
});
|
||||
|
||||
let config;
|
||||
|
||||
|
||||
if (configExistente) {
|
||||
// Se existir, atualiza
|
||||
config = await prisma.configuracaoDocumento.update({
|
||||
where: {
|
||||
id: configExistente.id
|
||||
id: configExistente.id,
|
||||
},
|
||||
data: {
|
||||
timbradoUrl: req.body.timbradoUrl,
|
||||
@@ -246,8 +248,8 @@ router.post('/configuracao', authMiddleware, async (req, res) => {
|
||||
timbradoMarginLeft: req.body.timbradoMarginLeft,
|
||||
timbradoMarginRight: req.body.timbradoMarginRight,
|
||||
templatePadrao: req.body.templatePadrao || "",
|
||||
margensTemplate: req.body.margensTemplate
|
||||
}
|
||||
margensTemplate: req.body.margensTemplate,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// Se não existir, cria
|
||||
@@ -260,42 +262,51 @@ router.post('/configuracao', authMiddleware, async (req, res) => {
|
||||
timbradoMarginLeft: req.body.timbradoMarginLeft,
|
||||
timbradoMarginRight: req.body.timbradoMarginRight,
|
||||
templatePadrao: req.body.templatePadrao || "",
|
||||
margensTemplate: req.body.margensTemplate
|
||||
}
|
||||
margensTemplate: req.body.margensTemplate,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
res.json(config);
|
||||
} catch (error) {
|
||||
console.error('Erro ao salvar configuração:', error);
|
||||
res.status(500).json({ error: 'Falha ao salvar configuração do documento' });
|
||||
console.error("Erro ao salvar configuração:", error);
|
||||
res
|
||||
.status(500)
|
||||
.json({ error: "Falha ao salvar configuração do documento" });
|
||||
}
|
||||
});
|
||||
|
||||
// Atualizar documento com manipulação de erro aprimorada
|
||||
router.put('/:id', async (req, res) => {
|
||||
router.put("/:id", async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { nome, tipo, conteudo } = req.body;
|
||||
|
||||
|
||||
// Verificar se o documento existe
|
||||
const documentoExistente = await prisma.documento.findUnique({
|
||||
where: { id },
|
||||
select: { id: true, criadoPorId: true }
|
||||
select: { id: true, criadoPorId: true },
|
||||
});
|
||||
|
||||
|
||||
if (!documentoExistente) {
|
||||
return res.status(404).json({ error: 'Documento não encontrado' });
|
||||
return res.status(404).json({ error: "Documento não encontrado" });
|
||||
}
|
||||
|
||||
|
||||
// Verificar permissão (apenas quem criou ou é admin pode editar)
|
||||
if (documentoExistente.criadoPorId !== req.user.id && req.user.perfil !== 'ADVOGADO_ADMIN') {
|
||||
return res.status(403).json({ error: 'Sem permissão para editar este documento' });
|
||||
if (
|
||||
documentoExistente.criadoPorId !== req.user.id &&
|
||||
req.user.perfil !== "ADVOGADO_ADMIN"
|
||||
) {
|
||||
return res
|
||||
.status(403)
|
||||
.json({ error: "Sem permissão para editar este documento" });
|
||||
}
|
||||
|
||||
// Validar dados
|
||||
if (!nome || !tipo || !conteudo) {
|
||||
return res.status(400).json({ error: 'Dados incompletos para atualização do documento' });
|
||||
return res
|
||||
.status(400)
|
||||
.json({ error: "Dados incompletos para atualização do documento" });
|
||||
}
|
||||
|
||||
// Atualizar o documento
|
||||
@@ -305,8 +316,8 @@ router.put('/:id', async (req, res) => {
|
||||
nome,
|
||||
tipo: tipo as TipoDocumento,
|
||||
conteudo,
|
||||
atualizadoEm: new Date()
|
||||
}
|
||||
atualizadoEm: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
// Registrar evento de atualização (opcional)
|
||||
@@ -321,81 +332,80 @@ router.put('/:id', async (req, res) => {
|
||||
|
||||
res.json(documento);
|
||||
} catch (error) {
|
||||
console.error('Erro ao atualizar documento:', error);
|
||||
res.status(500).json({ error: 'Erro ao atualizar documento' });
|
||||
console.error("Erro ao atualizar documento:", error);
|
||||
res.status(500).json({ error: "Erro ao atualizar documento" });
|
||||
}
|
||||
});
|
||||
|
||||
// Excluir documento
|
||||
router.delete('/:id', async (req, res) => {
|
||||
router.delete("/:id", async (req, res) => {
|
||||
try {
|
||||
await prisma.documento.delete({
|
||||
where: { id: req.params.id }
|
||||
where: { id: req.params.id },
|
||||
});
|
||||
res.status(204).send();
|
||||
} catch (error) {
|
||||
console.error('Erro ao excluir documento:', error);
|
||||
res.status(500).json({ error: 'Erro ao excluir documento' });
|
||||
console.error("Erro ao excluir documento:", error);
|
||||
res.status(500).json({ error: "Erro ao excluir documento" });
|
||||
}
|
||||
});
|
||||
|
||||
// Adicionar nova rota para exportar PDF
|
||||
router.post('/exportar-pdf', async (req, res) => {
|
||||
router.post("/exportar-pdf", async (req, res) => {
|
||||
try {
|
||||
const { conteudo, escritorio } = req.body;
|
||||
|
||||
|
||||
const pdfService = new PdfService();
|
||||
const pdfBuffer = await pdfService.gerarPDF(conteudo, escritorio);
|
||||
|
||||
res.setHeader('Content-Type', 'application/pdf');
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=documento.pdf');
|
||||
res.setHeader("Content-Type", "application/pdf");
|
||||
res.setHeader("Content-Disposition", "attachment; filename=documento.pdf");
|
||||
res.send(pdfBuffer);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Erro ao gerar PDF:', error);
|
||||
res.status(500).json({ error: 'Erro ao gerar PDF' });
|
||||
console.error("Erro ao gerar PDF:", error);
|
||||
res.status(500).json({ error: "Erro ao gerar PDF" });
|
||||
}
|
||||
});
|
||||
|
||||
// Configuração do multer para upload de arquivos
|
||||
const storage = multer.diskStorage({
|
||||
destination: (req, file, cb) => {
|
||||
const uploadDir = path.join(process.cwd(), 'public', 'uploads');
|
||||
const uploadDir = path.join(process.cwd(), "public", "uploads");
|
||||
if (!fs.existsSync(uploadDir)) {
|
||||
fs.mkdirSync(uploadDir, { recursive: true });
|
||||
}
|
||||
cb(null, uploadDir);
|
||||
},
|
||||
filename: (req, file, cb) => {
|
||||
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
|
||||
const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
|
||||
cb(null, `timbrado-${uniqueSuffix}-${file.originalname}`);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const fileFilter = (req: any, file: any, cb: any) => {
|
||||
// Atualizar para aceitar PDF
|
||||
const allowedTypes = ['image/png', 'image/svg+xml', 'application/pdf'];
|
||||
|
||||
const allowedTypes = ["image/png", "image/svg+xml", "application/pdf"];
|
||||
|
||||
if (allowedTypes.includes(file.mimetype)) {
|
||||
cb(null, true);
|
||||
} else {
|
||||
cb(new Error('Formato de arquivo não suportado. Use PNG, SVG ou PDF.'));
|
||||
cb(new Error("Formato de arquivo não suportado. Use PNG, SVG ou PDF."));
|
||||
}
|
||||
};
|
||||
|
||||
const upload = multer({
|
||||
const upload = multer({
|
||||
storage,
|
||||
fileFilter,
|
||||
limits: {
|
||||
fileSize: 5 * 1024 * 1024 // Aumentar para 5MB
|
||||
}
|
||||
fileSize: 5 * 1024 * 1024, // Aumentar para 5MB
|
||||
},
|
||||
});
|
||||
|
||||
// Rota de upload do timbrado
|
||||
router.post('/upload-timbrado', upload.single('timbrado'), async (req, res) => {
|
||||
router.post("/upload-timbrado", upload.single("timbrado"), async (req, res) => {
|
||||
try {
|
||||
if (!req.file) {
|
||||
throw new Error('Nenhum arquivo enviado');
|
||||
throw new Error("Nenhum arquivo enviado");
|
||||
}
|
||||
|
||||
// Criar URL relativa para o arquivo
|
||||
@@ -403,7 +413,7 @@ router.post('/upload-timbrado', upload.single('timbrado'), async (req, res) => {
|
||||
|
||||
res.json({ url: fileUrl });
|
||||
} catch (error: any) {
|
||||
console.error('Erro no upload do timbrado:', error);
|
||||
console.error("Erro no upload do timbrado:", error);
|
||||
res.status(400).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,68 +1,83 @@
|
||||
import express from 'express';
|
||||
import { IndiceMonetarioService, importarIndicesINPC } from '../services/IndiceMonetarioService';
|
||||
import express from "express";
|
||||
|
||||
import {
|
||||
IndiceMonetarioService,
|
||||
importarIndicesINPC,
|
||||
} from "../services/IndiceMonetarioService";
|
||||
|
||||
const router = express.Router();
|
||||
const indiceService = new IndiceMonetarioService();
|
||||
|
||||
// Importar índices INPC (somente admin)
|
||||
router.post('/importar/inpc', async (req, res) => {
|
||||
router.post("/importar/inpc", async (req, res) => {
|
||||
try {
|
||||
// Verificar permissão de admin
|
||||
if (req.user?.perfil !== 'ADVOGADO_ADMIN') {
|
||||
return res.status(403).json({ error: 'Permissão negada' });
|
||||
if (req.user?.perfil !== "ADVOGADO_ADMIN") {
|
||||
return res.status(403).json({ error: "Permissão negada" });
|
||||
}
|
||||
|
||||
|
||||
const resultado = await indiceService.importarIndicesINPC();
|
||||
res.json(resultado);
|
||||
} catch (error) {
|
||||
console.error('Erro ao importar índices INPC:', error);
|
||||
console.error("Erro ao importar índices INPC:", error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Importar índices SELIC (somente admin)
|
||||
router.post('/importar/selic', async (req, res) => {
|
||||
router.post("/importar/selic", async (req, res) => {
|
||||
try {
|
||||
// Verificar permissão de admin
|
||||
if (req.user?.perfil !== 'ADVOGADO_ADMIN') {
|
||||
return res.status(403).json({ error: 'Permissão negada' });
|
||||
if (req.user?.perfil !== "ADVOGADO_ADMIN") {
|
||||
return res.status(403).json({ error: "Permissão negada" });
|
||||
}
|
||||
|
||||
|
||||
const resultado = await indiceService.importarIndicesSELIC();
|
||||
res.json(resultado);
|
||||
} catch (error) {
|
||||
console.error('Erro ao importar índices SELIC:', error);
|
||||
console.error("Erro ao importar índices SELIC:", error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Adicionar índice manualmente
|
||||
router.post('/', async (req, res) => {
|
||||
router.post("/", async (req, res) => {
|
||||
try {
|
||||
const { nome, mesReferencia, anoReferencia, valor, dataPublicacao, fonte } = req.body;
|
||||
|
||||
const { nome, mesReferencia, anoReferencia, valor, dataPublicacao, fonte } =
|
||||
req.body;
|
||||
|
||||
// Verificar permissão de admin
|
||||
if (req.user?.perfil !== 'ADVOGADO_ADMIN') {
|
||||
return res.status(403).json({ error: 'Permissão negada' });
|
||||
if (req.user?.perfil !== "ADVOGADO_ADMIN") {
|
||||
return res.status(403).json({ error: "Permissão negada" });
|
||||
}
|
||||
|
||||
|
||||
// Validação de dados
|
||||
if (!nome || !mesReferencia || !anoReferencia || valor === undefined) {
|
||||
return res.status(400).json({ error: 'Campos obrigatórios faltando' });
|
||||
return res.status(400).json({ error: "Campos obrigatórios faltando" });
|
||||
}
|
||||
|
||||
if (isNaN(parseInt(mesReferencia)) || parseInt(mesReferencia) < 1 || parseInt(mesReferencia) > 12) {
|
||||
return res.status(400).json({ error: 'Mês de referência inválido (deve ser entre 1 e 12)' });
|
||||
|
||||
if (
|
||||
isNaN(parseInt(mesReferencia)) ||
|
||||
parseInt(mesReferencia) < 1 ||
|
||||
parseInt(mesReferencia) > 12
|
||||
) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({ error: "Mês de referência inválido (deve ser entre 1 e 12)" });
|
||||
}
|
||||
|
||||
if (isNaN(parseInt(anoReferencia)) || parseInt(anoReferencia) < 1900 || parseInt(anoReferencia) > 2100) {
|
||||
return res.status(400).json({ error: 'Ano de referência inválido' });
|
||||
|
||||
if (
|
||||
isNaN(parseInt(anoReferencia)) ||
|
||||
parseInt(anoReferencia) < 1900 ||
|
||||
parseInt(anoReferencia) > 2100
|
||||
) {
|
||||
return res.status(400).json({ error: "Ano de referência inválido" });
|
||||
}
|
||||
|
||||
|
||||
if (isNaN(parseFloat(valor))) {
|
||||
return res.status(400).json({ error: 'Valor inválido' });
|
||||
return res.status(400).json({ error: "Valor inválido" });
|
||||
}
|
||||
|
||||
|
||||
const resultado = await indiceService.adicionarIndice({
|
||||
nome,
|
||||
mesReferencia: parseInt(mesReferencia),
|
||||
@@ -71,90 +86,94 @@ router.post('/', async (req, res) => {
|
||||
dataPublicacao: dataPublicacao ? new Date(dataPublicacao) : undefined,
|
||||
fonte,
|
||||
});
|
||||
|
||||
|
||||
res.status(201).json(resultado);
|
||||
} catch (error) {
|
||||
console.error('Erro ao adicionar índice:', error);
|
||||
console.error("Erro ao adicionar índice:", error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Listar índices
|
||||
router.get('/', async (req, res) => {
|
||||
router.get("/", async (req, res) => {
|
||||
try {
|
||||
const { nome, anoReferencia, fonte } = req.query;
|
||||
|
||||
|
||||
const indices = await indiceService.listarIndices({
|
||||
nome: nome as string,
|
||||
anoReferencia: anoReferencia ? parseInt(anoReferencia as string) : undefined,
|
||||
anoReferencia: anoReferencia
|
||||
? parseInt(anoReferencia as string)
|
||||
: undefined,
|
||||
fonte: fonte as string,
|
||||
});
|
||||
|
||||
|
||||
res.json(indices);
|
||||
} catch (error) {
|
||||
console.error('Erro ao listar índices:', error);
|
||||
console.error("Erro ao listar índices:", error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Buscar índice específico
|
||||
router.get('/:nome/:mes/:ano', async (req, res) => {
|
||||
router.get("/:nome/:mes/:ano", async (req, res) => {
|
||||
try {
|
||||
const { nome, mes, ano } = req.params;
|
||||
|
||||
|
||||
// Validação de dados
|
||||
if (isNaN(parseInt(mes)) || parseInt(mes) < 1 || parseInt(mes) > 12) {
|
||||
return res.status(400).json({ error: 'Mês de referência inválido (deve ser entre 1 e 12)' });
|
||||
return res
|
||||
.status(400)
|
||||
.json({ error: "Mês de referência inválido (deve ser entre 1 e 12)" });
|
||||
}
|
||||
|
||||
|
||||
if (isNaN(parseInt(ano)) || parseInt(ano) < 1900 || parseInt(ano) > 2100) {
|
||||
return res.status(400).json({ error: 'Ano de referência inválido' });
|
||||
return res.status(400).json({ error: "Ano de referência inválido" });
|
||||
}
|
||||
|
||||
|
||||
const indice = await indiceService.buscarIndice({
|
||||
nome,
|
||||
mesReferencia: parseInt(mes),
|
||||
anoReferencia: parseInt(ano),
|
||||
});
|
||||
|
||||
|
||||
if (!indice) {
|
||||
return res.status(404).json({ error: 'Índice não encontrado' });
|
||||
return res.status(404).json({ error: "Índice não encontrado" });
|
||||
}
|
||||
|
||||
|
||||
res.json(indice);
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar índice:', error);
|
||||
console.error("Erro ao buscar índice:", error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Remover índice
|
||||
router.delete('/:id', async (req, res) => {
|
||||
router.delete("/:id", async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
|
||||
// Verificar permissão de admin
|
||||
if (req.user?.perfil !== 'ADVOGADO_ADMIN') {
|
||||
return res.status(403).json({ error: 'Permissão negada' });
|
||||
if (req.user?.perfil !== "ADVOGADO_ADMIN") {
|
||||
return res.status(403).json({ error: "Permissão negada" });
|
||||
}
|
||||
|
||||
|
||||
const resultado = await indiceService.removerIndice(id);
|
||||
|
||||
|
||||
res.json({ success: true, data: resultado });
|
||||
} catch (error) {
|
||||
console.error('Erro ao remover índice:', error);
|
||||
console.error("Erro ao remover índice:", error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Adicionar rota para forçar atualização dos índices
|
||||
router.post('/atualizar', async (req, res) => {
|
||||
router.post("/atualizar", async (req, res) => {
|
||||
try {
|
||||
await importarIndicesINPC();
|
||||
res.json({ message: 'Índices atualizados com sucesso' });
|
||||
res.json({ message: "Índices atualizados com sucesso" });
|
||||
} catch (error) {
|
||||
console.error('Erro ao atualizar índices:', error);
|
||||
res.status(500).json({ error: 'Falha ao atualizar índices' });
|
||||
console.error("Erro ao atualizar índices:", error);
|
||||
res.status(500).json({ error: "Falha ao atualizar índices" });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Router } from 'express';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { extractUser } from '../authMiddleware'; // Importar a função correta
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { Router } from "express";
|
||||
|
||||
import { extractUser } from "../authMiddleware"; // Importar a função correta
|
||||
|
||||
const router = Router();
|
||||
const prisma = new PrismaClient();
|
||||
@@ -9,26 +10,26 @@ const prisma = new PrismaClient();
|
||||
router.use(extractUser);
|
||||
|
||||
// Criar novo cálculo de pensão alimentícia
|
||||
router.post('/', async (req, res) => {
|
||||
router.post("/", async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
titulo,
|
||||
descricao,
|
||||
dataInicial,
|
||||
dataFinal,
|
||||
percentualSalarioMinimo,
|
||||
multaPactuada,
|
||||
const {
|
||||
titulo,
|
||||
descricao,
|
||||
dataInicial,
|
||||
dataFinal,
|
||||
percentualSalarioMinimo,
|
||||
multaPactuada,
|
||||
tipoAtualizacao,
|
||||
processoId,
|
||||
clienteId,
|
||||
observacoes
|
||||
observacoes,
|
||||
} = req.body;
|
||||
|
||||
const calculo = await prisma.calculoPensaoAlimenticia.create({
|
||||
data: {
|
||||
titulo,
|
||||
descricao,
|
||||
status: 'ATIVO',
|
||||
status: "ATIVO",
|
||||
dataInicial: new Date(dataInicial),
|
||||
dataFinal: dataFinal ? new Date(dataFinal) : null,
|
||||
percentualSalarioMinimo,
|
||||
@@ -40,22 +41,22 @@ router.post('/', async (req, res) => {
|
||||
processoId,
|
||||
clienteId,
|
||||
observacoes,
|
||||
usuarioId: req.user.id
|
||||
}
|
||||
usuarioId: req.user.id,
|
||||
},
|
||||
});
|
||||
|
||||
res.json(calculo);
|
||||
} catch (error) {
|
||||
console.error('Erro ao criar cálculo de pensão:', error);
|
||||
res.status(500).json({ error: 'Falha ao criar cálculo' });
|
||||
console.error("Erro ao criar cálculo de pensão:", error);
|
||||
res.status(500).json({ error: "Falha ao criar cálculo" });
|
||||
}
|
||||
});
|
||||
|
||||
// Buscar cálculo por ID
|
||||
router.get('/:id', async (req, res) => {
|
||||
router.get("/:id", async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
|
||||
const calculo = await prisma.calculoPensaoAlimenticia.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
@@ -65,25 +66,25 @@ router.get('/:id', async (req, res) => {
|
||||
select: {
|
||||
id: true,
|
||||
nome: true,
|
||||
email: true
|
||||
}
|
||||
}
|
||||
}
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!calculo) {
|
||||
return res.status(404).json({ error: 'Cálculo não encontrado' });
|
||||
return res.status(404).json({ error: "Cálculo não encontrado" });
|
||||
}
|
||||
|
||||
res.json(calculo);
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar cálculo:', error);
|
||||
res.status(500).json({ error: 'Falha ao buscar cálculo' });
|
||||
console.error("Erro ao buscar cálculo:", error);
|
||||
res.status(500).json({ error: "Falha ao buscar cálculo" });
|
||||
}
|
||||
});
|
||||
|
||||
// Listar cálculos
|
||||
router.get('/', async (req, res) => {
|
||||
router.get("/", async (req, res) => {
|
||||
try {
|
||||
const calculos = await prisma.calculoPensaoAlimenticia.findMany({
|
||||
include: {
|
||||
@@ -92,19 +93,19 @@ router.get('/', async (req, res) => {
|
||||
usuario: {
|
||||
select: {
|
||||
id: true,
|
||||
nome: true
|
||||
}
|
||||
}
|
||||
nome: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
criadoEm: 'desc'
|
||||
}
|
||||
criadoEm: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
res.json(calculos);
|
||||
} catch (error) {
|
||||
console.error('Erro ao listar cálculos:', error);
|
||||
res.status(500).json({ error: 'Falha ao listar cálculos' });
|
||||
console.error("Erro ao listar cálculos:", error);
|
||||
res.status(500).json({ error: "Falha ao listar cálculos" });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Router } from 'express';
|
||||
import { prisma } from '../../config/database';
|
||||
import { authMiddleware } from '../../middleware/auth';
|
||||
import { Router } from "express";
|
||||
|
||||
import { prisma } from "../../config/database";
|
||||
import { authMiddleware } from "../../middleware/auth";
|
||||
|
||||
const router = Router();
|
||||
|
||||
// Listar processos
|
||||
router.get('/', authMiddleware, async (req, res) => {
|
||||
router.get("/", authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const processos = await prisma.processo.findMany({
|
||||
include: {
|
||||
@@ -23,18 +24,18 @@ router.get('/', authMiddleware, async (req, res) => {
|
||||
custas: true,
|
||||
},
|
||||
orderBy: {
|
||||
dataCriacao: 'desc',
|
||||
dataCriacao: "desc",
|
||||
},
|
||||
});
|
||||
res.json(processos);
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar processos:', error);
|
||||
res.status(500).json({ error: 'Erro ao buscar processos' });
|
||||
console.error("Erro ao buscar processos:", error);
|
||||
res.status(500).json({ error: "Erro ao buscar processos" });
|
||||
}
|
||||
});
|
||||
|
||||
// Criar processo
|
||||
router.post('/', authMiddleware, async (req, res) => {
|
||||
router.post("/", authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const processo = await prisma.processo.create({
|
||||
data: {
|
||||
@@ -60,13 +61,13 @@ router.post('/', authMiddleware, async (req, res) => {
|
||||
});
|
||||
res.status(201).json(processo);
|
||||
} catch (error) {
|
||||
console.error('Erro ao criar processo:', error);
|
||||
res.status(400).json({ error: 'Erro ao criar processo' });
|
||||
console.error("Erro ao criar processo:", error);
|
||||
res.status(400).json({ error: "Erro ao criar processo" });
|
||||
}
|
||||
});
|
||||
|
||||
// Busca de processo individual
|
||||
router.get('/:id', async (req, res) => {
|
||||
router.get("/:id", async (req, res) => {
|
||||
try {
|
||||
const processo = await prisma.processo.findUnique({
|
||||
where: { id: req.params.id },
|
||||
@@ -92,7 +93,7 @@ router.get('/:id', async (req, res) => {
|
||||
|
||||
// Garantir que sempre tenha um resultado, mesmo que seja NAO_JULGADO
|
||||
if (!processo.resultado) {
|
||||
processo.resultado = 'NAO_JULGADO';
|
||||
processo.resultado = "NAO_JULGADO";
|
||||
}
|
||||
|
||||
res.json(processo);
|
||||
@@ -103,16 +104,16 @@ router.get('/:id', async (req, res) => {
|
||||
});
|
||||
|
||||
// Atualizar resultado do processo
|
||||
router.patch('/:id/resultado', async (req, res) => {
|
||||
router.patch("/:id/resultado", async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { resultado } = req.body;
|
||||
|
||||
const processo = await prisma.processo.update({
|
||||
where: { id },
|
||||
data: {
|
||||
data: {
|
||||
resultado,
|
||||
dataAtualizacao: new Date()
|
||||
dataAtualizacao: new Date(),
|
||||
},
|
||||
include: {
|
||||
cliente: true,
|
||||
@@ -120,19 +121,19 @@ router.patch('/:id/resultado', async (req, res) => {
|
||||
diligencias: true,
|
||||
audiencias: true,
|
||||
documentos: true,
|
||||
movimentacoes: true
|
||||
}
|
||||
movimentacoes: true,
|
||||
},
|
||||
});
|
||||
|
||||
res.json(processo);
|
||||
} catch (error) {
|
||||
console.error('Erro ao atualizar resultado:', error);
|
||||
res.status(500).json({ error: 'Erro ao atualizar resultado do processo' });
|
||||
console.error("Erro ao atualizar resultado:", error);
|
||||
res.status(500).json({ error: "Erro ao atualizar resultado do processo" });
|
||||
}
|
||||
});
|
||||
|
||||
// Atualizar processo
|
||||
router.put('/:id', authMiddleware, async (req, res) => {
|
||||
router.put("/:id", authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { clienteId, ...data } = req.body;
|
||||
@@ -141,9 +142,11 @@ router.put('/:id', authMiddleware, async (req, res) => {
|
||||
where: { id },
|
||||
data: {
|
||||
...data,
|
||||
cliente: clienteId ? {
|
||||
connect: { id: clienteId }
|
||||
} : undefined
|
||||
cliente: clienteId
|
||||
? {
|
||||
connect: { id: clienteId },
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
include: {
|
||||
cliente: true,
|
||||
@@ -153,24 +156,24 @@ router.put('/:id', authMiddleware, async (req, res) => {
|
||||
movimentacoes: true,
|
||||
honorarios: {
|
||||
include: {
|
||||
parcelas: true
|
||||
}
|
||||
parcelas: true,
|
||||
},
|
||||
},
|
||||
custas: true
|
||||
}
|
||||
custas: true,
|
||||
},
|
||||
});
|
||||
res.json(processo);
|
||||
} catch (error) {
|
||||
console.error('Erro ao atualizar processo:', error);
|
||||
res.status(400).json({ error: 'Erro ao atualizar processo' });
|
||||
console.error("Erro ao atualizar processo:", error);
|
||||
res.status(400).json({ error: "Erro ao atualizar processo" });
|
||||
}
|
||||
});
|
||||
|
||||
// Deletar processo
|
||||
router.delete('/:id', authMiddleware, async (req, res) => {
|
||||
router.delete("/:id", authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
|
||||
// Deletar primeiro as relações
|
||||
await prisma.$transaction([
|
||||
prisma.parteProcesso.deleteMany({ where: { processoId: id } }),
|
||||
@@ -179,14 +182,14 @@ router.delete('/:id', authMiddleware, async (req, res) => {
|
||||
prisma.documento.deleteMany({ where: { processoId: id } }),
|
||||
prisma.custaProcessual.deleteMany({ where: { processoId: id } }),
|
||||
prisma.honorario.deleteMany({ where: { processoId: id } }),
|
||||
prisma.processo.delete({ where: { id } })
|
||||
prisma.processo.delete({ where: { id } }),
|
||||
]);
|
||||
|
||||
res.status(204).send();
|
||||
} catch (error) {
|
||||
console.error('Erro ao deletar processo:', error);
|
||||
res.status(400).json({ error: 'Erro ao deletar processo' });
|
||||
console.error("Erro ao deletar processo:", error);
|
||||
res.status(400).json({ error: "Erro ao deletar processo" });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
export default router;
|
||||
@@ -1,11 +1,12 @@
|
||||
import express from 'express';
|
||||
import { ProcessoAdministrativoRepository } from '../../repositories/ProcessoAdministrativoRepository';
|
||||
import express from "express";
|
||||
|
||||
import { ProcessoAdministrativoRepository } from "../../repositories/ProcessoAdministrativoRepository";
|
||||
|
||||
const router = express.Router();
|
||||
const repository = new ProcessoAdministrativoRepository();
|
||||
|
||||
// Listar todos os processos administrativos
|
||||
router.get('/', async (req, res) => {
|
||||
router.get("/", async (req, res) => {
|
||||
try {
|
||||
const processos = await repository.findAll();
|
||||
res.json(processos);
|
||||
@@ -16,12 +17,12 @@ router.get('/', async (req, res) => {
|
||||
});
|
||||
|
||||
// Buscar processo por ID
|
||||
router.get('/:id', async (req, res) => {
|
||||
router.get("/:id", async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
|
||||
// Se o ID for "novo", retornar 404
|
||||
if (id === 'novo') {
|
||||
if (id === "novo") {
|
||||
return res.status(404).json({ error: "Processo não encontrado" });
|
||||
}
|
||||
|
||||
@@ -37,138 +38,138 @@ router.get('/:id', async (req, res) => {
|
||||
});
|
||||
|
||||
// Criar novo processo
|
||||
router.post('/', async (req, res) => {
|
||||
router.post("/", async (req, res) => {
|
||||
try {
|
||||
const novoProcesso = await repository.create(req.body);
|
||||
res.status(201).json(novoProcesso);
|
||||
} catch (error) {
|
||||
console.error("Erro ao criar processo:", error);
|
||||
res.status(400).json({
|
||||
res.status(400).json({
|
||||
error: "Erro ao criar processo",
|
||||
details: error instanceof Error ? error.message : undefined
|
||||
details: error instanceof Error ? error.message : undefined,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Adicionar movimentação
|
||||
router.post('/:id/movimentacoes', async (req, res) => {
|
||||
router.post("/:id/movimentacoes", async (req, res) => {
|
||||
try {
|
||||
const movimentacao = await repository.addMovimentacao(
|
||||
req.params.id,
|
||||
req.body
|
||||
req.body,
|
||||
);
|
||||
res.status(201).json(movimentacao);
|
||||
} catch (error) {
|
||||
console.error("Erro ao adicionar movimentação:", error);
|
||||
res.status(400).json({
|
||||
res.status(400).json({
|
||||
error: "Erro ao adicionar movimentação",
|
||||
details: error instanceof Error ? error.message : undefined
|
||||
details: error instanceof Error ? error.message : undefined,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Atualizar movimentação
|
||||
router.put('/movimentacoes/:id', async (req, res) => {
|
||||
router.put("/movimentacoes/:id", async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const movimentacao = await repository.updateMovimentacao(id, req.body);
|
||||
res.json(movimentacao);
|
||||
} catch (error) {
|
||||
console.error("Erro ao atualizar movimentação:", error);
|
||||
res.status(400).json({
|
||||
res.status(400).json({
|
||||
error: "Erro ao atualizar movimentação",
|
||||
details: error instanceof Error ? error.message : undefined
|
||||
details: error instanceof Error ? error.message : undefined,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Excluir movimentação
|
||||
router.delete('/movimentacoes/:id', async (req, res) => {
|
||||
router.delete("/movimentacoes/:id", async (req, res) => {
|
||||
try {
|
||||
await repository.deleteMovimentacao(req.params.id);
|
||||
res.status(204).send();
|
||||
} catch (error) {
|
||||
console.error("Erro ao excluir movimentação:", error);
|
||||
res.status(400).json({
|
||||
res.status(400).json({
|
||||
error: "Erro ao excluir movimentação",
|
||||
details: error instanceof Error ? error.message : undefined
|
||||
details: error instanceof Error ? error.message : undefined,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Atualizar processo
|
||||
router.put('/:id', async (req, res) => {
|
||||
router.put("/:id", async (req, res) => {
|
||||
try {
|
||||
const processoAtualizado = await repository.update(req.params.id, req.body);
|
||||
res.json(processoAtualizado);
|
||||
} catch (error) {
|
||||
console.error("Erro ao atualizar processo:", error);
|
||||
res.status(400).json({
|
||||
res.status(400).json({
|
||||
error: "Erro ao atualizar processo",
|
||||
details: error instanceof Error ? error.message : undefined
|
||||
details: error instanceof Error ? error.message : undefined,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Excluir processo
|
||||
router.delete('/:id', async (req, res) => {
|
||||
router.delete("/:id", async (req, res) => {
|
||||
try {
|
||||
await repository.delete(req.params.id);
|
||||
res.status(204).send();
|
||||
} catch (error) {
|
||||
console.error("Erro ao excluir processo:", error);
|
||||
res.status(400).json({
|
||||
res.status(400).json({
|
||||
error: "Erro ao excluir processo",
|
||||
details: error instanceof Error ? error.message : undefined
|
||||
details: error instanceof Error ? error.message : undefined,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Rotas para custas administrativas
|
||||
router.get('/:id/custas', async (req, res) => {
|
||||
router.get("/:id/custas", async (req, res) => {
|
||||
try {
|
||||
const custas = await repository.findCustasByProcesso(req.params.id);
|
||||
res.json(custas);
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
res.status(500).json({
|
||||
error: "Erro ao buscar custas",
|
||||
details: error instanceof Error ? error.message : undefined
|
||||
details: error instanceof Error ? error.message : undefined,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/:id/custas', async (req, res) => {
|
||||
router.post("/:id/custas", async (req, res) => {
|
||||
try {
|
||||
const custa = await repository.addCusta(req.params.id, req.body);
|
||||
res.status(201).json(custa);
|
||||
} catch (error) {
|
||||
res.status(400).json({
|
||||
res.status(400).json({
|
||||
error: "Erro ao criar custa",
|
||||
details: error instanceof Error ? error.message : undefined
|
||||
details: error instanceof Error ? error.message : undefined,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.put('/:processoId/custas/:id', async (req, res) => {
|
||||
router.put("/:processoId/custas/:id", async (req, res) => {
|
||||
try {
|
||||
const custa = await repository.updateCusta(req.params.id, req.body);
|
||||
res.json(custa);
|
||||
} catch (error) {
|
||||
res.status(400).json({
|
||||
res.status(400).json({
|
||||
error: "Erro ao atualizar custa",
|
||||
details: error instanceof Error ? error.message : undefined
|
||||
details: error instanceof Error ? error.message : undefined,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.delete('/:processoId/custas/:id', async (req, res) => {
|
||||
router.delete("/:processoId/custas/:id", async (req, res) => {
|
||||
try {
|
||||
await repository.deleteCusta(req.params.id);
|
||||
res.status(204).send();
|
||||
} catch (error) {
|
||||
res.status(400).json({
|
||||
res.status(400).json({
|
||||
error: "Erro ao excluir custa",
|
||||
details: error instanceof Error ? error.message : undefined
|
||||
details: error instanceof Error ? error.message : undefined,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
+157
-127
@@ -1,10 +1,12 @@
|
||||
import express from 'express';
|
||||
import multer from 'multer';
|
||||
import { ProvaService } from '../services/ProvaService';
|
||||
import { prisma } from '../prismaClient';
|
||||
import * as fsPromises from 'fs/promises';
|
||||
import * as fs from 'fs'; // Importando o módulo fs padrão
|
||||
import * as path from 'path';
|
||||
import * as fs from "fs"; // Importando o módulo fs padrão
|
||||
import * as fsPromises from "fs/promises";
|
||||
import * as path from "path";
|
||||
|
||||
import express from "express";
|
||||
import multer from "multer";
|
||||
|
||||
import { prisma } from "../prismaClient";
|
||||
import { ProvaService } from "../services/ProvaService";
|
||||
|
||||
const router = express.Router();
|
||||
const storage = multer.memoryStorage();
|
||||
@@ -12,20 +14,22 @@ const upload = multer({ storage });
|
||||
const provaService = new ProvaService();
|
||||
|
||||
// Upload de arquivo para validação
|
||||
router.post('/upload', upload.array('files'), async (req, res) => {
|
||||
router.post("/upload", upload.array("files"), async (req, res) => {
|
||||
try {
|
||||
console.log('Iniciando upload de arquivos...');
|
||||
|
||||
console.log("Iniciando upload de arquivos...");
|
||||
|
||||
if (!req.files || !Array.isArray(req.files) || req.files.length === 0) {
|
||||
return res.status(400).json({ error: 'Nenhum arquivo recebido' });
|
||||
return res.status(400).json({ error: "Nenhum arquivo recebido" });
|
||||
}
|
||||
|
||||
// Extrair informações sobre o endereço IP e porta do cliente
|
||||
const forwarded = req.headers['x-forwarded-for'] as string;
|
||||
const socketInfo = req.socket.remoteAddress ? {
|
||||
address: req.socket.remoteAddress,
|
||||
port: req.socket.remotePort || 'desconhecida'
|
||||
} : null;
|
||||
const forwarded = req.headers["x-forwarded-for"] as string;
|
||||
const socketInfo = req.socket.remoteAddress
|
||||
? {
|
||||
address: req.socket.remoteAddress,
|
||||
port: req.socket.remotePort || "desconhecida",
|
||||
}
|
||||
: null;
|
||||
|
||||
// Metadata avançada seguindo RFC 3227
|
||||
const metadata = {
|
||||
@@ -34,13 +38,13 @@ router.post('/upload', upload.array('files'), async (req, res) => {
|
||||
viewportWidth: req.body.viewport_width,
|
||||
viewportHeight: req.body.viewport_height,
|
||||
ipAddress: req.ip || req.socket.remoteAddress,
|
||||
clientPort: req.socket.remotePort || 'desconhecida',
|
||||
clientPort: req.socket.remotePort || "desconhecida",
|
||||
connectionInfo: {
|
||||
ip: req.ip || req.socket.remoteAddress,
|
||||
forwardedIp: forwarded,
|
||||
socketAddress: socketInfo?.address,
|
||||
socketPort: socketInfo?.port,
|
||||
remoteFamily: req.socket.remoteFamily
|
||||
remoteFamily: req.socket.remoteFamily,
|
||||
},
|
||||
timestamp: new Date(),
|
||||
modification_times: req.body.modification_times,
|
||||
@@ -55,23 +59,23 @@ router.post('/upload', upload.array('files'), async (req, res) => {
|
||||
location_permission: req.body.location_permission,
|
||||
client_timestamp: req.body.client_timestamp,
|
||||
headers: {
|
||||
'x-forwarded-for': req.headers['x-forwarded-for'],
|
||||
'user-agent': req.headers['user-agent'],
|
||||
'accept-language': req.headers['accept-language'],
|
||||
'referer': req.headers['referer'],
|
||||
'host': req.headers['host'],
|
||||
'connection': req.headers['connection'],
|
||||
"x-forwarded-for": req.headers["x-forwarded-for"],
|
||||
"user-agent": req.headers["user-agent"],
|
||||
"accept-language": req.headers["accept-language"],
|
||||
referer: req.headers["referer"],
|
||||
host: req.headers["host"],
|
||||
connection: req.headers["connection"],
|
||||
},
|
||||
server: {
|
||||
timestamp: new Date().toISOString(),
|
||||
hostname: require('os').hostname(),
|
||||
hostname: require("os").hostname(),
|
||||
process: process.pid,
|
||||
nodeVersion: process.version,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
console.log('Metadata recebida:', metadata);
|
||||
console.log('Informações de conexão do cliente:', metadata.connectionInfo);
|
||||
console.log("Metadata recebida:", metadata);
|
||||
console.log("Informações de conexão do cliente:", metadata.connectionInfo);
|
||||
|
||||
// Adicionar informações de segurança
|
||||
const securityInfo = {
|
||||
@@ -79,10 +83,10 @@ router.post('/upload', upload.array('files'), async (req, res) => {
|
||||
protocol: req.protocol,
|
||||
isSecure: req.secure,
|
||||
originalUrl: req.originalUrl,
|
||||
contentType: req.headers['content-type'],
|
||||
contentType: req.headers["content-type"],
|
||||
};
|
||||
|
||||
console.log('Informações de segurança:', securityInfo);
|
||||
console.log("Informações de segurança:", securityInfo);
|
||||
|
||||
// Processar cada arquivo
|
||||
const results = [];
|
||||
@@ -90,13 +94,15 @@ router.post('/upload', upload.array('files'), async (req, res) => {
|
||||
try {
|
||||
// Adicionar metadados específicos do arquivo atual
|
||||
const fileIndex = (req.files as Express.Multer.File[]).indexOf(file);
|
||||
|
||||
|
||||
// Extrair e formatar a data de modificação do arquivo
|
||||
let fileModifiedDate = null;
|
||||
const modificationTime = req.body.modification_times ?
|
||||
(Array.isArray(req.body.modification_times) ? req.body.modification_times[fileIndex] : req.body.modification_times) :
|
||||
null;
|
||||
|
||||
const modificationTime = req.body.modification_times
|
||||
? Array.isArray(req.body.modification_times)
|
||||
? req.body.modification_times[fileIndex]
|
||||
: req.body.modification_times
|
||||
: null;
|
||||
|
||||
if (modificationTime) {
|
||||
try {
|
||||
// Converter timestamp para objeto Date
|
||||
@@ -105,42 +111,54 @@ router.post('/upload', upload.array('files'), async (req, res) => {
|
||||
fileModifiedDate = new Date(timestamp);
|
||||
}
|
||||
} catch (dateError) {
|
||||
console.warn(`Erro ao processar data de modificação para ${file.originalname}:`, dateError);
|
||||
console.warn(
|
||||
`Erro ao processar data de modificação para ${file.originalname}:`,
|
||||
dateError,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const fileMetadata = {
|
||||
...metadata,
|
||||
file_specific: {
|
||||
name: req.body.file_names ?
|
||||
(Array.isArray(req.body.file_names) ? req.body.file_names[fileIndex] : req.body.file_names) :
|
||||
file.originalname,
|
||||
size: req.body.file_sizes ?
|
||||
(Array.isArray(req.body.file_sizes) ? req.body.file_sizes[fileIndex] : req.body.file_sizes) :
|
||||
file.size,
|
||||
type: req.body.file_types ?
|
||||
(Array.isArray(req.body.file_types) ? req.body.file_types[fileIndex] : req.body.file_types) :
|
||||
file.mimetype,
|
||||
name: req.body.file_names
|
||||
? Array.isArray(req.body.file_names)
|
||||
? req.body.file_names[fileIndex]
|
||||
: req.body.file_names
|
||||
: file.originalname,
|
||||
size: req.body.file_sizes
|
||||
? Array.isArray(req.body.file_sizes)
|
||||
? req.body.file_sizes[fileIndex]
|
||||
: req.body.file_sizes
|
||||
: file.size,
|
||||
type: req.body.file_types
|
||||
? Array.isArray(req.body.file_types)
|
||||
? req.body.file_types[fileIndex]
|
||||
: req.body.file_types
|
||||
: file.mimetype,
|
||||
modified: fileModifiedDate ? fileModifiedDate.toISOString() : null,
|
||||
modifiedTimestamp: modificationTime
|
||||
modifiedTimestamp: modificationTime,
|
||||
},
|
||||
security: securityInfo
|
||||
security: securityInfo,
|
||||
};
|
||||
|
||||
// Adicionar log para debug de data de modificação
|
||||
console.log(`Informações de modificação do arquivo ${file.originalname}:`, {
|
||||
rawTimestamp: modificationTime,
|
||||
processedDate: fileModifiedDate,
|
||||
isoString: fileModifiedDate ? fileModifiedDate.toISOString() : null
|
||||
});
|
||||
console.log(
|
||||
`Informações de modificação do arquivo ${file.originalname}:`,
|
||||
{
|
||||
rawTimestamp: modificationTime,
|
||||
processedDate: fileModifiedDate,
|
||||
isoString: fileModifiedDate ? fileModifiedDate.toISOString() : null,
|
||||
},
|
||||
);
|
||||
|
||||
const result = await provaService.processFile(file, fileMetadata);
|
||||
results.push(result);
|
||||
} catch (error) {
|
||||
console.error(`Erro ao processar arquivo ${file.originalname}:`, error);
|
||||
return res.status(500).json({
|
||||
error: `Erro ao processar arquivo ${file.originalname}`,
|
||||
details: error.message
|
||||
return res.status(500).json({
|
||||
error: `Erro ao processar arquivo ${file.originalname}`,
|
||||
details: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -148,171 +166,183 @@ router.post('/upload', upload.array('files'), async (req, res) => {
|
||||
res.json({
|
||||
success: true,
|
||||
results,
|
||||
id: results[0].id
|
||||
id: results[0].id,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro no upload:', error);
|
||||
res.status(500).json({ error: 'Erro ao processar arquivos' });
|
||||
console.error("Erro no upload:", error);
|
||||
res.status(500).json({ error: "Erro ao processar arquivos" });
|
||||
}
|
||||
});
|
||||
|
||||
// Status da prova
|
||||
router.get('/:id/status', async (req, res) => {
|
||||
router.get("/:id/status", async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const prova = await prisma.prova.findUnique({
|
||||
where: { id }
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!prova) {
|
||||
return res.status(404).json({ error: 'Prova não encontrada' });
|
||||
return res.status(404).json({ error: "Prova não encontrada" });
|
||||
}
|
||||
|
||||
res.json(prova);
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar status:', error);
|
||||
res.status(500).json({ error: 'Erro ao buscar status da prova' });
|
||||
console.error("Erro ao buscar status:", error);
|
||||
res.status(500).json({ error: "Erro ao buscar status da prova" });
|
||||
}
|
||||
});
|
||||
|
||||
// Modificar a rota de verificação
|
||||
router.get('/verificar/:hash', async (req, res) => {
|
||||
router.get("/verificar/:hash", async (req, res) => {
|
||||
try {
|
||||
const { hash } = req.params;
|
||||
|
||||
|
||||
// Capturar IP real usando a mesma lógica que funciona no processamento
|
||||
const realClientIP = req.headers['x-forwarded-for']?.toString().split(',')[0].trim() ||
|
||||
req.ip ||
|
||||
req.socket?.remoteAddress ||
|
||||
'127.0.0.1'; // Nunca usar 0.0.0.0
|
||||
|
||||
const clientPort = req.socket?.remotePort?.toString() || '0';
|
||||
|
||||
const realClientIP =
|
||||
req.headers["x-forwarded-for"]?.toString().split(",")[0].trim() ||
|
||||
req.ip ||
|
||||
req.socket?.remoteAddress ||
|
||||
"127.0.0.1"; // Nunca usar 0.0.0.0
|
||||
|
||||
const clientPort = req.socket?.remotePort?.toString() || "0";
|
||||
|
||||
// Registrar para debug
|
||||
console.log('Verificação de prova solicitada:', {
|
||||
console.log("Verificação de prova solicitada:", {
|
||||
hash,
|
||||
realClientIP,
|
||||
clientPort,
|
||||
headers: {
|
||||
'x-forwarded-for': req.headers['x-forwarded-for'],
|
||||
'user-agent': req.headers['user-agent']
|
||||
}
|
||||
"x-forwarded-for": req.headers["x-forwarded-for"],
|
||||
"user-agent": req.headers["user-agent"],
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const provaService = new ProvaService();
|
||||
const result = await provaService.verifyFile(hash);
|
||||
|
||||
|
||||
// Adicionar informações de verificação ao resultado
|
||||
return res.status(200).json({
|
||||
...result,
|
||||
verificacao: {
|
||||
timestamp: new Date().toISOString(),
|
||||
ip: realClientIP,
|
||||
porta: clientPort
|
||||
}
|
||||
porta: clientPort,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao verificar arquivo:', error);
|
||||
return res.status(500).json({
|
||||
verified: false,
|
||||
message: `Erro ao verificar: ${error.message}`
|
||||
console.error("Erro ao verificar arquivo:", error);
|
||||
return res.status(500).json({
|
||||
verified: false,
|
||||
message: `Erro ao verificar: ${error.message}`,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Modificar a rota de verificação pública
|
||||
|
||||
router.get('/public/verificar/:hash', async (req, res) => {
|
||||
router.get("/public/verificar/:hash", async (req, res) => {
|
||||
try {
|
||||
const { hash } = req.params;
|
||||
|
||||
|
||||
// Capturar IP real usando headers
|
||||
const realClientIP = req.headers['x-forwarded-for']?.toString().split(',')[0].trim() ||
|
||||
req.ip ||
|
||||
req.socket?.remoteAddress ||
|
||||
'127.0.0.1';
|
||||
|
||||
const clientPort = req.socket?.remotePort?.toString() || '0';
|
||||
|
||||
console.log('Verificação pública solicitada:', {
|
||||
const realClientIP =
|
||||
req.headers["x-forwarded-for"]?.toString().split(",")[0].trim() ||
|
||||
req.ip ||
|
||||
req.socket?.remoteAddress ||
|
||||
"127.0.0.1";
|
||||
|
||||
const clientPort = req.socket?.remotePort?.toString() || "0";
|
||||
|
||||
console.log("Verificação pública solicitada:", {
|
||||
hash,
|
||||
realClientIP,
|
||||
clientPort,
|
||||
headers: {
|
||||
'x-forwarded-for': req.headers['x-forwarded-for'],
|
||||
'user-agent': req.headers['user-agent']
|
||||
}
|
||||
"x-forwarded-for": req.headers["x-forwarded-for"],
|
||||
"user-agent": req.headers["user-agent"],
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const provaService = new ProvaService();
|
||||
const result = await provaService.verifyFile(hash);
|
||||
|
||||
|
||||
// Adicionar informações de verificação ao resultado
|
||||
return res.status(200).json({
|
||||
...result,
|
||||
verificacao: {
|
||||
timestamp: new Date().toISOString(),
|
||||
ip: realClientIP,
|
||||
porta: clientPort
|
||||
}
|
||||
porta: clientPort,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao verificar arquivo:', error);
|
||||
return res.status(500).json({
|
||||
verified: false,
|
||||
message: `Erro ao verificar: ${error.message}`
|
||||
console.error("Erro ao verificar arquivo:", error);
|
||||
return res.status(500).json({
|
||||
verified: false,
|
||||
message: `Erro ao verificar: ${error.message}`,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Modificar/adicionar esta rota para servir o relatório a partir do hash
|
||||
router.get('/relatorio/:hash', async (req, res) => {
|
||||
router.get("/relatorio/:hash", async (req, res) => {
|
||||
try {
|
||||
const { hash } = req.params;
|
||||
|
||||
|
||||
// Buscar a prova pelo hash
|
||||
const prova = await prisma.prova.findFirst({
|
||||
where: { checksumSha256: hash }
|
||||
where: { checksumSha256: hash },
|
||||
});
|
||||
|
||||
if (!prova || prova.status !== 'CONCLUIDO') {
|
||||
return res.status(404).json({ error: 'Relatório não encontrado' });
|
||||
|
||||
if (!prova || prova.status !== "CONCLUIDO") {
|
||||
return res.status(404).json({ error: "Relatório não encontrado" });
|
||||
}
|
||||
|
||||
|
||||
// Construir o caminho para o arquivo do relatório
|
||||
const reportPath = path.join(process.cwd(), 'storage', 'provas', `${hash}_report.pdf`);
|
||||
|
||||
const reportPath = path.join(
|
||||
process.cwd(),
|
||||
"storage",
|
||||
"provas",
|
||||
`${hash}_report.pdf`,
|
||||
);
|
||||
|
||||
// Verificar se o arquivo existe
|
||||
try {
|
||||
await fsPromises.access(reportPath); // Usando fsPromises para operação assíncrona
|
||||
} catch (error) {
|
||||
console.error('Erro ao acessar relatório:', error);
|
||||
return res.status(404).json({ error: 'Arquivo do relatório não encontrado' });
|
||||
console.error("Erro ao acessar relatório:", error);
|
||||
return res
|
||||
.status(404)
|
||||
.json({ error: "Arquivo do relatório não encontrado" });
|
||||
}
|
||||
|
||||
|
||||
// Enviar o arquivo como resposta
|
||||
res.setHeader('Content-Type', 'application/pdf');
|
||||
res.setHeader('Content-Disposition', `inline; filename="${prova.fileName.replace(/\.[^/.]+$/, '')}_relatorio.pdf"`);
|
||||
|
||||
res.setHeader("Content-Type", "application/pdf");
|
||||
res.setHeader(
|
||||
"Content-Disposition",
|
||||
`inline; filename="${prova.fileName.replace(/\.[^/.]+$/, "")}_relatorio.pdf"`,
|
||||
);
|
||||
|
||||
// Criar stream de leitura e enviar para o cliente
|
||||
const fileStream = fs.createReadStream(reportPath); // Usando fs padrão para createReadStream
|
||||
fileStream.pipe(res);
|
||||
} catch (error) {
|
||||
console.error('Erro ao servir relatório:', error);
|
||||
res.status(500).json({ error: 'Erro ao gerar relatório' });
|
||||
console.error("Erro ao servir relatório:", error);
|
||||
res.status(500).json({ error: "Erro ao gerar relatório" });
|
||||
}
|
||||
});
|
||||
|
||||
// Listar provas (admin)
|
||||
router.get('/admin', async (req, res) => {
|
||||
router.get("/admin", async (req, res) => {
|
||||
try {
|
||||
const provas = await prisma.prova.findMany({
|
||||
orderBy: { dataCriacao: 'desc' }
|
||||
orderBy: { dataCriacao: "desc" },
|
||||
});
|
||||
res.json(provas);
|
||||
} catch (error) {
|
||||
console.error('Erro ao listar provas:', error);
|
||||
res.status(500).json({ error: 'Erro ao listar provas' });
|
||||
console.error("Erro ao listar provas:", error);
|
||||
res.status(500).json({ error: "Erro ao listar provas" });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Router } from 'express';
|
||||
import { prisma } from '../../config/database';
|
||||
import { authMiddleware } from '../../middleware/auth';
|
||||
import { Router } from "express";
|
||||
|
||||
import { prisma } from "../../config/database";
|
||||
import { authMiddleware } from "../../middleware/auth";
|
||||
|
||||
const router = Router();
|
||||
|
||||
// Listar tarefas
|
||||
router.get('/', authMiddleware, async (req, res) => {
|
||||
router.get("/", authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const tarefas = await prisma.tarefa.findMany({
|
||||
include: {
|
||||
@@ -13,49 +14,49 @@ router.get('/', authMiddleware, async (req, res) => {
|
||||
subtarefas: true,
|
||||
comentarios: {
|
||||
include: {
|
||||
criadoPor: true
|
||||
}
|
||||
}
|
||||
criadoPor: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
dataVencimento: 'asc'
|
||||
}
|
||||
dataVencimento: "asc",
|
||||
},
|
||||
});
|
||||
res.json(tarefas);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Erro ao buscar tarefas' });
|
||||
res.status(500).json({ error: "Erro ao buscar tarefas" });
|
||||
}
|
||||
});
|
||||
|
||||
// Criar tarefa
|
||||
router.post('/', authMiddleware, async (req, res) => {
|
||||
router.post("/", authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const tarefa = await prisma.tarefa.create({
|
||||
data: {
|
||||
...req.body,
|
||||
criadoPorId: req.user.id,
|
||||
status: req.body.status || 'PENDENTE',
|
||||
status: req.body.status || "PENDENTE",
|
||||
progresso: req.body.progresso || 0,
|
||||
etiquetas: req.body.etiquetas || [],
|
||||
subtarefas: {
|
||||
create: req.body.subtarefas || []
|
||||
create: req.body.subtarefas || [],
|
||||
},
|
||||
comentarios: {
|
||||
create: req.body.comentarios || []
|
||||
}
|
||||
create: req.body.comentarios || [],
|
||||
},
|
||||
},
|
||||
include: {
|
||||
subtarefas: true,
|
||||
comentarios: true,
|
||||
responsavel: true,
|
||||
criadoPor: true
|
||||
}
|
||||
criadoPor: true,
|
||||
},
|
||||
});
|
||||
|
||||
res.json(tarefa);
|
||||
} catch (error) {
|
||||
console.error('Erro ao criar tarefa:', error);
|
||||
res.status(500).json({ error: 'Erro ao criar tarefa' });
|
||||
console.error("Erro ao criar tarefa:", error);
|
||||
res.status(500).json({ error: "Erro ao criar tarefa" });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -63,39 +64,45 @@ router.post('/', authMiddleware, async (req, res) => {
|
||||
// router.post('/:id/anexos', authMiddleware, uploadHandler.array('files'), async (req, res) => { ... });
|
||||
|
||||
// Atualizar status da tarefa
|
||||
router.patch('/:id/status', authMiddleware, async (req, res) => {
|
||||
router.patch("/:id/status", authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { status, progresso, justificativa } = req.body;
|
||||
|
||||
const tarefa = await prisma.tarefa.update({
|
||||
where: { id },
|
||||
data: {
|
||||
data: {
|
||||
status,
|
||||
progresso: progresso || (status === 'CONCLUIDA' ? 100 : status === 'CANCELADA' ? 0 : undefined),
|
||||
progresso:
|
||||
progresso ||
|
||||
(status === "CONCLUIDA"
|
||||
? 100
|
||||
: status === "CANCELADA"
|
||||
? 0
|
||||
: undefined),
|
||||
justificativa,
|
||||
atualizadoEm: new Date()
|
||||
atualizadoEm: new Date(),
|
||||
},
|
||||
include: {
|
||||
responsavel: true,
|
||||
subtarefas: true,
|
||||
comentarios: {
|
||||
include: {
|
||||
criadoPor: true
|
||||
}
|
||||
}
|
||||
}
|
||||
criadoPor: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
res.json(tarefa);
|
||||
} catch (error) {
|
||||
console.error('Erro ao atualizar status:', error);
|
||||
res.status(500).json({ error: 'Erro ao atualizar status' });
|
||||
console.error("Erro ao atualizar status:", error);
|
||||
res.status(500).json({ error: "Erro ao atualizar status" });
|
||||
}
|
||||
});
|
||||
|
||||
// Adicionar rota de edição
|
||||
router.put('/:id', authMiddleware, async (req, res) => {
|
||||
router.put("/:id", authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const {
|
||||
@@ -113,34 +120,43 @@ router.put('/:id', authMiddleware, async (req, res) => {
|
||||
const updateData = {
|
||||
...data,
|
||||
atualizadoEm: new Date(),
|
||||
responsavel: responsavelId ? {
|
||||
connect: { id: responsavelId }
|
||||
} : undefined,
|
||||
processo: processoId ? {
|
||||
connect: { id: processoId }
|
||||
} : {
|
||||
disconnect: true
|
||||
},
|
||||
cliente: clienteId ? {
|
||||
connect: { id: clienteId }
|
||||
} : {
|
||||
disconnect: true
|
||||
},
|
||||
responsavel: responsavelId
|
||||
? {
|
||||
connect: { id: responsavelId },
|
||||
}
|
||||
: undefined,
|
||||
processo: processoId
|
||||
? {
|
||||
connect: { id: processoId },
|
||||
}
|
||||
: {
|
||||
disconnect: true,
|
||||
},
|
||||
cliente: clienteId
|
||||
? {
|
||||
connect: { id: clienteId },
|
||||
}
|
||||
: {
|
||||
disconnect: true,
|
||||
},
|
||||
subtarefas: {
|
||||
deleteMany: {},
|
||||
create: subtarefas?.map((st: any) => ({
|
||||
titulo: st.titulo,
|
||||
concluida: st.concluida || false
|
||||
})) || []
|
||||
create:
|
||||
subtarefas?.map((st: any) => ({
|
||||
titulo: st.titulo,
|
||||
concluida: st.concluida || false,
|
||||
})) || [],
|
||||
},
|
||||
comentarios: comentarios?.length ? {
|
||||
create: comentarios.map((c: any) => ({
|
||||
texto: c.texto,
|
||||
criadoPor: {
|
||||
connect: { id: req.user.id }
|
||||
comentarios: comentarios?.length
|
||||
? {
|
||||
create: comentarios.map((c: any) => ({
|
||||
texto: c.texto,
|
||||
criadoPor: {
|
||||
connect: { id: req.user.id },
|
||||
},
|
||||
})),
|
||||
}
|
||||
}))
|
||||
} : undefined
|
||||
: undefined,
|
||||
};
|
||||
|
||||
const tarefa = await prisma.tarefa.update({
|
||||
@@ -150,41 +166,43 @@ router.put('/:id', authMiddleware, async (req, res) => {
|
||||
subtarefas: true,
|
||||
comentarios: {
|
||||
include: {
|
||||
criadoPor: true
|
||||
}
|
||||
criadoPor: true,
|
||||
},
|
||||
},
|
||||
responsavel: true,
|
||||
criadoPor: true,
|
||||
processo: true,
|
||||
cliente: true
|
||||
}
|
||||
cliente: true,
|
||||
},
|
||||
});
|
||||
|
||||
res.json(tarefa);
|
||||
} catch (error) {
|
||||
console.error('Erro ao atualizar tarefa:', error);
|
||||
res.status(500).json({ error: 'Erro ao atualizar tarefa' });
|
||||
console.error("Erro ao atualizar tarefa:", error);
|
||||
res.status(500).json({ error: "Erro ao atualizar tarefa" });
|
||||
}
|
||||
});
|
||||
|
||||
// Adicionar rota de exclusão
|
||||
router.delete('/:id', authMiddleware, async (req, res) => {
|
||||
router.delete("/:id", authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
|
||||
// Verificar se usuário tem permissão
|
||||
if (!['ADVOGADO_ADMIN', 'ADVOGADO'].includes(req.user.perfil)) {
|
||||
return res.status(403).json({ error: 'Sem permissão para excluir tarefas' });
|
||||
if (!["ADVOGADO_ADMIN", "ADVOGADO"].includes(req.user.perfil)) {
|
||||
return res
|
||||
.status(403)
|
||||
.json({ error: "Sem permissão para excluir tarefas" });
|
||||
}
|
||||
|
||||
await prisma.tarefa.delete({
|
||||
where: { id }
|
||||
where: { id },
|
||||
});
|
||||
|
||||
res.status(204).send();
|
||||
} catch (error) {
|
||||
console.error('Erro ao excluir tarefa:', error);
|
||||
res.status(500).json({ error: 'Erro ao excluir tarefa' });
|
||||
console.error("Erro ao excluir tarefa:", error);
|
||||
res.status(500).json({ error: "Erro ao excluir tarefa" });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import OpenTimestamps from 'opentimestamps-javascript';
|
||||
import { createHash } from 'crypto';
|
||||
import * as fs from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
import { createHash } from "crypto";
|
||||
import * as fs from "fs/promises";
|
||||
import * as path from "path";
|
||||
|
||||
import OpenTimestamps from "opentimestamps-javascript";
|
||||
|
||||
export class BlockchainService {
|
||||
private readonly storageDir: string;
|
||||
|
||||
constructor() {
|
||||
this.storageDir = path.join(process.cwd(), 'storage', 'timestamps');
|
||||
this.storageDir = path.join(process.cwd(), "storage", "timestamps");
|
||||
this.ensureStorageDir();
|
||||
}
|
||||
|
||||
@@ -15,40 +16,42 @@ export class BlockchainService {
|
||||
try {
|
||||
await fs.mkdir(this.storageDir, { recursive: true });
|
||||
} catch (error) {
|
||||
console.error('Erro ao criar diretório de timestamps:', error);
|
||||
console.error("Erro ao criar diretório de timestamps:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async createTimestamp(buffer: Buffer) {
|
||||
try {
|
||||
console.log('Iniciando criação de timestamp na blockchain...');
|
||||
|
||||
console.log("Iniciando criação de timestamp na blockchain...");
|
||||
|
||||
// Criar hash do arquivo e usar diretamente
|
||||
const fileHash = createHash('sha256').update(buffer).digest('hex');
|
||||
console.log('Hash gerado:', fileHash);
|
||||
const fileHash = createHash("sha256").update(buffer).digest("hex");
|
||||
console.log("Hash gerado:", fileHash);
|
||||
|
||||
// Criar timestamp usando o hash em hex
|
||||
const timestamp = await OpenTimestamps.stamp(fileHash);
|
||||
if (!timestamp) {
|
||||
throw new Error('Falha ao gerar timestamp');
|
||||
throw new Error("Falha ao gerar timestamp");
|
||||
}
|
||||
|
||||
console.log('Timestamp criado com sucesso');
|
||||
console.log("Timestamp criado com sucesso");
|
||||
|
||||
// Salvar arquivo .ots
|
||||
const otsPath = path.join(this.storageDir, `${fileHash}.ots`);
|
||||
await fs.writeFile(otsPath, Buffer.from(timestamp));
|
||||
console.log('Arquivo .ots salvo em:', otsPath);
|
||||
console.log("Arquivo .ots salvo em:", otsPath);
|
||||
|
||||
return {
|
||||
hash: fileHash,
|
||||
timestamp,
|
||||
otsPath
|
||||
otsPath,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Erro detalhado ao criar timestamp:', error);
|
||||
console.error('Stack:', error.stack);
|
||||
throw new Error(`Falha ao criar timestamp na blockchain: ${error.message}`);
|
||||
console.error("Erro detalhado ao criar timestamp:", error);
|
||||
console.error("Stack:", error.stack);
|
||||
throw new Error(
|
||||
`Falha ao criar timestamp na blockchain: ${error.message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,26 +59,26 @@ export class BlockchainService {
|
||||
try {
|
||||
const result = await OpenTimestamps.verify(
|
||||
Buffer.from(timestamp),
|
||||
Buffer.from(hash, 'hex')
|
||||
Buffer.from(hash, "hex"),
|
||||
);
|
||||
|
||||
|
||||
if (!result) {
|
||||
return {
|
||||
verified: false,
|
||||
message: 'Timestamp ainda não confirmado na blockchain'
|
||||
return {
|
||||
verified: false,
|
||||
message: "Timestamp ainda não confirmado na blockchain",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
verified: true,
|
||||
timestamp: new Date(result * 1000),
|
||||
message: 'Timestamp verificado na blockchain do Bitcoin'
|
||||
message: "Timestamp verificado na blockchain do Bitcoin",
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Erro ao verificar timestamp:', error);
|
||||
return {
|
||||
verified: false,
|
||||
message: 'Erro ao verificar timestamp'
|
||||
console.error("Erro ao verificar timestamp:", error);
|
||||
return {
|
||||
verified: false,
|
||||
message: "Erro ao verificar timestamp",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PrismaClient, Decimal } from '@prisma/client';
|
||||
import { v4 as uuidv4 } from 'uuid'; // Importar uuid para gerar IDs
|
||||
import { PrismaClient, Decimal } from "@prisma/client";
|
||||
import { v4 as uuidv4 } from "uuid"; // Importar uuid para gerar IDs
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
@@ -19,7 +19,7 @@ export class CalculoJudicialService {
|
||||
try {
|
||||
// Validar dados de entrada
|
||||
if (!data.titulo || !data.dataBase || data.valorBase === undefined) {
|
||||
throw new Error('Dados incompletos para criar cálculo');
|
||||
throw new Error("Dados incompletos para criar cálculo");
|
||||
}
|
||||
|
||||
// Criar cálculo no banco de dados com ID UUID explícito
|
||||
@@ -33,24 +33,30 @@ export class CalculoJudicialService {
|
||||
valorBase: new Decimal(data.valorBase),
|
||||
valorCorrigido: new Decimal(data.valorBase),
|
||||
valorFinal: new Decimal(data.valorBase),
|
||||
...(data.processoId && { processo: { connect: { id: data.processoId } } }),
|
||||
...(data.clienteId && { cliente: { connect: { id: data.clienteId } } }),
|
||||
...(data.usuarioId && { usuario: { connect: { id: data.usuarioId } } })
|
||||
}
|
||||
...(data.processoId && {
|
||||
processo: { connect: { id: data.processoId } },
|
||||
}),
|
||||
...(data.clienteId && {
|
||||
cliente: { connect: { id: data.clienteId } },
|
||||
}),
|
||||
...(data.usuarioId && {
|
||||
usuario: { connect: { id: data.usuarioId } },
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
// Registrar o evento de criação no histórico
|
||||
await this.registrarHistorico({
|
||||
calculoId: calculo.id,
|
||||
usuarioId: data.usuarioId,
|
||||
acao: 'CRIACAO',
|
||||
descricao: 'Cálculo criado',
|
||||
valorNovo: data.valorBase
|
||||
acao: "CRIACAO",
|
||||
descricao: "Cálculo criado",
|
||||
valorNovo: data.valorBase,
|
||||
});
|
||||
|
||||
return calculo;
|
||||
} catch (error) {
|
||||
console.error('Erro ao criar cálculo judicial:', error);
|
||||
console.error("Erro ao criar cálculo judicial:", error);
|
||||
throw new Error(`Falha ao criar cálculo: ${error.message}`);
|
||||
}
|
||||
}
|
||||
@@ -74,22 +80,22 @@ export class CalculoJudicialService {
|
||||
descontos: true,
|
||||
historico: {
|
||||
include: {
|
||||
usuario: true
|
||||
usuario: true,
|
||||
},
|
||||
orderBy: {
|
||||
criadoEm: 'desc'
|
||||
}
|
||||
}
|
||||
}
|
||||
criadoEm: "desc",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!calculo) {
|
||||
throw new Error('Cálculo não encontrado');
|
||||
throw new Error("Cálculo não encontrado");
|
||||
}
|
||||
|
||||
return calculo;
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar cálculo:', error);
|
||||
console.error("Erro ao buscar cálculo:", error);
|
||||
throw new Error(`Falha ao buscar cálculo: ${error.message}`);
|
||||
}
|
||||
}
|
||||
@@ -107,28 +113,33 @@ export class CalculoJudicialService {
|
||||
}) {
|
||||
try {
|
||||
// Validar dados
|
||||
if (!data.calculoId || !data.indice || !data.dataInicial || data.valorOriginal === undefined) {
|
||||
throw new Error('Dados incompletos para adicionar correção monetária');
|
||||
if (
|
||||
!data.calculoId ||
|
||||
!data.indice ||
|
||||
!data.dataInicial ||
|
||||
data.valorOriginal === undefined
|
||||
) {
|
||||
throw new Error("Dados incompletos para adicionar correção monetária");
|
||||
}
|
||||
|
||||
// Verificar se o cálculo existe e não está finalizado
|
||||
const calculo = await prisma.calculoJudicial.findUnique({
|
||||
where: { id: data.calculoId }
|
||||
where: { id: data.calculoId },
|
||||
});
|
||||
|
||||
if (!calculo) {
|
||||
throw new Error('Cálculo não encontrado');
|
||||
throw new Error("Cálculo não encontrado");
|
||||
}
|
||||
|
||||
if (calculo.status === 'FINALIZADO') {
|
||||
throw new Error('Não é possível modificar um cálculo finalizado');
|
||||
if (calculo.status === "FINALIZADO") {
|
||||
throw new Error("Não é possível modificar um cálculo finalizado");
|
||||
}
|
||||
|
||||
// Calcular o fator de correção
|
||||
const fatorCorrecao = await this.calcularFatorCorrecao({
|
||||
indice: data.indice,
|
||||
dataInicial: data.dataInicial,
|
||||
dataFinal: data.dataFinal || new Date()
|
||||
dataFinal: data.dataFinal || new Date(),
|
||||
});
|
||||
|
||||
// Calcular valor corrigido
|
||||
@@ -144,8 +155,8 @@ export class CalculoJudicialService {
|
||||
dataFinal: data.dataFinal || new Date(),
|
||||
valorOriginal: new Decimal(data.valorOriginal),
|
||||
valorCorrigido,
|
||||
fatorCorrecao
|
||||
}
|
||||
fatorCorrecao,
|
||||
},
|
||||
});
|
||||
|
||||
// Atualizar o valor corrigido do cálculo
|
||||
@@ -155,7 +166,7 @@ export class CalculoJudicialService {
|
||||
await this.registrarHistorico({
|
||||
calculoId: data.calculoId,
|
||||
usuarioId: data.usuarioId,
|
||||
acao: 'ATUALIZACAO',
|
||||
acao: "ATUALIZACAO",
|
||||
descricao: `Correção monetária adicionada: ${data.indice}`,
|
||||
valorNovo: valorCorrigido.toNumber(),
|
||||
metadados: {
|
||||
@@ -163,14 +174,16 @@ export class CalculoJudicialService {
|
||||
indice: data.indice,
|
||||
dataInicial: data.dataInicial,
|
||||
dataFinal: data.dataFinal || new Date(),
|
||||
fatorCorrecao: fatorCorrecao.toNumber()
|
||||
}
|
||||
fatorCorrecao: fatorCorrecao.toNumber(),
|
||||
},
|
||||
});
|
||||
|
||||
return correcao;
|
||||
} catch (error) {
|
||||
console.error('Erro ao adicionar correção monetária:', error);
|
||||
throw new Error(`Falha ao adicionar correção monetária: ${error.message}`);
|
||||
console.error("Erro ao adicionar correção monetária:", error);
|
||||
throw new Error(
|
||||
`Falha ao adicionar correção monetária: ${error.message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,21 +203,26 @@ export class CalculoJudicialService {
|
||||
}) {
|
||||
try {
|
||||
// Validar dados
|
||||
if (!data.calculoId || !data.tipo || data.taxa === undefined || !data.dataInicial) {
|
||||
throw new Error('Dados incompletos para adicionar juros');
|
||||
if (
|
||||
!data.calculoId ||
|
||||
!data.tipo ||
|
||||
data.taxa === undefined ||
|
||||
!data.dataInicial
|
||||
) {
|
||||
throw new Error("Dados incompletos para adicionar juros");
|
||||
}
|
||||
|
||||
// Verificar se o cálculo existe e não está finalizado
|
||||
const calculo = await prisma.calculoJudicial.findUnique({
|
||||
where: { id: data.calculoId }
|
||||
where: { id: data.calculoId },
|
||||
});
|
||||
|
||||
if (!calculo) {
|
||||
throw new Error('Cálculo não encontrado');
|
||||
throw new Error("Cálculo não encontrado");
|
||||
}
|
||||
|
||||
if (calculo.status === 'FINALIZADO') {
|
||||
throw new Error('Não é possível modificar um cálculo finalizado');
|
||||
if (calculo.status === "FINALIZADO") {
|
||||
throw new Error("Não é possível modificar um cálculo finalizado");
|
||||
}
|
||||
|
||||
// Calcular valor dos juros
|
||||
@@ -213,7 +231,7 @@ export class CalculoJudicialService {
|
||||
data.taxa,
|
||||
data.tipo,
|
||||
data.dataInicial,
|
||||
data.dataFinal || new Date()
|
||||
data.dataFinal || new Date(),
|
||||
);
|
||||
|
||||
// Criar registro de juros
|
||||
@@ -228,8 +246,8 @@ export class CalculoJudicialService {
|
||||
dataInicial: data.dataInicial,
|
||||
dataFinal: data.dataFinal || new Date(),
|
||||
valorBase: new Decimal(data.valorBase),
|
||||
valorJuros: new Decimal(valorJuros)
|
||||
}
|
||||
valorJuros: new Decimal(valorJuros),
|
||||
},
|
||||
});
|
||||
|
||||
// Atualizar o valor final do cálculo
|
||||
@@ -239,7 +257,7 @@ export class CalculoJudicialService {
|
||||
await this.registrarHistorico({
|
||||
calculoId: data.calculoId,
|
||||
usuarioId: data.usuarioId,
|
||||
acao: 'ATUALIZACAO',
|
||||
acao: "ATUALIZACAO",
|
||||
descricao: `Juros adicionados: ${data.tipo} ${data.taxa * 100}% ${data.periodicidade}`,
|
||||
valorNovo: valorJuros,
|
||||
metadados: {
|
||||
@@ -247,13 +265,13 @@ export class CalculoJudicialService {
|
||||
tipo: data.tipo,
|
||||
taxa: data.taxa,
|
||||
dataInicial: data.dataInicial,
|
||||
dataFinal: data.dataFinal || new Date()
|
||||
}
|
||||
dataFinal: data.dataFinal || new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
return juros;
|
||||
} catch (error) {
|
||||
console.error('Erro ao adicionar juros:', error);
|
||||
console.error("Erro ao adicionar juros:", error);
|
||||
throw new Error(`Falha ao adicionar juros: ${error.message}`);
|
||||
}
|
||||
}
|
||||
@@ -272,20 +290,20 @@ export class CalculoJudicialService {
|
||||
try {
|
||||
// Validar dados
|
||||
if (!data.calculoId || !data.tipo || data.percentual === undefined) {
|
||||
throw new Error('Dados incompletos para adicionar multa');
|
||||
throw new Error("Dados incompletos para adicionar multa");
|
||||
}
|
||||
|
||||
// Verificar se o cálculo existe e não está finalizado
|
||||
const calculo = await prisma.calculoJudicial.findUnique({
|
||||
where: { id: data.calculoId }
|
||||
where: { id: data.calculoId },
|
||||
});
|
||||
|
||||
if (!calculo) {
|
||||
throw new Error('Cálculo não encontrado');
|
||||
throw new Error("Cálculo não encontrado");
|
||||
}
|
||||
|
||||
if (calculo.status === 'FINALIZADO') {
|
||||
throw new Error('Não é possível modificar um cálculo finalizado');
|
||||
if (calculo.status === "FINALIZADO") {
|
||||
throw new Error("Não é possível modificar um cálculo finalizado");
|
||||
}
|
||||
|
||||
// Calcular valor da multa
|
||||
@@ -300,8 +318,8 @@ export class CalculoJudicialService {
|
||||
percentual: new Decimal(data.percentual),
|
||||
valorBase: new Decimal(data.valorBase),
|
||||
valorMulta,
|
||||
fundamento: data.fundamento
|
||||
}
|
||||
fundamento: data.fundamento,
|
||||
},
|
||||
});
|
||||
|
||||
// Atualizar o valor final do cálculo
|
||||
@@ -311,19 +329,19 @@ export class CalculoJudicialService {
|
||||
await this.registrarHistorico({
|
||||
calculoId: data.calculoId,
|
||||
usuarioId: data.usuarioId,
|
||||
acao: 'ATUALIZACAO',
|
||||
acao: "ATUALIZACAO",
|
||||
descricao: `Multa adicionada: ${data.tipo} ${data.percentual * 100}%`,
|
||||
valorNovo: valorMulta.toNumber(),
|
||||
metadados: {
|
||||
multaId: multa.id,
|
||||
tipo: data.tipo,
|
||||
percentual: data.percentual
|
||||
}
|
||||
percentual: data.percentual,
|
||||
},
|
||||
});
|
||||
|
||||
return multa;
|
||||
} catch (error) {
|
||||
console.error('Erro ao adicionar multa:', error);
|
||||
console.error("Erro ao adicionar multa:", error);
|
||||
throw new Error(`Falha ao adicionar multa: ${error.message}`);
|
||||
}
|
||||
}
|
||||
@@ -342,20 +360,20 @@ export class CalculoJudicialService {
|
||||
try {
|
||||
// Validar dados
|
||||
if (!data.calculoId || !data.tipo || data.percentual === undefined) {
|
||||
throw new Error('Dados incompletos para adicionar honorários');
|
||||
throw new Error("Dados incompletos para adicionar honorários");
|
||||
}
|
||||
|
||||
// Verificar se o cálculo existe e não está finalizado
|
||||
const calculo = await prisma.calculoJudicial.findUnique({
|
||||
where: { id: data.calculoId }
|
||||
where: { id: data.calculoId },
|
||||
});
|
||||
|
||||
if (!calculo) {
|
||||
throw new Error('Cálculo não encontrado');
|
||||
throw new Error("Cálculo não encontrado");
|
||||
}
|
||||
|
||||
if (calculo.status === 'FINALIZADO') {
|
||||
throw new Error('Não é possível modificar um cálculo finalizado');
|
||||
if (calculo.status === "FINALIZADO") {
|
||||
throw new Error("Não é possível modificar um cálculo finalizado");
|
||||
}
|
||||
|
||||
// Calcular valor dos honorários
|
||||
@@ -370,8 +388,8 @@ export class CalculoJudicialService {
|
||||
percentual: new Decimal(data.percentual),
|
||||
valorBase: new Decimal(data.valorBase),
|
||||
valorHonorario,
|
||||
fundamento: data.fundamento
|
||||
}
|
||||
fundamento: data.fundamento,
|
||||
},
|
||||
});
|
||||
|
||||
// Atualizar o valor final do cálculo
|
||||
@@ -381,19 +399,19 @@ export class CalculoJudicialService {
|
||||
await this.registrarHistorico({
|
||||
calculoId: data.calculoId,
|
||||
usuarioId: data.usuarioId,
|
||||
acao: 'ATUALIZACAO',
|
||||
acao: "ATUALIZACAO",
|
||||
descricao: `Honorários adicionados: ${data.tipo} ${data.percentual * 100}%`,
|
||||
valorNovo: valorHonorario.toNumber(),
|
||||
metadados: {
|
||||
honorarioId: honorario.id,
|
||||
tipo: data.tipo,
|
||||
percentual: data.percentual
|
||||
}
|
||||
percentual: data.percentual,
|
||||
},
|
||||
});
|
||||
|
||||
return honorario;
|
||||
} catch (error) {
|
||||
console.error('Erro ao adicionar honorários:', error);
|
||||
console.error("Erro ao adicionar honorários:", error);
|
||||
throw new Error(`Falha ao adicionar honorários: ${error.message}`);
|
||||
}
|
||||
}
|
||||
@@ -410,33 +428,39 @@ export class CalculoJudicialService {
|
||||
}) {
|
||||
try {
|
||||
// Validar dados
|
||||
if (!data.calculoId || data.numero === undefined || !data.dataVencimento) {
|
||||
throw new Error('Dados incompletos para adicionar parcela');
|
||||
if (
|
||||
!data.calculoId ||
|
||||
data.numero === undefined ||
|
||||
!data.dataVencimento
|
||||
) {
|
||||
throw new Error("Dados incompletos para adicionar parcela");
|
||||
}
|
||||
|
||||
// Verificar se o cálculo existe e não está finalizado
|
||||
const calculo = await prisma.calculoJudicial.findUnique({
|
||||
where: { id: data.calculoId }
|
||||
where: { id: data.calculoId },
|
||||
});
|
||||
|
||||
if (!calculo) {
|
||||
throw new Error('Cálculo não encontrado');
|
||||
throw new Error("Cálculo não encontrado");
|
||||
}
|
||||
|
||||
if (calculo.status === 'FINALIZADO') {
|
||||
throw new Error('Não é possível modificar um cálculo finalizado');
|
||||
if (calculo.status === "FINALIZADO") {
|
||||
throw new Error("Não é possível modificar um cálculo finalizado");
|
||||
}
|
||||
|
||||
// Verificar se já existe uma parcela com este número
|
||||
const existente = await prisma.parcelaCalculo.findFirst({
|
||||
where: {
|
||||
calculoId: data.calculoId,
|
||||
numero: data.numero
|
||||
}
|
||||
numero: data.numero,
|
||||
},
|
||||
});
|
||||
|
||||
if (existente) {
|
||||
throw new Error(`Já existe uma parcela ${data.numero} para este cálculo`);
|
||||
throw new Error(
|
||||
`Já existe uma parcela ${data.numero} para este cálculo`,
|
||||
);
|
||||
}
|
||||
|
||||
// Calcular valores corrigidos se a data de vencimento já passou
|
||||
@@ -448,9 +472,9 @@ export class CalculoJudicialService {
|
||||
if (data.dataVencimento < hoje) {
|
||||
// Calcular correção monetária (usando INPC como padrão)
|
||||
const fatorCorrecao = await this.calcularFatorCorrecao({
|
||||
indice: 'INPC',
|
||||
indice: "INPC",
|
||||
dataInicial: data.dataVencimento,
|
||||
dataFinal: hoje
|
||||
dataFinal: hoje,
|
||||
});
|
||||
|
||||
valorCorrigido = valorCorrigido.mul(fatorCorrecao);
|
||||
@@ -477,8 +501,8 @@ export class CalculoJudicialService {
|
||||
valorJuros,
|
||||
valorMulta,
|
||||
valorTotal,
|
||||
pago: false
|
||||
}
|
||||
pago: false,
|
||||
},
|
||||
});
|
||||
|
||||
// Atualizar o valor final do cálculo
|
||||
@@ -488,20 +512,20 @@ export class CalculoJudicialService {
|
||||
await this.registrarHistorico({
|
||||
calculoId: data.calculoId,
|
||||
usuarioId: data.usuarioId,
|
||||
acao: 'ATUALIZACAO',
|
||||
acao: "ATUALIZACAO",
|
||||
descricao: `Parcela ${data.numero} adicionada`,
|
||||
valorNovo: valorTotal.toNumber(),
|
||||
metadados: {
|
||||
parcelaId: parcela.id,
|
||||
numero: data.numero,
|
||||
dataVencimento: data.dataVencimento,
|
||||
valorOriginal: data.valorOriginal
|
||||
}
|
||||
valorOriginal: data.valorOriginal,
|
||||
},
|
||||
});
|
||||
|
||||
return parcela;
|
||||
} catch (error) {
|
||||
console.error('Erro ao adicionar parcela:', error);
|
||||
console.error("Erro ao adicionar parcela:", error);
|
||||
throw new Error(`Falha ao adicionar parcela: ${error.message}`);
|
||||
}
|
||||
}
|
||||
@@ -519,20 +543,20 @@ export class CalculoJudicialService {
|
||||
try {
|
||||
// Validar dados
|
||||
if (!data.calculoId || !data.parcelaId || !data.dataPagamento) {
|
||||
throw new Error('Dados incompletos para registrar pagamento');
|
||||
throw new Error("Dados incompletos para registrar pagamento");
|
||||
}
|
||||
|
||||
// Buscar a parcela
|
||||
const parcela = await prisma.parcelaCalculo.findUnique({
|
||||
where: { id: data.parcelaId }
|
||||
where: { id: data.parcelaId },
|
||||
});
|
||||
|
||||
if (!parcela) {
|
||||
throw new Error('Parcela não encontrada');
|
||||
throw new Error("Parcela não encontrada");
|
||||
}
|
||||
|
||||
if (parcela.calculoId !== data.calculoId) {
|
||||
throw new Error('Parcela não pertence ao cálculo informado');
|
||||
throw new Error("Parcela não pertence ao cálculo informado");
|
||||
}
|
||||
|
||||
// Atualizar parcela como paga
|
||||
@@ -540,8 +564,8 @@ export class CalculoJudicialService {
|
||||
where: { id: data.parcelaId },
|
||||
data: {
|
||||
pago: true,
|
||||
dataPagamento: data.dataPagamento
|
||||
}
|
||||
dataPagamento: data.dataPagamento,
|
||||
},
|
||||
});
|
||||
|
||||
// Atualizar o valor final do cálculo
|
||||
@@ -551,19 +575,19 @@ export class CalculoJudicialService {
|
||||
await this.registrarHistorico({
|
||||
calculoId: data.calculoId,
|
||||
usuarioId: data.usuarioId,
|
||||
acao: 'ATUALIZACAO',
|
||||
acao: "ATUALIZACAO",
|
||||
descricao: `Pagamento da parcela ${parcela.numero} registrado`,
|
||||
valorNovo: data.valorPago || parcela.valorTotal.toNumber(),
|
||||
metadados: {
|
||||
parcelaId: data.parcelaId,
|
||||
valorPago: data.valorPago || parcela.valorTotal.toNumber(),
|
||||
dataPagamento: data.dataPagamento
|
||||
}
|
||||
dataPagamento: data.dataPagamento,
|
||||
},
|
||||
});
|
||||
|
||||
return parcelaAtualizada;
|
||||
} catch (error) {
|
||||
console.error('Erro ao registrar pagamento de parcela:', error);
|
||||
console.error("Erro ao registrar pagamento de parcela:", error);
|
||||
throw new Error(`Falha ao registrar pagamento: ${error.message}`);
|
||||
}
|
||||
}
|
||||
@@ -582,21 +606,25 @@ export class CalculoJudicialService {
|
||||
}) {
|
||||
try {
|
||||
// Validar dados
|
||||
if (!data.calculoId || !data.tipo || (data.percentual === undefined && data.valorFixo === undefined)) {
|
||||
throw new Error('Dados incompletos para adicionar desconto');
|
||||
if (
|
||||
!data.calculoId ||
|
||||
!data.tipo ||
|
||||
(data.percentual === undefined && data.valorFixo === undefined)
|
||||
) {
|
||||
throw new Error("Dados incompletos para adicionar desconto");
|
||||
}
|
||||
|
||||
// Verificar se o cálculo existe e não está finalizado
|
||||
const calculo = await prisma.calculoJudicial.findUnique({
|
||||
where: { id: data.calculoId }
|
||||
where: { id: data.calculoId },
|
||||
});
|
||||
|
||||
if (!calculo) {
|
||||
throw new Error('Cálculo não encontrado');
|
||||
throw new Error("Cálculo não encontrado");
|
||||
}
|
||||
|
||||
if (calculo.status === 'FINALIZADO') {
|
||||
throw new Error('Não é possível modificar um cálculo finalizado');
|
||||
if (calculo.status === "FINALIZADO") {
|
||||
throw new Error("Não é possível modificar um cálculo finalizado");
|
||||
}
|
||||
|
||||
// Calcular valor do desconto
|
||||
@@ -617,8 +645,8 @@ export class CalculoJudicialService {
|
||||
percentual: data.percentual ? new Decimal(data.percentual) : null,
|
||||
valorFixo: data.valorFixo ? new Decimal(data.valorFixo) : null,
|
||||
valorBase: new Decimal(data.valorBase),
|
||||
valorDesconto
|
||||
}
|
||||
valorDesconto,
|
||||
},
|
||||
});
|
||||
|
||||
// Atualizar o valor final do cálculo
|
||||
@@ -628,20 +656,20 @@ export class CalculoJudicialService {
|
||||
await this.registrarHistorico({
|
||||
calculoId: data.calculoId,
|
||||
usuarioId: data.usuarioId,
|
||||
acao: 'ATUALIZACAO',
|
||||
acao: "ATUALIZACAO",
|
||||
descricao: `Desconto adicionado: ${data.tipo}`,
|
||||
valorNovo: valorDesconto.toNumber() * -1, // Valor negativo para desconto
|
||||
metadados: {
|
||||
descontoId: desconto.id,
|
||||
tipo: data.tipo,
|
||||
percentual: data.percentual,
|
||||
valorFixo: data.valorFixo
|
||||
}
|
||||
valorFixo: data.valorFixo,
|
||||
},
|
||||
});
|
||||
|
||||
return desconto;
|
||||
} catch (error) {
|
||||
console.error('Erro ao adicionar desconto:', error);
|
||||
console.error("Erro ao adicionar desconto:", error);
|
||||
throw new Error(`Falha ao adicionar desconto: ${error.message}`);
|
||||
}
|
||||
}
|
||||
@@ -653,38 +681,38 @@ export class CalculoJudicialService {
|
||||
try {
|
||||
// Verificar se o cálculo existe
|
||||
const calculo = await prisma.calculoJudicial.findUnique({
|
||||
where: { id }
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!calculo) {
|
||||
throw new Error('Cálculo não encontrado');
|
||||
throw new Error("Cálculo não encontrado");
|
||||
}
|
||||
|
||||
if (calculo.status === 'FINALIZADO') {
|
||||
throw new Error('Cálculo já está finalizado');
|
||||
if (calculo.status === "FINALIZADO") {
|
||||
throw new Error("Cálculo já está finalizado");
|
||||
}
|
||||
|
||||
// Finalizar o cálculo
|
||||
const calculoFinalizado = await prisma.calculoJudicial.update({
|
||||
where: { id },
|
||||
data: {
|
||||
status: 'FINALIZADO',
|
||||
atualizadoEm: new Date()
|
||||
}
|
||||
status: "FINALIZADO",
|
||||
atualizadoEm: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
// Registrar no histórico
|
||||
await this.registrarHistorico({
|
||||
calculoId: id,
|
||||
usuarioId,
|
||||
acao: 'FINALIZACAO',
|
||||
descricao: 'Cálculo finalizado',
|
||||
valorNovo: calculo.valorFinal.toNumber()
|
||||
acao: "FINALIZACAO",
|
||||
descricao: "Cálculo finalizado",
|
||||
valorNovo: calculo.valorFinal.toNumber(),
|
||||
});
|
||||
|
||||
return calculoFinalizado;
|
||||
} catch (error) {
|
||||
console.error('Erro ao finalizar cálculo:', error);
|
||||
console.error("Erro ao finalizar cálculo:", error);
|
||||
throw new Error(`Falha ao finalizar cálculo: ${error.message}`);
|
||||
}
|
||||
}
|
||||
@@ -703,7 +731,7 @@ export class CalculoJudicialService {
|
||||
const where: any = {};
|
||||
|
||||
if (filtros.titulo) {
|
||||
where.titulo = { contains: filtros.titulo, mode: 'insensitive' };
|
||||
where.titulo = { contains: filtros.titulo, mode: "insensitive" };
|
||||
}
|
||||
|
||||
if (filtros.status) {
|
||||
@@ -727,16 +755,16 @@ export class CalculoJudicialService {
|
||||
include: {
|
||||
usuario: true,
|
||||
processo: true,
|
||||
cliente: true
|
||||
cliente: true,
|
||||
},
|
||||
orderBy: {
|
||||
criadoEm: 'desc'
|
||||
}
|
||||
criadoEm: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
return calculos;
|
||||
} catch (error) {
|
||||
console.error('Erro ao listar cálculos:', error);
|
||||
console.error("Erro ao listar cálculos:", error);
|
||||
throw new Error(`Falha ao listar cálculos: ${error.message}`);
|
||||
}
|
||||
}
|
||||
@@ -750,14 +778,14 @@ export class CalculoJudicialService {
|
||||
const calculo = await this.buscarCalculo(id);
|
||||
|
||||
if (!calculo) {
|
||||
throw new Error('Cálculo não encontrado');
|
||||
throw new Error("Cálculo não encontrado");
|
||||
}
|
||||
|
||||
// Aqui seria implementada a lógica de geração de PDF
|
||||
// Por enquanto, retornaremos apenas um buffer vazio
|
||||
return Buffer.from('Relatório PDF placeholder');
|
||||
return Buffer.from("Relatório PDF placeholder");
|
||||
} catch (error) {
|
||||
console.error('Erro ao gerar relatório:', error);
|
||||
console.error("Erro ao gerar relatório:", error);
|
||||
throw new Error(`Falha ao gerar relatório: ${error.message}`);
|
||||
}
|
||||
}
|
||||
@@ -771,14 +799,14 @@ export class CalculoJudicialService {
|
||||
const calculo = await this.buscarCalculo(id);
|
||||
|
||||
if (!calculo) {
|
||||
throw new Error('Cálculo não encontrado');
|
||||
throw new Error("Cálculo não encontrado");
|
||||
}
|
||||
|
||||
// Aqui seria implementada a lógica de exportação para Excel
|
||||
// Por enquanto, retornaremos apenas um buffer vazio
|
||||
return Buffer.from('Excel placeholder');
|
||||
return Buffer.from("Excel placeholder");
|
||||
} catch (error) {
|
||||
console.error('Erro ao exportar para Excel:', error);
|
||||
console.error("Erro ao exportar para Excel:", error);
|
||||
throw new Error(`Falha ao exportar para Excel: ${error.message}`);
|
||||
}
|
||||
}
|
||||
@@ -808,29 +836,27 @@ export class CalculoJudicialService {
|
||||
// Índices do ano inicial
|
||||
{
|
||||
anoReferencia: anoInicial,
|
||||
mesReferencia: { gte: mesInicial }
|
||||
mesReferencia: { gte: mesInicial },
|
||||
},
|
||||
// Anos intermediários (se houver)
|
||||
{
|
||||
anoReferencia: { gt: anoInicial, lt: anoFinal }
|
||||
anoReferencia: { gt: anoInicial, lt: anoFinal },
|
||||
},
|
||||
// Índices do ano final
|
||||
{
|
||||
anoReferencia: anoFinal,
|
||||
mesReferencia: { lte: mesFinal }
|
||||
}
|
||||
]
|
||||
mesReferencia: { lte: mesFinal },
|
||||
},
|
||||
],
|
||||
},
|
||||
orderBy: [
|
||||
{ anoReferencia: 'asc' },
|
||||
{ mesReferencia: 'asc' }
|
||||
]
|
||||
orderBy: [{ anoReferencia: "asc" }, { mesReferencia: "asc" }],
|
||||
});
|
||||
|
||||
// Se não houver índices suficientes, usar uma estimativa
|
||||
if (indices.length === 0) {
|
||||
const mesesDiferenca = (anoFinal - anoInicial) * 12 + (mesFinal - mesInicial);
|
||||
|
||||
const mesesDiferenca =
|
||||
(anoFinal - anoInicial) * 12 + (mesFinal - mesInicial);
|
||||
|
||||
// Taxa média mensal estimada (usar 0.5% ao mês como estimativa conservadora)
|
||||
const taxaMediaMensal = 0.005;
|
||||
return new Decimal(Math.pow(1 + taxaMediaMensal, mesesDiferenca));
|
||||
@@ -838,16 +864,16 @@ export class CalculoJudicialService {
|
||||
|
||||
// Calcular o fator de correção acumulado
|
||||
let fatorAcumulado = new Decimal(1);
|
||||
|
||||
|
||||
for (const indice of indices) {
|
||||
// Somar 1 ao valor do índice (ex: índice de 0.0082 vira fator de 1.0082)
|
||||
const fator = new Decimal(1).add(indice.valor);
|
||||
fatorAcumulado = fatorAcumulado.mul(fator);
|
||||
}
|
||||
|
||||
|
||||
return fatorAcumulado;
|
||||
} catch (error) {
|
||||
console.error('Erro ao calcular fator de correção:', error);
|
||||
console.error("Erro ao calcular fator de correção:", error);
|
||||
throw new Error(`Falha ao calcular fator de correção: ${error.message}`);
|
||||
}
|
||||
}
|
||||
@@ -860,21 +886,21 @@ export class CalculoJudicialService {
|
||||
taxa: number,
|
||||
tipo: string,
|
||||
dataInicial: Date,
|
||||
dataFinal: Date
|
||||
dataFinal: Date,
|
||||
): number {
|
||||
// Calcular tempo entre as datas (em meses)
|
||||
const meses = this.calcularMesesAtraso(dataInicial, dataFinal);
|
||||
|
||||
|
||||
// Juros simples: valorBase * taxa * tempo
|
||||
if (tipo === 'SIMPLES') {
|
||||
if (tipo === "SIMPLES") {
|
||||
return valorBase * taxa * meses;
|
||||
}
|
||||
|
||||
|
||||
// Juros compostos: valorBase * ((1 + taxa) ^ tempo - 1)
|
||||
if (tipo === 'COMPOSTO') {
|
||||
if (tipo === "COMPOSTO") {
|
||||
return valorBase * (Math.pow(1 + taxa, meses) - 1);
|
||||
}
|
||||
|
||||
|
||||
return 0; // Tipo de juros não reconhecido
|
||||
}
|
||||
|
||||
@@ -884,21 +910,25 @@ export class CalculoJudicialService {
|
||||
private calcularMesesAtraso(dataInicial: Date, dataFinal: Date): number {
|
||||
const diferencaAnos = dataFinal.getFullYear() - dataInicial.getFullYear();
|
||||
const diferencaMeses = dataFinal.getMonth() - dataInicial.getMonth();
|
||||
|
||||
|
||||
let meses = diferencaAnos * 12 + diferencaMeses;
|
||||
|
||||
|
||||
// Ajustar para dias (se a diferença for menos de um mês completo)
|
||||
const diaInicial = dataInicial.getDate();
|
||||
const diaFinal = dataFinal.getDate();
|
||||
|
||||
|
||||
if (diaFinal < diaInicial) {
|
||||
meses -= 1;
|
||||
}
|
||||
|
||||
|
||||
// Adicionar fração do mês se necessário
|
||||
const diasNoMes = new Date(dataInicial.getFullYear(), dataInicial.getMonth() + 1, 0).getDate();
|
||||
const diasNoMes = new Date(
|
||||
dataInicial.getFullYear(),
|
||||
dataInicial.getMonth() + 1,
|
||||
0,
|
||||
).getDate();
|
||||
const fracaoDias = (diasNoMes - diaInicial + diaFinal) / diasNoMes;
|
||||
|
||||
|
||||
return Math.max(0, meses + fracaoDias);
|
||||
}
|
||||
|
||||
@@ -916,23 +946,24 @@ export class CalculoJudicialService {
|
||||
multas: true,
|
||||
honorarios: true,
|
||||
parcelas: true,
|
||||
descontos: true
|
||||
}
|
||||
descontos: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!calculo) {
|
||||
throw new Error('Cálculo não encontrado');
|
||||
throw new Error("Cálculo não encontrado");
|
||||
}
|
||||
|
||||
// Valor base corrigido (usar o valor da última correção ou o valor base original)
|
||||
let valorCorrigido = calculo.valorBase;
|
||||
|
||||
|
||||
if (calculo.correcoes.length > 0) {
|
||||
// Ordenar correções por data
|
||||
const correcoes = [...calculo.correcoes].sort((a, b) =>
|
||||
new Date(b.dataFinal).getTime() - new Date(a.dataFinal).getTime()
|
||||
const correcoes = [...calculo.correcoes].sort(
|
||||
(a, b) =>
|
||||
new Date(b.dataFinal).getTime() - new Date(a.dataFinal).getTime(),
|
||||
);
|
||||
|
||||
|
||||
// Usar o valor da correção mais recente
|
||||
valorCorrigido = correcoes[0].valorCorrigido;
|
||||
}
|
||||
@@ -970,7 +1001,12 @@ export class CalculoJudicialService {
|
||||
}
|
||||
|
||||
// Calcular valor final: Valor Corrigido + Juros + Multas + Honorários + Parcelas - Descontos
|
||||
const valorFinal = valorCorrigido.add(valorJuros).add(valorMultas).add(valorHonorarios).add(valorParcelas).sub(valorDescontos);
|
||||
const valorFinal = valorCorrigido
|
||||
.add(valorJuros)
|
||||
.add(valorMultas)
|
||||
.add(valorHonorarios)
|
||||
.add(valorParcelas)
|
||||
.sub(valorDescontos);
|
||||
|
||||
// Atualizar o cálculo
|
||||
await prisma.calculoJudicial.update({
|
||||
@@ -978,13 +1014,13 @@ export class CalculoJudicialService {
|
||||
data: {
|
||||
valorCorrigido,
|
||||
valorFinal,
|
||||
atualizadoEm: new Date()
|
||||
}
|
||||
atualizadoEm: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
return valorFinal;
|
||||
} catch (error) {
|
||||
console.error('Erro ao atualizar valor do cálculo:', error);
|
||||
console.error("Erro ao atualizar valor do cálculo:", error);
|
||||
throw new Error(`Falha ao atualizar valor: ${error.message}`);
|
||||
}
|
||||
}
|
||||
@@ -1009,13 +1045,17 @@ export class CalculoJudicialService {
|
||||
usuarioId: data.usuarioId,
|
||||
acao: data.acao,
|
||||
descricao: data.descricao,
|
||||
valorAnterior: data.valorAnterior !== undefined ? new Decimal(data.valorAnterior) : null,
|
||||
valorNovo: data.valorNovo !== undefined ? new Decimal(data.valorNovo) : null,
|
||||
metadados: data.metadados || {}
|
||||
}
|
||||
valorAnterior:
|
||||
data.valorAnterior !== undefined
|
||||
? new Decimal(data.valorAnterior)
|
||||
: null,
|
||||
valorNovo:
|
||||
data.valorNovo !== undefined ? new Decimal(data.valorNovo) : null,
|
||||
metadados: data.metadados || {},
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao registrar histórico:', error);
|
||||
console.error("Erro ao registrar histórico:", error);
|
||||
// Apenas logar o erro, não interrompe o fluxo
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createHash } from 'crypto';
|
||||
import { createHash } from "crypto";
|
||||
|
||||
/**
|
||||
* Versão simplificada do MetadataCollector para quando faltam dependências
|
||||
@@ -16,7 +16,7 @@ export class FallbackMetadataCollector {
|
||||
[key: string]: any;
|
||||
}) {
|
||||
const timestamp = new Date();
|
||||
|
||||
|
||||
// Retornar metadados simplificados
|
||||
return {
|
||||
timestamp: timestamp.toISOString(),
|
||||
@@ -26,20 +26,23 @@ export class FallbackMetadataCollector {
|
||||
fileName: data.fileName,
|
||||
fileSize: data.fileSize,
|
||||
fileSizeFormatted: this.formatFileSize(data.fileSize),
|
||||
integrityHash: this.generateHash(data.fileName, data.fileSize.toString(), timestamp.toISOString())
|
||||
integrityHash: this.generateHash(
|
||||
data.fileName,
|
||||
data.fileSize.toString(),
|
||||
timestamp.toISOString(),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private formatFileSize(bytes: number): string {
|
||||
if (bytes < 1024) return bytes + ' bytes';
|
||||
else if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB';
|
||||
else if (bytes < 1024 * 1024 * 1024) return (bytes / (1024 * 1024)).toFixed(2) + ' MB';
|
||||
else return (bytes / (1024 * 1024 * 1024)).toFixed(2) + ' GB';
|
||||
if (bytes < 1024) return bytes + " bytes";
|
||||
else if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + " KB";
|
||||
else if (bytes < 1024 * 1024 * 1024)
|
||||
return (bytes / (1024 * 1024)).toFixed(2) + " MB";
|
||||
else return (bytes / (1024 * 1024 * 1024)).toFixed(2) + " GB";
|
||||
}
|
||||
|
||||
|
||||
private generateHash(...values: string[]): string {
|
||||
return createHash('sha256')
|
||||
.update(values.join('|'))
|
||||
.digest('hex');
|
||||
return createHash("sha256").update(values.join("|")).digest("hex");
|
||||
}
|
||||
}
|
||||
@@ -1,63 +1,71 @@
|
||||
import { createHash } from 'crypto';
|
||||
import sharp from 'sharp';
|
||||
import { createHash } from "crypto";
|
||||
|
||||
import sharp from "sharp";
|
||||
|
||||
export class FileValidator {
|
||||
async validateFile(buffer: Buffer, mimeType: string) {
|
||||
try {
|
||||
// Validação básica
|
||||
if (!buffer || buffer.length === 0) {
|
||||
return { valid: false, error: 'Arquivo vazio' };
|
||||
return { valid: false, error: "Arquivo vazio" };
|
||||
}
|
||||
|
||||
// Verificar tamanho máximo (10MB)
|
||||
const MAX_SIZE = 10 * 1024 * 1024;
|
||||
if (buffer.length > MAX_SIZE) {
|
||||
return { valid: false, error: 'Arquivo muito grande (máximo 10MB)' };
|
||||
return { valid: false, error: "Arquivo muito grande (máximo 10MB)" };
|
||||
}
|
||||
|
||||
// Verificar tipos de arquivo permitidos
|
||||
const allowedTypes = [
|
||||
'application/pdf',
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'image.webp'
|
||||
"application/pdf",
|
||||
"image/jpeg",
|
||||
"image/png",
|
||||
"image/gif",
|
||||
"image.webp",
|
||||
];
|
||||
|
||||
if (!allowedTypes.includes(mimeType)) {
|
||||
return { valid: false, error: 'Tipo de arquivo não suportado. Use PDF, JPEG, PNG ou GIF.' };
|
||||
return {
|
||||
valid: false,
|
||||
error: "Tipo de arquivo não suportado. Use PDF, JPEG, PNG ou GIF.",
|
||||
};
|
||||
}
|
||||
|
||||
// Verificação adicional para JPEG
|
||||
if (mimeType === 'image/jpeg') {
|
||||
if (mimeType === "image/jpeg") {
|
||||
try {
|
||||
// Verificar se é um JPEG válido tentando processá-lo
|
||||
await sharp(buffer).metadata();
|
||||
} catch (jpegError) {
|
||||
console.error('Erro ao validar JPEG:', jpegError);
|
||||
return {
|
||||
valid: false,
|
||||
error: 'O arquivo JPEG parece estar corrompido. Tente salvar a imagem em um formato diferente e enviar novamente.'
|
||||
console.error("Erro ao validar JPEG:", jpegError);
|
||||
return {
|
||||
valid: false,
|
||||
error:
|
||||
"O arquivo JPEG parece estar corrompido. Tente salvar a imagem em um formato diferente e enviar novamente.",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return { valid: true };
|
||||
} catch (error) {
|
||||
console.error('Erro na validação de arquivo:', error);
|
||||
return { valid: false, error: `Erro ao validar arquivo: ${error.message}` };
|
||||
console.error("Erro na validação de arquivo:", error);
|
||||
return {
|
||||
valid: false,
|
||||
error: `Erro ao validar arquivo: ${error.message}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async validateMetadata(metadata: any) {
|
||||
// Campos básicos obrigatórios
|
||||
const requiredFields = ['userAgent', 'timestamp'];
|
||||
const missingFields = requiredFields.filter(field => !metadata[field]);
|
||||
|
||||
const requiredFields = ["userAgent", "timestamp"];
|
||||
const missingFields = requiredFields.filter((field) => !metadata[field]);
|
||||
|
||||
if (missingFields.length > 0) {
|
||||
return {
|
||||
valid: false,
|
||||
error: `Dados necessários ausentes: ${missingFields.join(', ')}`
|
||||
error: `Dados necessários ausentes: ${missingFields.join(", ")}`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -66,9 +74,9 @@ export class FileValidator {
|
||||
|
||||
generateHashes(buffer: Buffer) {
|
||||
return {
|
||||
sha256: createHash('sha256').update(buffer).digest('hex'),
|
||||
sha512: createHash('sha512').update(buffer).digest('hex'),
|
||||
md5: createHash('md5').update(buffer).digest('hex')
|
||||
sha256: createHash("sha256").update(buffer).digest("hex"),
|
||||
sha512: createHash("sha512").update(buffer).digest("hex"),
|
||||
md5: createHash("md5").update(buffer).digest("hex"),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,57 +1,75 @@
|
||||
import axios from 'axios';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { createHash } from 'crypto';
|
||||
import { createHash } from "crypto";
|
||||
|
||||
import axios from "axios";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
// Tentativa de importar o UAParser com fallback para evitar quebra caso não esteja instalado
|
||||
let UAParser;
|
||||
try {
|
||||
UAParser = require('ua-parser-js');
|
||||
UAParser = require("ua-parser-js");
|
||||
} catch (error) {
|
||||
console.warn('Aviso: UAParser não está disponível. Usando método alternativo para analisar User-Agent.');
|
||||
console.warn(
|
||||
"Aviso: UAParser não está disponível. Usando método alternativo para analisar User-Agent.",
|
||||
);
|
||||
// Implementação simples de fallback para quando a biblioteca não está disponível
|
||||
UAParser = class SimpleBrowserDetect {
|
||||
constructor(userAgent) {
|
||||
this.userAgent = userAgent;
|
||||
}
|
||||
|
||||
|
||||
getResult() {
|
||||
const isMobile = /mobile|android|iphone|ipod|ipad/i.test(this.userAgent);
|
||||
const isWindows = /windows/i.test(this.userAgent);
|
||||
const isMac = /macintosh|mac os/i.test(this.userAgent);
|
||||
const isLinux = /linux/i.test(this.userAgent);
|
||||
|
||||
|
||||
const chromeMatch = this.userAgent.match(/chrome\/(\d+)/i);
|
||||
const firefoxMatch = this.userAgent.match(/firefox\/(\d+)/i);
|
||||
const safariMatch = this.userAgent.match(/safari\/(\d+)/i);
|
||||
const edgeMatch = this.userAgent.match(/edge\/(\d+)|edg\/(\d+)/i);
|
||||
|
||||
|
||||
return {
|
||||
browser: {
|
||||
name: edgeMatch ? 'Edge' :
|
||||
chromeMatch ? 'Chrome' :
|
||||
firefoxMatch ? 'Firefox' :
|
||||
safariMatch ? 'Safari' : 'Desconhecido',
|
||||
version: (edgeMatch || chromeMatch || firefoxMatch || safariMatch || ['', '0'])[1],
|
||||
major: (edgeMatch || chromeMatch || firefoxMatch || safariMatch || ['', '0'])[1],
|
||||
name: edgeMatch
|
||||
? "Edge"
|
||||
: chromeMatch
|
||||
? "Chrome"
|
||||
: firefoxMatch
|
||||
? "Firefox"
|
||||
: safariMatch
|
||||
? "Safari"
|
||||
: "Desconhecido",
|
||||
version: (edgeMatch ||
|
||||
chromeMatch ||
|
||||
firefoxMatch ||
|
||||
safariMatch || ["", "0"])[1],
|
||||
major: (edgeMatch ||
|
||||
chromeMatch ||
|
||||
firefoxMatch ||
|
||||
safariMatch || ["", "0"])[1],
|
||||
},
|
||||
engine: {
|
||||
name: 'Desconhecido',
|
||||
version: '',
|
||||
name: "Desconhecido",
|
||||
version: "",
|
||||
},
|
||||
os: {
|
||||
name: isWindows ? 'Windows' :
|
||||
isMac ? 'Mac OS' :
|
||||
isLinux ? 'Linux' : 'Desconhecido',
|
||||
version: '',
|
||||
name: isWindows
|
||||
? "Windows"
|
||||
: isMac
|
||||
? "Mac OS"
|
||||
: isLinux
|
||||
? "Linux"
|
||||
: "Desconhecido",
|
||||
version: "",
|
||||
},
|
||||
device: {
|
||||
vendor: '',
|
||||
model: '',
|
||||
type: isMobile ? 'mobile' : 'desktop',
|
||||
vendor: "",
|
||||
model: "",
|
||||
type: isMobile ? "mobile" : "desktop",
|
||||
},
|
||||
cpu: {
|
||||
architecture: '',
|
||||
}
|
||||
architecture: "",
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -83,92 +101,95 @@ export class MetadataCollector {
|
||||
// Gerar identificador único para a coleta
|
||||
const collectionId = uuidv4();
|
||||
const collectionTimestamp = new Date();
|
||||
|
||||
|
||||
// Parser para informações do navegador
|
||||
const parser = new UAParser(data.userAgent);
|
||||
const browserInfo = parser.getResult();
|
||||
|
||||
|
||||
// Obter geolocalização a partir do IP se não fornecida diretamente
|
||||
let geoInfo = null;
|
||||
if (!data.latitude && !data.longitude) {
|
||||
try {
|
||||
geoInfo = await this.getGeolocationFromIP(data.ipAddress);
|
||||
} catch (error) {
|
||||
console.warn('Falha ao obter geolocalização:', error);
|
||||
console.warn("Falha ao obter geolocalização:", error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Construir o objeto de metadados
|
||||
return {
|
||||
// Identificação da coleta
|
||||
collectionId,
|
||||
collectionTimestamp: collectionTimestamp.toISOString(),
|
||||
|
||||
|
||||
// Dados do usuário
|
||||
userInfo: {
|
||||
ipAddress: data.ipAddress,
|
||||
userAgent: data.userAgent,
|
||||
language: data.language || 'não informado',
|
||||
language: data.language || "não informado",
|
||||
browser: {
|
||||
name: browserInfo.browser.name || 'desconhecido',
|
||||
version: browserInfo.browser.version || 'desconhecido',
|
||||
major: browserInfo.browser.major || 'desconhecido',
|
||||
name: browserInfo.browser.name || "desconhecido",
|
||||
version: browserInfo.browser.version || "desconhecido",
|
||||
major: browserInfo.browser.major || "desconhecido",
|
||||
},
|
||||
engine: {
|
||||
name: browserInfo.engine.name || 'desconhecido',
|
||||
version: browserInfo.engine.version || 'desconhecido',
|
||||
name: browserInfo.engine.name || "desconhecido",
|
||||
version: browserInfo.engine.version || "desconhecido",
|
||||
},
|
||||
os: {
|
||||
name: browserInfo.os.name || 'desconhecido',
|
||||
version: browserInfo.os.version || 'desconhecido',
|
||||
name: browserInfo.os.name || "desconhecido",
|
||||
version: browserInfo.os.version || "desconhecido",
|
||||
},
|
||||
device: {
|
||||
vendor: browserInfo.device.vendor || 'desconhecido',
|
||||
model: browserInfo.device.model || 'desconhecido',
|
||||
type: browserInfo.device.type || 'desconhecido',
|
||||
vendor: browserInfo.device.vendor || "desconhecido",
|
||||
model: browserInfo.device.model || "desconhecido",
|
||||
type: browserInfo.device.type || "desconhecido",
|
||||
},
|
||||
cpu: {
|
||||
architecture: browserInfo.cpu.architecture || 'desconhecido',
|
||||
architecture: browserInfo.cpu.architecture || "desconhecido",
|
||||
},
|
||||
viewportSize: data.viewportWidth && data.viewportHeight
|
||||
? `${data.viewportWidth}x${data.viewportHeight}`
|
||||
: 'não informado',
|
||||
referrer: data.referrer || 'não informado',
|
||||
viewportSize:
|
||||
data.viewportWidth && data.viewportHeight
|
||||
? `${data.viewportWidth}x${data.viewportHeight}`
|
||||
: "não informado",
|
||||
referrer: data.referrer || "não informado",
|
||||
},
|
||||
|
||||
|
||||
// Dados do arquivo
|
||||
fileInfo: {
|
||||
name: data.fileName,
|
||||
size: data.fileSize,
|
||||
sizeFormatted: this.formatFileSize(data.fileSize),
|
||||
modifiedDate: data.fileModified ? data.fileModified.toISOString() : 'não informado',
|
||||
modifiedDate: data.fileModified
|
||||
? data.fileModified.toISOString()
|
||||
: "não informado",
|
||||
},
|
||||
|
||||
|
||||
// Dados de localização
|
||||
locationInfo: geoInfo || {
|
||||
latitude: data.latitude || null,
|
||||
longitude: data.longitude || null,
|
||||
city: 'não disponível',
|
||||
region: 'não disponível',
|
||||
country: 'não disponível',
|
||||
timezone: 'não disponível'
|
||||
city: "não disponível",
|
||||
region: "não disponível",
|
||||
country: "não disponível",
|
||||
timezone: "não disponível",
|
||||
},
|
||||
|
||||
|
||||
// Timestamp oficial do sistema
|
||||
timestamp: data.timestamp.toISOString(),
|
||||
|
||||
|
||||
// Hash da coleta para garantir integridade
|
||||
integrityHash: this.generateIntegrityHash(
|
||||
data.ipAddress,
|
||||
data.ipAddress,
|
||||
data.userAgent,
|
||||
data.fileName,
|
||||
data.fileSize.toString(),
|
||||
collectionTimestamp.toISOString()
|
||||
)
|
||||
collectionTimestamp.toISOString(),
|
||||
),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Erro ao coletar metadados:', error);
|
||||
|
||||
console.error("Erro ao coletar metadados:", error);
|
||||
|
||||
// Retornar metadados simplificados se algo der errado
|
||||
return {
|
||||
timestamp: new Date().toISOString(),
|
||||
@@ -176,70 +197,74 @@ export class MetadataCollector {
|
||||
ipAddress: data.ipAddress,
|
||||
fileName: data.fileName,
|
||||
fileSize: data.fileSize,
|
||||
error: 'Falha na coleta completa de metadados'
|
||||
error: "Falha na coleta completa de metadados",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Formata o tamanho do arquivo para exibição
|
||||
*/
|
||||
private formatFileSize(bytes: number): string {
|
||||
if (bytes < 1024) return bytes + ' bytes';
|
||||
else if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB';
|
||||
else if (bytes < 1024 * 1024 * 1024) return (bytes / (1024 * 1024)).toFixed(2) + ' MB';
|
||||
else return (bytes / (1024 * 1024 * 1024)).toFixed(2) + ' GB';
|
||||
if (bytes < 1024) return bytes + " bytes";
|
||||
else if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + " KB";
|
||||
else if (bytes < 1024 * 1024 * 1024)
|
||||
return (bytes / (1024 * 1024)).toFixed(2) + " MB";
|
||||
else return (bytes / (1024 * 1024 * 1024)).toFixed(2) + " GB";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gera um hash de integridade para os metadados coletados
|
||||
*/
|
||||
private generateIntegrityHash(...inputs: string[]): string {
|
||||
try {
|
||||
return createHash('sha256')
|
||||
.update(inputs.join('|'))
|
||||
.digest('hex');
|
||||
return createHash("sha256").update(inputs.join("|")).digest("hex");
|
||||
} catch (error) {
|
||||
console.error('Erro ao gerar hash de integridade:', error);
|
||||
return 'hash-error-' + Date.now();
|
||||
console.error("Erro ao gerar hash de integridade:", error);
|
||||
return "hash-error-" + Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtém informações de geolocalização a partir do endereço IP
|
||||
*/
|
||||
private async getGeolocationFromIP(ipAddress: string) {
|
||||
try {
|
||||
// Apenas para endereços IP públicos, ignorar IPs locais
|
||||
if (ipAddress === '127.0.0.1' || ipAddress === '::1' || ipAddress.startsWith('192.168.') || ipAddress.startsWith('10.')) {
|
||||
if (
|
||||
ipAddress === "127.0.0.1" ||
|
||||
ipAddress === "::1" ||
|
||||
ipAddress.startsWith("192.168.") ||
|
||||
ipAddress.startsWith("10.")
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// Usar API pública de geolocalização
|
||||
const response = await axios.get(`https://ipinfo.io/${ipAddress}/json`);
|
||||
const data = response.data;
|
||||
|
||||
|
||||
// Extrair coordenadas (formato: "lat,long")
|
||||
let latitude = null;
|
||||
let longitude = null;
|
||||
|
||||
|
||||
if (data.loc) {
|
||||
const [lat, lng] = data.loc.split(',');
|
||||
const [lat, lng] = data.loc.split(",");
|
||||
latitude = parseFloat(lat);
|
||||
longitude = parseFloat(lng);
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
latitude,
|
||||
longitude,
|
||||
city: data.city || 'não disponível',
|
||||
region: data.region || 'não disponível',
|
||||
country: data.country || 'não disponível',
|
||||
timezone: data.timezone || 'não disponível',
|
||||
provider: 'ipinfo.io'
|
||||
city: data.city || "não disponível",
|
||||
region: data.region || "não disponível",
|
||||
country: data.country || "não disponível",
|
||||
timezone: data.timezone || "não disponível",
|
||||
provider: "ipinfo.io",
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Erro ao obter geolocalização:', error);
|
||||
console.error("Erro ao obter geolocalização:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import puppeteer from 'puppeteer';
|
||||
import { prisma } from '../prismaClient';
|
||||
import puppeteer from "puppeteer";
|
||||
|
||||
import { prisma } from "../prismaClient";
|
||||
|
||||
export class PdfService {
|
||||
private async getConfiguracao() {
|
||||
try {
|
||||
// Buscar configuração ou usar valores padrão
|
||||
const config = await prisma.configuracaoDocumento.findFirst({
|
||||
orderBy: { criadoEm: 'desc' },
|
||||
orderBy: { criadoEm: "desc" },
|
||||
select: {
|
||||
id: true,
|
||||
headerImageUrl: true,
|
||||
@@ -17,44 +18,48 @@ export class PdfService {
|
||||
timbradoMarginBottom: true,
|
||||
timbradoMarginLeft: true,
|
||||
timbradoMarginRight: true,
|
||||
criadoEm: true
|
||||
}
|
||||
criadoEm: true,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('Configuração encontrada:', config); // Debug
|
||||
console.log("Configuração encontrada:", config); // Debug
|
||||
|
||||
// Retornar configuração padrão se não existir
|
||||
return {
|
||||
cabecalhoHtml: config?.cabecalhoHtml || `
|
||||
cabecalhoHtml:
|
||||
config?.cabecalhoHtml ||
|
||||
`
|
||||
<div style="text-align: center; padding: 20px;">
|
||||
<h2 style="margin:0;font-size:14pt">{{escritorio.nome}}</h2>
|
||||
</div>
|
||||
`,
|
||||
rodapeHtml: config?.rodapeHtml || `
|
||||
rodapeHtml:
|
||||
config?.rodapeHtml ||
|
||||
`
|
||||
<div style="text-align: center; font-size: 10pt;">
|
||||
{{escritorio.endereco}}<br>
|
||||
Tel: {{escritorio.telefone}} | Email: {{escritorio.email}}
|
||||
</div>
|
||||
`,
|
||||
logoUrl: config?.logoUrl || '',
|
||||
timbradoUrl: config?.timbradoUrl || '',
|
||||
logoUrl: config?.logoUrl || "",
|
||||
timbradoUrl: config?.timbradoUrl || "",
|
||||
timbradoMarginTop: config?.timbradoMarginTop || 100,
|
||||
timbradoMarginBottom: config?.timbradoMarginBottom || 50,
|
||||
timbradoMarginLeft: config?.timbradoMarginLeft || 50,
|
||||
timbradoMarginRight: config?.timbradoMarginRight || 50
|
||||
timbradoMarginRight: config?.timbradoMarginRight || 50,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar configuração:', error);
|
||||
throw new Error('Falha ao buscar configuração');
|
||||
console.error("Erro ao buscar configuração:", error);
|
||||
throw new Error("Falha ao buscar configuração");
|
||||
}
|
||||
}
|
||||
|
||||
private gerarHTML(content: string, config: any) {
|
||||
const timbradoUrl = config.timbradoUrl
|
||||
? `${process.env.BACKEND_URL || 'http://localhost:5000'}${config.timbradoUrl}`
|
||||
: '';
|
||||
const timbradoUrl = config.timbradoUrl
|
||||
? `${process.env.BACKEND_URL || "http://localhost:5000"}${config.timbradoUrl}`
|
||||
: "";
|
||||
|
||||
console.log('Gerando PDF com timbrado:', timbradoUrl);
|
||||
console.log("Gerando PDF com timbrado:", timbradoUrl);
|
||||
|
||||
return `
|
||||
<!DOCTYPE html>
|
||||
@@ -83,7 +88,7 @@ export class PdfService {
|
||||
width: 21cm;
|
||||
min-height: 29.7cm;
|
||||
padding: ${config.timbradoMarginTop || 100}px ${config.timbradoMarginRight || 50}px ${config.timbradoMarginBottom || 50}px ${config.timbradoMarginLeft || 50}px;
|
||||
background: ${timbradoUrl ? `url('${timbradoUrl}')` : 'none'};
|
||||
background: ${timbradoUrl ? `url('${timbradoUrl}')` : "none"};
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
@@ -110,44 +115,43 @@ export class PdfService {
|
||||
async gerarPDF(conteudo: string, escritorio: any) {
|
||||
try {
|
||||
const config = await prisma.configuracaoDocumento.findFirst({
|
||||
orderBy: { criadoEm: 'desc' }
|
||||
orderBy: { criadoEm: "desc" },
|
||||
});
|
||||
|
||||
console.log('Configuração do PDF:', config);
|
||||
|
||||
console.log("Configuração do PDF:", config);
|
||||
|
||||
const html = this.gerarHTML(conteudo, config);
|
||||
|
||||
|
||||
const browser = await puppeteer.launch({
|
||||
headless: 'new',
|
||||
args: ['--no-sandbox']
|
||||
headless: "new",
|
||||
args: ["--no-sandbox"],
|
||||
});
|
||||
|
||||
const page = await browser.newPage();
|
||||
|
||||
|
||||
await page.setViewport({
|
||||
width: 794, // A4 width in pixels (72 dpi)
|
||||
height: 1123,
|
||||
deviceScaleFactor: 2
|
||||
deviceScaleFactor: 2,
|
||||
});
|
||||
|
||||
await page.setContent(html, {
|
||||
waitUntil: ['networkidle0', 'load'],
|
||||
timeout: 30000
|
||||
await page.setContent(html, {
|
||||
waitUntil: ["networkidle0", "load"],
|
||||
timeout: 30000,
|
||||
});
|
||||
|
||||
const pdfBuffer = await page.pdf({
|
||||
format: 'A4',
|
||||
format: "A4",
|
||||
printBackground: true,
|
||||
margin: { top: '0', right: '0', bottom: '0', left: '0' },
|
||||
preferCSSPageSize: true
|
||||
margin: { top: "0", right: "0", bottom: "0", left: "0" },
|
||||
preferCSSPageSize: true,
|
||||
});
|
||||
|
||||
await browser.close();
|
||||
return Buffer.from(pdfBuffer);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Erro ao gerar PDF:', error);
|
||||
throw new Error('Falha ao gerar PDF');
|
||||
console.error("Erro ao gerar PDF:", error);
|
||||
throw new Error("Falha ao gerar PDF");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
import puppeteer from 'puppeteer';
|
||||
import { prisma } from '../prismaClient';
|
||||
import { PDFDocument, rgb, StandardFonts } from 'pdf-lib';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import fs from "fs/promises";
|
||||
import path from "path";
|
||||
|
||||
import { PDFDocument, rgb, StandardFonts } from "pdf-lib";
|
||||
import puppeteer from "puppeteer";
|
||||
|
||||
import { prisma } from "../prismaClient";
|
||||
|
||||
/**
|
||||
* Serviço para geração e manipulação de arquivos PDF
|
||||
@@ -15,7 +17,7 @@ export class PdfService {
|
||||
async criarDocumento() {
|
||||
return await PDFDocument.create();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Carrega um documento PDF existente
|
||||
* @param pdfBytes - Bytes do PDF
|
||||
@@ -24,7 +26,7 @@ export class PdfService {
|
||||
async carregarDocumento(pdfBytes: Uint8Array) {
|
||||
return await PDFDocument.load(pdfBytes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adiciona uma página ao documento
|
||||
* @param doc - Documento PDF
|
||||
@@ -34,7 +36,7 @@ export class PdfService {
|
||||
adicionarPagina(doc: any, tamanho = [595.28, 841.89]) {
|
||||
return doc.addPage(tamanho);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Salva o documento como buffer
|
||||
* @param doc - Documento PDF
|
||||
@@ -44,7 +46,7 @@ export class PdfService {
|
||||
const pdfBytes = await doc.save();
|
||||
return Buffer.from(pdfBytes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Carrega uma fonte padrão
|
||||
* @param doc - Documento PDF
|
||||
@@ -54,7 +56,7 @@ export class PdfService {
|
||||
async carregarFonte(doc: any, fonte: StandardFonts) {
|
||||
return await doc.embedFont(fonte);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Desenha texto em uma página
|
||||
* @param pagina - Página do PDF
|
||||
@@ -72,7 +74,7 @@ export class PdfService {
|
||||
y: number,
|
||||
fonte: any,
|
||||
tamanho: number,
|
||||
cor = rgb(0, 0, 0)
|
||||
cor = rgb(0, 0, 0),
|
||||
) {
|
||||
pagina.drawText(texto, {
|
||||
x,
|
||||
@@ -82,7 +84,7 @@ export class PdfService {
|
||||
color: cor,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cria um cabeçalho para o documento
|
||||
* @param pagina - Página do PDF
|
||||
@@ -97,20 +99,12 @@ export class PdfService {
|
||||
titulo: string,
|
||||
fonte: any,
|
||||
margemSuperior: number,
|
||||
margem: number
|
||||
margem: number,
|
||||
): number {
|
||||
const yPos = pagina.getHeight() - margemSuperior;
|
||||
|
||||
this.desenharTexto(
|
||||
pagina,
|
||||
titulo,
|
||||
margem,
|
||||
yPos,
|
||||
fonte,
|
||||
18,
|
||||
rgb(0, 0, 0.7)
|
||||
);
|
||||
|
||||
|
||||
this.desenharTexto(pagina, titulo, margem, yPos, fonte, 18, rgb(0, 0, 0.7));
|
||||
|
||||
// Linha separadora
|
||||
pagina.drawLine({
|
||||
start: { x: margem, y: yPos - 10 },
|
||||
@@ -118,10 +112,10 @@ export class PdfService {
|
||||
thickness: 1,
|
||||
color: rgb(0, 0, 0.5),
|
||||
});
|
||||
|
||||
|
||||
return yPos - 30; // Retorna a posição Y após o cabeçalho
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cria um rodapé para o documento
|
||||
* @param pagina - Página do PDF
|
||||
@@ -131,7 +125,7 @@ export class PdfService {
|
||||
*/
|
||||
criarRodape(pagina: any, texto: string, fonte: any, margem: number) {
|
||||
const yPos = margem / 2;
|
||||
|
||||
|
||||
this.desenharTexto(
|
||||
pagina,
|
||||
texto,
|
||||
@@ -139,7 +133,7 @@ export class PdfService {
|
||||
yPos,
|
||||
fonte,
|
||||
8,
|
||||
rgb(0.5, 0.5, 0.5)
|
||||
rgb(0.5, 0.5, 0.5),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -147,7 +141,7 @@ export class PdfService {
|
||||
try {
|
||||
// Buscar configuração ou usar valores padrão
|
||||
const config = await prisma.configuracaoDocumento.findFirst({
|
||||
orderBy: { criadoEm: 'desc' },
|
||||
orderBy: { criadoEm: "desc" },
|
||||
select: {
|
||||
id: true,
|
||||
headerImageUrl: true,
|
||||
@@ -158,44 +152,48 @@ export class PdfService {
|
||||
timbradoMarginBottom: true,
|
||||
timbradoMarginLeft: true,
|
||||
timbradoMarginRight: true,
|
||||
criadoEm: true
|
||||
}
|
||||
criadoEm: true,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('Configuração encontrada:', config); // Debug
|
||||
console.log("Configuração encontrada:", config); // Debug
|
||||
|
||||
// Retornar configuração padrão se não existir
|
||||
return {
|
||||
cabecalhoHtml: config?.cabecalhoHtml || `
|
||||
cabecalhoHtml:
|
||||
config?.cabecalhoHtml ||
|
||||
`
|
||||
<div style="text-align: center; padding: 20px;">
|
||||
<h2 style="margin:0;font-size:14pt">{{escritorio.nome}}</h2>
|
||||
</div>
|
||||
`,
|
||||
rodapeHtml: config?.rodapeHtml || `
|
||||
rodapeHtml:
|
||||
config?.rodapeHtml ||
|
||||
`
|
||||
<div style="text-align: center; font-size: 10pt;">
|
||||
{{escritorio.endereco}}<br>
|
||||
Tel: {{escritorio.telefone}} | Email: {{escritorio.email}}
|
||||
</div>
|
||||
`,
|
||||
logoUrl: config?.logoUrl || '',
|
||||
timbradoUrl: config?.timbradoUrl || '',
|
||||
logoUrl: config?.logoUrl || "",
|
||||
timbradoUrl: config?.timbradoUrl || "",
|
||||
timbradoMarginTop: config?.timbradoMarginTop || 100,
|
||||
timbradoMarginBottom: config?.timbradoMarginBottom || 50,
|
||||
timbradoMarginLeft: config?.timbradoMarginLeft || 50,
|
||||
timbradoMarginRight: config?.timbradoMarginRight || 50
|
||||
timbradoMarginRight: config?.timbradoMarginRight || 50,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar configuração:', error);
|
||||
throw new Error('Falha ao buscar configuração');
|
||||
console.error("Erro ao buscar configuração:", error);
|
||||
throw new Error("Falha ao buscar configuração");
|
||||
}
|
||||
}
|
||||
|
||||
private gerarHTML(content: string, config: any) {
|
||||
const timbradoUrl = config.timbradoUrl
|
||||
? `${process.env.BACKEND_URL || 'http://localhost:5000'}${config.timbradoUrl}`
|
||||
: '';
|
||||
const timbradoUrl = config.timbradoUrl
|
||||
? `${process.env.BACKEND_URL || "http://localhost:5000"}${config.timbradoUrl}`
|
||||
: "";
|
||||
|
||||
console.log('Gerando PDF com timbrado:', timbradoUrl);
|
||||
console.log("Gerando PDF com timbrado:", timbradoUrl);
|
||||
|
||||
return `
|
||||
<!DOCTYPE html>
|
||||
@@ -224,7 +222,7 @@ export class PdfService {
|
||||
width: 21cm;
|
||||
min-height: 29.7cm;
|
||||
padding: ${config.timbradoMarginTop || 100}px ${config.timbradoMarginRight || 50}px ${config.timbradoMarginBottom || 50}px ${config.timbradoMarginLeft || 50}px;
|
||||
background: ${timbradoUrl ? `url('${timbradoUrl}')` : 'none'};
|
||||
background: ${timbradoUrl ? `url('${timbradoUrl}')` : "none"};
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
@@ -252,19 +250,21 @@ export class PdfService {
|
||||
try {
|
||||
// 1. Buscar configuração do template
|
||||
const config = await prisma.configuracaoDocumento.findFirst({
|
||||
orderBy: { criadoEm: 'desc' }
|
||||
orderBy: { criadoEm: "desc" },
|
||||
});
|
||||
|
||||
// 2. Gerar PDF do conteúdo
|
||||
const browser = await puppeteer.launch({
|
||||
headless: 'new',
|
||||
args: ['--no-sandbox']
|
||||
headless: "new",
|
||||
args: ["--no-sandbox"],
|
||||
});
|
||||
|
||||
const page = await browser.newPage();
|
||||
|
||||
|
||||
// Usar template padrão ou fallback para o template básico
|
||||
const template = config?.templatePadrao || `
|
||||
const template =
|
||||
config?.templatePadrao ||
|
||||
`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
@@ -298,32 +298,40 @@ export class PdfService {
|
||||
`;
|
||||
|
||||
// Processar o template substituindo variáveis
|
||||
const templateProcessado = template.replace(/\{\{(.+?)\}\}/g, (match, key) => {
|
||||
return escritorio[key.trim()] || match;
|
||||
});
|
||||
const templateProcessado = template.replace(
|
||||
/\{\{(.+?)\}\}/g,
|
||||
(match, key) => {
|
||||
return escritorio[key.trim()] || match;
|
||||
},
|
||||
);
|
||||
|
||||
await page.setContent(templateProcessado);
|
||||
|
||||
const margens = config?.margensTemplate || {
|
||||
top: '35mm',
|
||||
bottom: '29mm',
|
||||
left: '25mm',
|
||||
right: '25mm'
|
||||
top: "35mm",
|
||||
bottom: "29mm",
|
||||
left: "25mm",
|
||||
right: "25mm",
|
||||
};
|
||||
|
||||
const contentPdfBuffer = await page.pdf({
|
||||
format: 'A4',
|
||||
format: "A4",
|
||||
margin: margens,
|
||||
displayHeaderFooter: false,
|
||||
printBackground: true
|
||||
printBackground: true,
|
||||
});
|
||||
|
||||
await browser.close();
|
||||
|
||||
// 2. Carregar os PDFs
|
||||
const templatePath = path.join(process.cwd(), 'public', 'uploads', 'Timbrado_2025.pdf');
|
||||
const templatePath = path.join(
|
||||
process.cwd(),
|
||||
"public",
|
||||
"uploads",
|
||||
"Timbrado_2025.pdf",
|
||||
);
|
||||
const templateBytes = await fs.readFile(templatePath);
|
||||
|
||||
|
||||
const templateDoc = await PDFDocument.load(templateBytes);
|
||||
const contentDoc = await PDFDocument.load(contentPdfBuffer);
|
||||
|
||||
@@ -339,10 +347,10 @@ export class PdfService {
|
||||
// Primeiro: adicionar template
|
||||
const page = await pdfDoc.embedPage(templatePage);
|
||||
const contentPage = await pdfDoc.embedPage(contentPages[i]);
|
||||
|
||||
|
||||
// Segundo: criar nova página
|
||||
const newPage = pdfDoc.addPage([page.width, page.height]);
|
||||
|
||||
|
||||
// Terceiro: desenhar template e conteúdo
|
||||
newPage.drawPage(page);
|
||||
newPage.drawPage(contentPage);
|
||||
@@ -351,10 +359,9 @@ export class PdfService {
|
||||
// 6. Gerar PDF final
|
||||
const pdfBytes = await pdfDoc.save();
|
||||
return Buffer.from(pdfBytes);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Erro ao gerar PDF:', error);
|
||||
throw new Error('Falha ao gerar PDF');
|
||||
console.error("Erro ao gerar PDF:", error);
|
||||
throw new Error("Falha ao gerar PDF");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { PrismaClient, Decimal } from '@prisma/client';
|
||||
import { ApiError } from '../utils/ApiError';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { PrismaClient, Decimal } from "@prisma/client";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { ApiError } from "../utils/ApiError";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
@@ -23,17 +24,17 @@ export class PensaoAlimenticiaService {
|
||||
data: {
|
||||
id: uuidv4(),
|
||||
...dados,
|
||||
status: 'ATIVO',
|
||||
status: "ATIVO",
|
||||
valorCalculado: new Decimal(0), // Será atualizado após o cálculo
|
||||
valorMulta: new Decimal(0),
|
||||
valorTotal: new Decimal(0)
|
||||
}
|
||||
valorTotal: new Decimal(0),
|
||||
},
|
||||
});
|
||||
|
||||
return calculo;
|
||||
} catch (error) {
|
||||
console.error('Erro ao criar cálculo de pensão:', error);
|
||||
throw new ApiError('Falha ao criar cálculo de pensão alimentícia', 500);
|
||||
console.error("Erro ao criar cálculo de pensão:", error);
|
||||
throw new ApiError("Falha ao criar cálculo de pensão alimentícia", 500);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,14 +48,14 @@ export class PensaoAlimenticiaService {
|
||||
select: {
|
||||
id: true,
|
||||
nome: true,
|
||||
email: true
|
||||
}
|
||||
}
|
||||
}
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!calculo) {
|
||||
throw new Error('Cálculo não encontrado');
|
||||
throw new Error("Cálculo não encontrado");
|
||||
}
|
||||
|
||||
return calculo;
|
||||
@@ -76,19 +77,19 @@ export class PensaoAlimenticiaService {
|
||||
select: {
|
||||
id: true,
|
||||
nome: true,
|
||||
email: true
|
||||
}
|
||||
}
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
criadoEm: 'desc'
|
||||
}
|
||||
criadoEm: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
return calculos;
|
||||
} catch (error) {
|
||||
console.error('Erro ao listar cálculos:', error);
|
||||
throw new ApiError('Falha ao listar cálculos de pensão alimentícia', 500);
|
||||
console.error("Erro ao listar cálculos:", error);
|
||||
throw new ApiError("Falha ao listar cálculos de pensão alimentícia", 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
+229
-147
@@ -1,23 +1,29 @@
|
||||
import { prisma } from '../prismaClient';
|
||||
import { createHash } from 'crypto';
|
||||
import { PDFDocument, StandardFonts } from 'pdf-lib';
|
||||
import sharp from 'sharp';
|
||||
import * as fs from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
import { execSync } from 'child_process';
|
||||
import { BlockchainService } from './BlockchainService';
|
||||
import { ipDetectionMiddleware } from '../middleware/ipDetection';
|
||||
import { FileValidator } from './FileValidator';
|
||||
import { ReportGenerator } from './ReportGenerator'; // Adicionar import
|
||||
import { Request, Response } from 'express'; // Importar Request e Response
|
||||
import { execSync } from "child_process";
|
||||
import { createHash } from "crypto";
|
||||
import * as fs from "fs/promises";
|
||||
import * as path from "path";
|
||||
|
||||
import { Request, Response } from "express"; // Importar Request e Response
|
||||
import { PDFDocument, StandardFonts } from "pdf-lib";
|
||||
import sharp from "sharp";
|
||||
|
||||
import { ipDetectionMiddleware } from "../middleware/ipDetection";
|
||||
import { prisma } from "../prismaClient";
|
||||
|
||||
import { BlockchainService } from "./BlockchainService";
|
||||
import { FileValidator } from "./FileValidator";
|
||||
import { ReportGenerator } from "./ReportGenerator"; // Adicionar import
|
||||
|
||||
// Tentativa de importar o MetadataCollector com fallback
|
||||
let MetadataCollectorClass;
|
||||
try {
|
||||
MetadataCollectorClass = require('./MetadataCollector').MetadataCollector;
|
||||
MetadataCollectorClass = require("./MetadataCollector").MetadataCollector;
|
||||
} catch (error) {
|
||||
console.warn('Aviso: MetadataCollector não está disponível, usando versão simplificada');
|
||||
MetadataCollectorClass = require('./FallbackMetadataCollector').FallbackMetadataCollector;
|
||||
console.warn(
|
||||
"Aviso: MetadataCollector não está disponível, usando versão simplificada",
|
||||
);
|
||||
MetadataCollectorClass =
|
||||
require("./FallbackMetadataCollector").FallbackMetadataCollector;
|
||||
}
|
||||
|
||||
export class ProvaService {
|
||||
@@ -29,18 +35,24 @@ export class ProvaService {
|
||||
constructor() {
|
||||
this.fileValidator = new FileValidator();
|
||||
this.reportGenerator = new ReportGenerator();
|
||||
this.storageDir = path.join(process.cwd(), 'storage', 'provas');
|
||||
|
||||
this.storageDir = path.join(process.cwd(), "storage", "provas");
|
||||
|
||||
// Inicializar o coletor de metadados com tratamento de erro
|
||||
try {
|
||||
this.metadataCollector = new MetadataCollectorClass();
|
||||
} catch (error) {
|
||||
console.error('Erro ao inicializar MetadataCollector, usando objeto vazio:', error);
|
||||
console.error(
|
||||
"Erro ao inicializar MetadataCollector, usando objeto vazio:",
|
||||
error,
|
||||
);
|
||||
this.metadataCollector = {
|
||||
collectMetadata: async (data: any) => ({ ...data, error: 'Falha na coleta de metadados' })
|
||||
collectMetadata: async (data: any) => ({
|
||||
...data,
|
||||
error: "Falha na coleta de metadados",
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
this.ensureStorageDir();
|
||||
}
|
||||
|
||||
@@ -48,19 +60,23 @@ export class ProvaService {
|
||||
try {
|
||||
await fs.mkdir(this.storageDir, { recursive: true });
|
||||
} catch (error) {
|
||||
console.error('Erro ao criar diretório de armazenamento:', error);
|
||||
console.error("Erro ao criar diretório de armazenamento:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async processFile(file: Express.Multer.File, metadata: any) {
|
||||
try {
|
||||
// 1. Validar arquivo e metadados
|
||||
const validationResult = await this.fileValidator.validateFile(file.buffer, file.mimetype);
|
||||
const validationResult = await this.fileValidator.validateFile(
|
||||
file.buffer,
|
||||
file.mimetype,
|
||||
);
|
||||
if (!validationResult.valid) {
|
||||
throw new Error(validationResult.error);
|
||||
}
|
||||
|
||||
const metadataValidation = await this.fileValidator.validateMetadata(metadata);
|
||||
const metadataValidation =
|
||||
await this.fileValidator.validateMetadata(metadata);
|
||||
if (!metadataValidation.valid) {
|
||||
throw new Error(metadataValidation.error);
|
||||
}
|
||||
@@ -70,27 +86,33 @@ export class ProvaService {
|
||||
try {
|
||||
// Garantir que estamos usando o IP real do cliente
|
||||
const clientIP = this.extractRealClientIP(metadata.ipAddress, metadata);
|
||||
|
||||
|
||||
enhancedMetadata = await this.metadataCollector.collectMetadata({
|
||||
userAgent: metadata.userAgent,
|
||||
ipAddress: clientIP,
|
||||
timestamp: new Date(metadata.timestamp),
|
||||
fileName: file.originalname,
|
||||
fileModified: metadata.modification_times ? new Date(parseInt(metadata.modification_times)) : undefined,
|
||||
fileModified: metadata.modification_times
|
||||
? new Date(parseInt(metadata.modification_times))
|
||||
: undefined,
|
||||
fileSize: file.size,
|
||||
viewportWidth: metadata.viewportWidth,
|
||||
viewportHeight: metadata.viewportHeight,
|
||||
language: metadata.language,
|
||||
referrer: metadata.referrer,
|
||||
latitude: metadata.latitude ? parseFloat(metadata.latitude) : undefined,
|
||||
longitude: metadata.longitude ? parseFloat(metadata.longitude) : undefined
|
||||
latitude: metadata.latitude
|
||||
? parseFloat(metadata.latitude)
|
||||
: undefined,
|
||||
longitude: metadata.longitude
|
||||
? parseFloat(metadata.longitude)
|
||||
: undefined,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao coletar metadados avançados:', error);
|
||||
enhancedMetadata = {
|
||||
...metadata,
|
||||
error: 'Falha na coleta de metadados avançados',
|
||||
fallback: true
|
||||
console.error("Erro ao coletar metadados avançados:", error);
|
||||
enhancedMetadata = {
|
||||
...metadata,
|
||||
error: "Falha na coleta de metadados avançados",
|
||||
fallback: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -98,13 +120,16 @@ export class ProvaService {
|
||||
const checksums = this.generateChecksums(file.buffer);
|
||||
|
||||
// 4. Registrar timestamp
|
||||
const timestamp = await this.createLocalTimestamp(file.buffer, checksums.sha256);
|
||||
const timestamp = await this.createLocalTimestamp(
|
||||
file.buffer,
|
||||
checksums.sha256,
|
||||
);
|
||||
|
||||
// 5. Processar arquivo
|
||||
const processedFile = await this.processFileByType(file);
|
||||
|
||||
// 6. Gerar URL de verificação
|
||||
const verificationUrl = `${process.env.PUBLIC_URL || 'http://localhost:5000'}/verificar/${checksums.sha256}`;
|
||||
const verificationUrl = `${process.env.PUBLIC_URL || "http://localhost:5000"}/verificar/${checksums.sha256}`;
|
||||
|
||||
// 7. Gerar relatório
|
||||
const reportResult = await this.reportGenerator.generateReport({
|
||||
@@ -117,14 +142,17 @@ export class ProvaService {
|
||||
ipAddress: enhancedMetadata.ipAddress,
|
||||
headers: metadata.headers,
|
||||
connectionInfo: metadata.connectionInfo,
|
||||
clientPort: metadata.clientPort
|
||||
clientPort: metadata.clientPort,
|
||||
},
|
||||
processedFile,
|
||||
verificationUrl
|
||||
verificationUrl,
|
||||
});
|
||||
|
||||
// 8. Salvar relatório
|
||||
const reportPath = path.join(this.storageDir, `${checksums.sha256}_report.pdf`);
|
||||
const reportPath = path.join(
|
||||
this.storageDir,
|
||||
`${checksums.sha256}_report.pdf`,
|
||||
);
|
||||
await fs.writeFile(reportPath, reportResult.buffer);
|
||||
|
||||
// 9. Salvar no banco de dados
|
||||
@@ -137,13 +165,16 @@ export class ProvaService {
|
||||
checksumMd5: checksums.md5,
|
||||
metadata: enhancedMetadata,
|
||||
reportPath: reportPath,
|
||||
status: 'CONCLUIDO',
|
||||
timestamp: JSON.stringify(timestamp)
|
||||
}
|
||||
status: "CONCLUIDO",
|
||||
timestamp: JSON.stringify(timestamp),
|
||||
},
|
||||
});
|
||||
|
||||
// 10. Salvar o arquivo original
|
||||
const originalFilePath = path.join(this.storageDir, `${checksums.sha256}_original`);
|
||||
const originalFilePath = path.join(
|
||||
this.storageDir,
|
||||
`${checksums.sha256}_original`,
|
||||
);
|
||||
await fs.writeFile(originalFilePath, file.buffer);
|
||||
|
||||
// 11. Registrar log forense
|
||||
@@ -151,7 +182,7 @@ export class ProvaService {
|
||||
|
||||
return prova;
|
||||
} catch (error) {
|
||||
console.error('Erro ao processar arquivo:', error);
|
||||
console.error("Erro ao processar arquivo:", error);
|
||||
throw error;
|
||||
} finally {
|
||||
// Limpar recursos
|
||||
@@ -168,162 +199,193 @@ export class ProvaService {
|
||||
private extractRealClientIP(ipAddress: string, metadata?: any): string {
|
||||
// Verificar se temos dados válidos para trabalhar
|
||||
if (!metadata) {
|
||||
return ipAddress || '0.0.0.0';
|
||||
return ipAddress || "0.0.0.0";
|
||||
}
|
||||
|
||||
console.log('Tentando extrair IP real. IP original:', ipAddress);
|
||||
|
||||
console.log("Tentando extrair IP real. IP original:", ipAddress);
|
||||
|
||||
// Lista de IPs locais/internos que devem ser evitados
|
||||
const localIPs = ['::1', '127.0.0.1', 'localhost', '::ffff:127.0.0.1', '::ffff:10.15.0.1'];
|
||||
|
||||
const localIPs = [
|
||||
"::1",
|
||||
"127.0.0.1",
|
||||
"localhost",
|
||||
"::ffff:127.0.0.1",
|
||||
"::ffff:10.15.0.1",
|
||||
];
|
||||
|
||||
// Verificar PRIMEIRO os headers HTTP que são a fonte mais confiável em produção
|
||||
if (metadata.headers) {
|
||||
// Headers comuns que podem conter o IP real
|
||||
if (metadata.headers['x-forwarded-for']) {
|
||||
const forwardedIps = metadata.headers['x-forwarded-for'].split(',').map(ip => ip.trim());
|
||||
if (metadata.headers["x-forwarded-for"]) {
|
||||
const forwardedIps = metadata.headers["x-forwarded-for"]
|
||||
.split(",")
|
||||
.map((ip) => ip.trim());
|
||||
if (forwardedIps.length > 0 && !localIPs.includes(forwardedIps[0])) {
|
||||
console.log('Usando IP do x-forwarded-for:', forwardedIps[0]);
|
||||
console.log("Usando IP do x-forwarded-for:", forwardedIps[0]);
|
||||
return forwardedIps[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Outros headers comuns
|
||||
const otherHeaders = ['x-real-ip', 'cf-connecting-ip', 'true-client-ip'];
|
||||
const otherHeaders = ["x-real-ip", "cf-connecting-ip", "true-client-ip"];
|
||||
for (const header of otherHeaders) {
|
||||
if (metadata.headers[header] && !localIPs.includes(metadata.headers[header])) {
|
||||
console.log(`Usando IP do header ${header}:`, metadata.headers[header]);
|
||||
if (
|
||||
metadata.headers[header] &&
|
||||
!localIPs.includes(metadata.headers[header])
|
||||
) {
|
||||
console.log(
|
||||
`Usando IP do header ${header}:`,
|
||||
metadata.headers[header],
|
||||
);
|
||||
return metadata.headers[header];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Verificar nas informações de conexão
|
||||
if (metadata.connectionInfo && metadata.connectionInfo.forwardedIp) {
|
||||
if (!localIPs.includes(metadata.connectionInfo.forwardedIp)) {
|
||||
console.log('Usando IP encaminhado da conexão:', metadata.connectionInfo.forwardedIp);
|
||||
console.log(
|
||||
"Usando IP encaminhado da conexão:",
|
||||
metadata.connectionInfo.forwardedIp,
|
||||
);
|
||||
return metadata.connectionInfo.forwardedIp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Se o IP não é local, usá-lo
|
||||
if (ipAddress && !localIPs.includes(ipAddress)) {
|
||||
console.log('Usando IP original:', ipAddress);
|
||||
console.log("Usando IP original:", ipAddress);
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
|
||||
// Se chegamos aqui, temos apenas IPs locais. Usar o IP com a porta como identificador
|
||||
const port = metadata.clientPort ||
|
||||
metadata.connectionInfo?.socketPort ||
|
||||
metadata.porta;
|
||||
|
||||
const port =
|
||||
metadata.clientPort ||
|
||||
metadata.connectionInfo?.socketPort ||
|
||||
metadata.porta;
|
||||
|
||||
if (port) {
|
||||
const ipWithPort = `${ipAddress || '127.0.0.1'}:${port}`;
|
||||
console.log('Usando identificador IP:porta:', ipWithPort);
|
||||
const ipWithPort = `${ipAddress || "127.0.0.1"}:${port}`;
|
||||
console.log("Usando identificador IP:porta:", ipWithPort);
|
||||
return ipWithPort;
|
||||
}
|
||||
|
||||
|
||||
// NUNCA retornar 0.0.0.0 - se nada funcionar, usar 127.0.0.1 com timestamp
|
||||
const fallbackIp = `127.0.0.1:${Date.now() % 10000}`;
|
||||
console.log('Usando fallback:', fallbackIp);
|
||||
console.log("Usando fallback:", fallbackIp);
|
||||
return fallbackIp;
|
||||
}
|
||||
|
||||
private generateChecksums(buffer: Buffer) {
|
||||
return {
|
||||
sha256: createHash('sha256').update(buffer).digest('hex'),
|
||||
sha512: createHash('sha512').update(buffer).digest('hex'),
|
||||
md5: createHash('md5').update(buffer).digest('hex')
|
||||
sha256: createHash("sha256").update(buffer).digest("hex"),
|
||||
sha512: createHash("sha512").update(buffer).digest("hex"),
|
||||
md5: createHash("md5").update(buffer).digest("hex"),
|
||||
};
|
||||
}
|
||||
|
||||
private async createLocalTimestamp(buffer: Buffer, hash: string) {
|
||||
try {
|
||||
console.log('Criando timestamp local...');
|
||||
console.log("Criando timestamp local...");
|
||||
|
||||
// 1. Tempo atual em formato RFC3339
|
||||
const now = new Date();
|
||||
const isoString = now.toISOString();
|
||||
|
||||
|
||||
// 2. Obter tempo NTP do servidor oficial para maior confiabilidade
|
||||
let ntpTime = '';
|
||||
let ntpTime = "";
|
||||
try {
|
||||
// Detectar plataforma e usar o comando apropriado
|
||||
if (process.platform === 'win32') {
|
||||
if (process.platform === "win32") {
|
||||
// Windows
|
||||
ntpTime = execSync('w32tm /stripchart /computer:time.nist.gov /samples:1 /dataonly').toString();
|
||||
ntpTime = execSync(
|
||||
"w32tm /stripchart /computer:time.nist.gov /samples:1 /dataonly",
|
||||
).toString();
|
||||
} else {
|
||||
// Linux/Unix/MacOS - usar implementação alternativa
|
||||
// Tentar usar o comando 'date' para obter a hora do sistema
|
||||
ntpTime = execSync('date -u +"%Y-%m-%dT%H:%M:%S.%3NZ"').toString().trim();
|
||||
ntpTime = execSync('date -u +"%Y-%m-%dT%H:%M:%S.%3NZ"')
|
||||
.toString()
|
||||
.trim();
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Não foi possível obter tempo NTP, usando tempo local:', e);
|
||||
console.warn(
|
||||
"Não foi possível obter tempo NTP, usando tempo local:",
|
||||
e,
|
||||
);
|
||||
}
|
||||
|
||||
// 3. Criar arquivo de timestamp com metadados e assinatura
|
||||
const timestampData = {
|
||||
hash,
|
||||
timestamp: isoString,
|
||||
ntpReference: ntpTime || 'Indisponível',
|
||||
unixTimestamp: Math.floor(now.getTime() / 1000)
|
||||
ntpReference: ntpTime || "Indisponível",
|
||||
unixTimestamp: Math.floor(now.getTime() / 1000),
|
||||
};
|
||||
|
||||
// 4. Salvar o arquivo de timestamp
|
||||
const timestampPath = path.join(this.storageDir, `${hash}.timestamp.json`);
|
||||
const timestampPath = path.join(
|
||||
this.storageDir,
|
||||
`${hash}.timestamp.json`,
|
||||
);
|
||||
await fs.writeFile(timestampPath, JSON.stringify(timestampData, null, 2));
|
||||
|
||||
return {
|
||||
...timestampData,
|
||||
path: timestampPath
|
||||
path: timestampPath,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Erro ao criar timestamp local:', error);
|
||||
console.error("Erro ao criar timestamp local:", error);
|
||||
// Em caso de erro, retornar um timestamp básico sem interromper o processo
|
||||
const basicTimestamp = {
|
||||
hash,
|
||||
timestamp: new Date().toISOString(),
|
||||
ntpReference: 'Indisponível (erro)',
|
||||
ntpReference: "Indisponível (erro)",
|
||||
unixTimestamp: Math.floor(Date.now() / 1000),
|
||||
error: error.message
|
||||
error: error.message,
|
||||
};
|
||||
|
||||
|
||||
return basicTimestamp;
|
||||
}
|
||||
}
|
||||
|
||||
private async processFileByType(file: Express.Multer.File) {
|
||||
if (file.mimetype.startsWith('image/')) {
|
||||
if (file.mimetype.startsWith("image/")) {
|
||||
try {
|
||||
// Para JPEGs, converter sempre para PNG para evitar problemas
|
||||
if (file.mimetype === 'image/jpeg') {
|
||||
if (file.mimetype === "image/jpeg") {
|
||||
return await sharp(file.buffer)
|
||||
.png() // Converter para PNG é mais seguro
|
||||
.resize(1200, 900, {
|
||||
fit: 'inside',
|
||||
withoutEnlargement: true
|
||||
fit: "inside",
|
||||
withoutEnlargement: true,
|
||||
})
|
||||
.toBuffer();
|
||||
}
|
||||
|
||||
|
||||
// Para outras imagens, manter o formato original
|
||||
return await sharp(file.buffer)
|
||||
.resize(1200, 900, {
|
||||
fit: 'inside',
|
||||
withoutEnlargement: true
|
||||
fit: "inside",
|
||||
withoutEnlargement: true,
|
||||
})
|
||||
.toBuffer();
|
||||
} catch (error) {
|
||||
console.error('Erro ao processar imagem com sharp:', error);
|
||||
|
||||
console.error("Erro ao processar imagem com sharp:", error);
|
||||
|
||||
// Se sharp falhar, tentar abordagem mais simples
|
||||
try {
|
||||
console.log('Tentando método alternativo de processamento de imagem...');
|
||||
console.log(
|
||||
"Tentando método alternativo de processamento de imagem...",
|
||||
);
|
||||
// Converter diretamente para PNG sem redimensionamento
|
||||
return await sharp(file.buffer)
|
||||
.png()
|
||||
.toBuffer();
|
||||
return await sharp(file.buffer).png().toBuffer();
|
||||
} catch (fallbackError) {
|
||||
console.error('Falha total no processamento da imagem:', fallbackError);
|
||||
console.error(
|
||||
"Falha total no processamento da imagem:",
|
||||
fallbackError,
|
||||
);
|
||||
// Em último caso, retornar o buffer original
|
||||
return file.buffer;
|
||||
}
|
||||
@@ -336,11 +398,14 @@ export class ProvaService {
|
||||
try {
|
||||
// 1. Buscar prova no banco de dados
|
||||
const prova = await prisma.prova.findFirst({
|
||||
where: { checksumSha256: hash }
|
||||
where: { checksumSha256: hash },
|
||||
});
|
||||
|
||||
if (!prova) {
|
||||
return { verified: false, message: 'Arquivo não encontrado na base de dados' };
|
||||
return {
|
||||
verified: false,
|
||||
message: "Arquivo não encontrado na base de dados",
|
||||
};
|
||||
}
|
||||
|
||||
// 2. Verificar se o arquivo original existe
|
||||
@@ -348,27 +413,32 @@ export class ProvaService {
|
||||
try {
|
||||
await fs.access(originalFilePath);
|
||||
} catch (error) {
|
||||
return {
|
||||
verified: false,
|
||||
message: 'Arquivo original não encontrado no servidor',
|
||||
prova
|
||||
return {
|
||||
verified: false,
|
||||
message: "Arquivo original não encontrado no servidor",
|
||||
prova,
|
||||
};
|
||||
}
|
||||
|
||||
// 3. Verificar se o timestamp existe
|
||||
const timestampPath = path.join(this.storageDir, `${hash}.timestamp.json`);
|
||||
const timestampPath = path.join(
|
||||
this.storageDir,
|
||||
`${hash}.timestamp.json`,
|
||||
);
|
||||
try {
|
||||
await fs.access(timestampPath);
|
||||
} catch (error) {
|
||||
return {
|
||||
verified: false,
|
||||
message: 'Timestamp não encontrado',
|
||||
prova
|
||||
return {
|
||||
verified: false,
|
||||
message: "Timestamp não encontrado",
|
||||
prova,
|
||||
};
|
||||
}
|
||||
|
||||
// 4. Ler o timestamp
|
||||
const timestampData = JSON.parse(await fs.readFile(timestampPath, 'utf-8'));
|
||||
const timestampData = JSON.parse(
|
||||
await fs.readFile(timestampPath, "utf-8"),
|
||||
);
|
||||
|
||||
// 5. Se fornecido, incluir informações de IP no registro
|
||||
if (ipInfo) {
|
||||
@@ -376,19 +446,22 @@ export class ProvaService {
|
||||
try {
|
||||
await this.logVerification(hash, ipInfo);
|
||||
} catch (logError) {
|
||||
console.error('Erro ao registrar verificação:', logError);
|
||||
console.error("Erro ao registrar verificação:", logError);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
verified: true,
|
||||
message: 'Arquivo verificado com sucesso',
|
||||
message: "Arquivo verificado com sucesso",
|
||||
prova,
|
||||
timestamp: timestampData
|
||||
timestamp: timestampData,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Erro ao verificar arquivo:', error);
|
||||
return { verified: false, message: `Erro ao verificar: ${error.message}` };
|
||||
console.error("Erro ao verificar arquivo:", error);
|
||||
return {
|
||||
verified: false,
|
||||
message: `Erro ao verificar: ${error.message}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,17 +471,17 @@ export class ProvaService {
|
||||
const verificationLog = {
|
||||
timestamp: new Date().toISOString(),
|
||||
hash,
|
||||
ip: ipInfo.ip || '127.0.0.1',
|
||||
porta: ipInfo.porta || '0',
|
||||
userAgent: ipInfo.userAgent || 'Desconhecido'
|
||||
ip: ipInfo.ip || "127.0.0.1",
|
||||
porta: ipInfo.porta || "0",
|
||||
userAgent: ipInfo.userAgent || "Desconhecido",
|
||||
};
|
||||
|
||||
|
||||
const logPath = path.join(this.storageDir, `${hash}_verifications.json`);
|
||||
|
||||
|
||||
// Verificar se o arquivo já existe
|
||||
let existingLogs = [];
|
||||
try {
|
||||
const existingData = await fs.readFile(logPath, 'utf-8');
|
||||
const existingData = await fs.readFile(logPath, "utf-8");
|
||||
existingLogs = JSON.parse(existingData);
|
||||
if (!Array.isArray(existingLogs)) {
|
||||
existingLogs = [];
|
||||
@@ -416,21 +489,25 @@ export class ProvaService {
|
||||
} catch (e) {
|
||||
// Arquivo não existe, criar novo
|
||||
}
|
||||
|
||||
|
||||
// Adicionar nova verificação
|
||||
existingLogs.push(verificationLog);
|
||||
|
||||
|
||||
// Salvar arquivo
|
||||
await fs.writeFile(logPath, JSON.stringify(existingLogs, null, 2));
|
||||
|
||||
|
||||
console.log(`Verificação registrada para ${hash} de IP ${ipInfo.ip}`);
|
||||
} catch (error) {
|
||||
console.error('Erro ao registrar verificação:', error);
|
||||
console.error("Erro ao registrar verificação:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Novo método para registrar informações forenses
|
||||
private async logForensicInfo(file: Express.Multer.File, metadata: any, checksums: any) {
|
||||
private async logForensicInfo(
|
||||
file: Express.Multer.File,
|
||||
metadata: any,
|
||||
checksums: any,
|
||||
) {
|
||||
try {
|
||||
const logEntry = {
|
||||
timestamp: new Date().toISOString(),
|
||||
@@ -440,25 +517,30 @@ export class ProvaService {
|
||||
checksums,
|
||||
metadata,
|
||||
processingInfo: {
|
||||
processingNode: require('os').hostname(),
|
||||
processingUser: process.env.USER || process.env.USERNAME || 'unknown',
|
||||
processingNode: require("os").hostname(),
|
||||
processingUser: process.env.USER || process.env.USERNAME || "unknown",
|
||||
processingTimestamp: new Date().toISOString(),
|
||||
systemInfo: {
|
||||
platform: process.platform,
|
||||
release: require('os').release(),
|
||||
cpus: require('os').cpus().length,
|
||||
memory: Math.round(require('os').totalmem() / (1024 * 1024 * 1024)) + 'GB',
|
||||
uptime: Math.round(require('os').uptime() / 3600) + 'h',
|
||||
}
|
||||
}
|
||||
release: require("os").release(),
|
||||
cpus: require("os").cpus().length,
|
||||
memory:
|
||||
Math.round(require("os").totalmem() / (1024 * 1024 * 1024)) +
|
||||
"GB",
|
||||
uptime: Math.round(require("os").uptime() / 3600) + "h",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const logPath = path.join(this.storageDir, `${checksums.sha256}_forensic.json`);
|
||||
const logPath = path.join(
|
||||
this.storageDir,
|
||||
`${checksums.sha256}_forensic.json`,
|
||||
);
|
||||
await fs.writeFile(logPath, JSON.stringify(logEntry, null, 2));
|
||||
|
||||
console.log('Log forense registrado em:', logPath);
|
||||
|
||||
console.log("Log forense registrado em:", logPath);
|
||||
} catch (error) {
|
||||
console.error('Erro ao registrar log forense:', error);
|
||||
console.error("Erro ao registrar log forense:", error);
|
||||
// Não interromper o processamento por falha no log
|
||||
}
|
||||
}
|
||||
@@ -469,7 +551,7 @@ export const uploadProva = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const file = req.file;
|
||||
if (!file) {
|
||||
return res.status(400).json({ error: 'Nenhum arquivo enviado' });
|
||||
return res.status(400).json({ error: "Nenhum arquivo enviado" });
|
||||
}
|
||||
|
||||
// Coletar todas as informações possíveis sobre o cliente
|
||||
@@ -477,21 +559,21 @@ export const uploadProva = async (req: Request, res: Response) => {
|
||||
ipAddress: req.ip, // Express geralmente fornece isso
|
||||
remoteAddress: req.socket?.remoteAddress, // Endereço IP direto do socket
|
||||
porta: req.socket?.remotePort?.toString(), // Porta de conexão do cliente
|
||||
userAgent: req.headers['user-agent'],
|
||||
userAgent: req.headers["user-agent"],
|
||||
headers: req.headers, // Todos os headers HTTP
|
||||
timestamp: new Date().toISOString(),
|
||||
// Outros metadados do formulário
|
||||
...req.body
|
||||
...req.body,
|
||||
};
|
||||
|
||||
console.log('Metadados de conexão:', {
|
||||
console.log("Metadados de conexão:", {
|
||||
ip: req.ip,
|
||||
remoteAddress: req.socket?.remoteAddress,
|
||||
remotePort: req.socket?.remotePort,
|
||||
headers: {
|
||||
'x-forwarded-for': req.headers['x-forwarded-for'],
|
||||
'x-real-ip': req.headers['x-real-ip']
|
||||
}
|
||||
"x-forwarded-for": req.headers["x-forwarded-for"],
|
||||
"x-real-ip": req.headers["x-real-ip"],
|
||||
},
|
||||
});
|
||||
|
||||
const provaService = new ProvaService();
|
||||
@@ -499,7 +581,7 @@ export const uploadProva = async (req: Request, res: Response) => {
|
||||
|
||||
return res.status(200).json(result);
|
||||
} catch (error) {
|
||||
console.error('Erro ao processar upload de prova:', error);
|
||||
console.error("Erro ao processar upload de prova:", error);
|
||||
return res.status(500).json({ error: error.message });
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user