diff --git a/INSTALL_PDF.md b/INSTALL_PDF.md
new file mode 100644
index 0000000..996d843
--- /dev/null
+++ b/INSTALL_PDF.md
@@ -0,0 +1,100 @@
+# Passos para ativar a funcionalidade de PDF
+
+## 1. Instalar Dependências
+
+Execute o seguinte comando para instalar todas as dependências necessárias:
+
+```bash
+npm install pdf-lib pdf-parse pdf2pic tesseract.js
+npm install --save-dev @types/pdf-parse
+```
+
+## 2. Executar Migration do Banco de Dados
+
+Execute a migration para criar a tabela de operações de PDF:
+
+```bash
+npx prisma migrate dev --name add_operacoes_pdf
+```
+
+Ou simplesmente:
+
+```bash
+npm run prisma:migrate
+```
+
+## 3. Gerar Cliente Prisma
+
+Gere o cliente Prisma atualizado:
+
+```bash
+npm run prisma:generate
+```
+
+## 4. Criar Diretórios
+
+Os diretórios serão criados automaticamente, mas você pode criá-los manualmente:
+
+**No Windows (PowerShell):**
+```powershell
+New-Item -Path "storage\temp\pdf-ops" -ItemType Directory -Force
+New-Item -Path "storage\pdf-output" -ItemType Directory -Force
+New-Item -Path "storage\temp\uploads" -ItemType Directory -Force
+```
+
+**No Linux/Mac:**
+```bash
+mkdir -p storage/temp/pdf-ops
+mkdir -p storage/pdf-output
+mkdir -p storage/temp/uploads
+```
+
+## 5. Reiniciar o Servidor
+
+Após instalar as dependências e executar as migrations, reinicie o servidor:
+
+```bash
+npm run dev:full
+```
+
+Ou apenas o backend:
+
+```bash
+npm run server
+```
+
+## Verificação
+
+Após seguir todos os passos:
+
+1. Acesse a aplicação no navegador
+2. Faça login
+3. Clique no item "PDFs" no menu lateral
+4. Teste a funcionalidade de compressão enviando um PDF
+
+## Troubleshooting
+
+### Se continuar dando erro 404:
+
+1. Verifique se o servidor foi reiniciado após as mudanças
+2. Verifique se as dependências foram instaladas corretamente
+3. Verifique os logs do servidor para erros de importação
+
+### Se der erro de módulo não encontrado:
+
+Execute:
+```bash
+npm install
+npm run prisma:generate
+```
+
+### Se der erro no banco de dados:
+
+Execute:
+```bash
+npx prisma migrate reset
+npx prisma migrate dev
+npm run prisma:generate
+```
+
+**ATENÇÃO:** O comando `migrate reset` apaga todos os dados do banco!
diff --git a/backend/middleware/auth.ts b/backend/middleware/auth.ts
deleted file mode 100644
index a7b5505..0000000
--- a/backend/middleware/auth.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-import { Request, Response, NextFunction } from 'express';
-import jwt from 'jsonwebtoken';
-import { PrismaClient } from '@prisma/client';
-
-const prisma = new PrismaClient();
-
-// Adicionar interface para tipo de usuário no Request
-declare global {
- namespace Express {
- interface Request {
- user?: {
- id: string;
- nome: string;
- email: string;
- perfil: string;
- };
- }
- }
-}
-
-export const authenticateToken = async (
- req: Request,
- res: Response,
- next: NextFunction
-): Promise => {
- try {
- // Verificar se há token
- const authHeader = req.headers.authorization;
- if (!authHeader) {
- res.status(401).json({ error: 'Token não fornecido' });
- return;
- }
-
- // Extrair o token do header (remover o Bearer)
- const token = authHeader.replace('Bearer ', '');
-
- // Verificar o token
- const secret = process.env.JWT_SECRET || 'default-secret';
- const decoded = jwt.verify(token, secret) as any;
-
- // Buscar o usuário no banco
- const user = await prisma.usuario.findUnique({
- where: { id: decoded.id },
- select: {
- id: true,
- nome: true,
- email: true,
- perfil: true,
- ativo: true,
- },
- });
-
- if (!user || !user.ativo) {
- res.status(401).json({ error: 'Usuário inválido ou inativo' });
- return;
- }
-
- // Adicionar o usuário no request
- req.user = {
- id: user.id,
- nome: user.nome,
- email: user.email,
- perfil: user.perfil,
- };
-
- next();
- } catch (error) {
- console.error('Erro na autenticação:', error);
-
- if (error instanceof jwt.TokenExpiredError) {
- res.status(401).json({ error: 'Token expirado' });
- return;
- }
-
- if (error instanceof jwt.JsonWebTokenError) {
- res.status(401).json({ error: 'Token inválido' });
- return;
- }
-
- res.status(500).json({ error: 'Erro ao autenticar' });
- }
-};
-
-export const requireAdmin = async (
- req: Request,
- res: Response,
- next: NextFunction
-): Promise => {
- try {
- if (!req.user) {
- res.status(401).json({ error: 'Usuário não autenticado' });
- return;
- }
-
- if (req.user.perfil !== 'ADVOGADO_ADMIN') {
- res.status(403).json({ error: 'Acesso negado. Apenas administradores.' });
- return;
- }
-
- next();
- } catch (error) {
- console.error('Erro na verificação de permissão:', error);
- res.status(500).json({ error: 'Erro ao verificar permissões' });
- }
-};
diff --git a/prisma/migrations/20260227000000_add_operacoes_pdf.sql b/prisma/migrations/20260227000000_add_operacoes_pdf.sql
index 4102d31..c6f29d1 100644
--- a/prisma/migrations/20260227000000_add_operacoes_pdf.sql
+++ b/prisma/migrations/20260227000000_add_operacoes_pdf.sql
@@ -22,12 +22,13 @@ CREATE TABLE "operacoes_pdf" (
"detalhesOCR" JSONB,
"tempoProcessamento" INTEGER,
"criadoEm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
- "atualizadoEm" TIMESTAMP(3) NOT NULL,
+ "atualizadoEm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "operacoes_pdf_pkey" PRIMARY KEY ("id")
);
--- Adicionar foreign key para usuariosALTER TABLE "operacoes_pdf" ADD CONSTRAINT "operacoes_pdf_usuarioId_fkey" FOREIGN KEY ("usuarioId") REFERENCES "usuarios"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+-- Adicionar foreign key para usuarios
+ALTER TABLE "operacoes_pdf" ADD CONSTRAINT "operacoes_pdf_usuarioId_fkey" FOREIGN KEY ("usuarioId") REFERENCES "usuarios"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- Criar índice para melhor performance nas consultas
CREATE INDEX "operacoes_pdf_usuarioId_idx" ON "operacoes_pdf"("usuarioId");
diff --git a/src/components/layouts/MainLayout.tsx b/src/components/layouts/MainLayout.tsx
index 9c12a6d..7c7a46b 100644
--- a/src/components/layouts/MainLayout.tsx
+++ b/src/components/layouts/MainLayout.tsx
@@ -18,6 +18,7 @@ import {
CurrencyDollarIcon,
CalculatorIcon,
BriefcaseIcon,
+ DocumentDuplicateIcon,
} from "@heroicons/react/24/outline";
import { Menu, Transition } from "@headlessui/react";
import api from "../../services/api";
@@ -91,6 +92,7 @@ export const MainLayout: React.FC = () => {
{ path: "/atendimentos", label: "Atendimentos", icon: UserIcon },
{ path: "/tarefas", label: "Tarefas", icon: ClipboardDocumentCheckIcon },
{ path: "/documentos", label: "Documentos", icon: DocumentTextIcon },
+ { path: "/pdf", label: "PDFs", icon: DocumentDuplicateIcon },
{
path: null,
label: "Cálculos",
diff --git a/src/components/pdf/PDFManipulationPage.tsx b/src/components/pdf/PDFManipulationPage.tsx
index ba99e8d..52892b8 100644
--- a/src/components/pdf/PDFManipulationPage.tsx
+++ b/src/components/pdf/PDFManipulationPage.tsx
@@ -2,6 +2,7 @@ import React, { useState, useCallback } from 'react';
import { useDropzone } from 'react-dropzone';
import axios from 'axios';
import { toast } from 'react-toastify';
+import { useAuth } from '../../contexts/AuthContext';
import {
DocumentIcon,
CloudArrowUpIcon,
@@ -36,6 +37,7 @@ type OperationType =
| 'info';
export const PDFManipulationPage: React.FC = () => {
+ const { getToken } = useAuth();
const [selectedOperation, setSelectedOperation] = useState('compress');
const [files, setFiles] = useState([]);
const [processing, setProcessing] = useState(false);
@@ -74,10 +76,6 @@ export const PDFManipulationPage: React.FC = () => {
setResult(null);
};
- const getToken = () => {
- return localStorage.getItem('token');
- };
-
const processFiles = async () => {
if (files.length === 0) {
toast.error('Selecione pelo menos um arquivo');
@@ -645,6 +643,20 @@ export const PDFManipulationPage: React.FC = () => {
{op.tempoProcessamento}ms
)}
+ {op.status === 'CONCLUIDO' && op.arquivosResultado && op.arquivosResultado.length > 0 && (
+
+ {op.arquivosResultado.map((arquivo: string, index: number) => (
+
+ ))}
+
+ )}
))}
diff --git a/backend/controllers/pdfController.ts b/src/server/controllers/pdfController.ts
similarity index 97%
rename from backend/controllers/pdfController.ts
rename to src/server/controllers/pdfController.ts
index fcd15cb..61335a9 100644
--- a/backend/controllers/pdfController.ts
+++ b/src/server/controllers/pdfController.ts
@@ -1,6 +1,6 @@
import { Request, Response } from 'express';
import { PrismaClient } from '@prisma/client';
-import { PDFService } from '../services/pdfService';
+import { PDFService } from '../../services/pdfService';
import multer from 'multer';
import path from 'path';
import fs from 'fs/promises';
@@ -138,9 +138,10 @@ export class PDFController {
success: true,
operacaoId,
results,
+ outputPaths: arquivosResultado,
totalOriginalSize: Number(tamanhoOriginal),
totalCompressedSize: Number(tamanhoResultado),
- totalReduction: ((Number(tamanhoOriginal) - Number(tamanhoResultado)) / Number(tamanhoOriginal)) * 100,
+ reduction: ((Number(tamanhoOriginal) - Number(tamanhoResultado)) / Number(tamanhoOriginal)) * 100,
processingTime: tempoProcessamento,
});
} catch (error) {
@@ -346,6 +347,7 @@ export class PDFController {
success: true,
operacaoId,
outputPath,
+ outputPaths: [outputPath],
originalSize: Number(tamanhoOriginal),
mergedSize: Number(tamanhoResultado),
processingTime: tempoProcessamento,
@@ -551,6 +553,7 @@ export class PDFController {
success: true,
operacaoId,
outputPath,
+ outputPaths: [outputPath],
imagesProcessed: files.length,
pdfSize: Number(tamanhoResultado),
processingTime: tempoProcessamento,
@@ -725,6 +728,7 @@ export class PDFController {
mensagemErro: true,
criadoEm: true,
parametros: true,
+ arquivosResultado: true,
},
}),
prisma.operacaoPDF.count({
@@ -732,9 +736,16 @@ export class PDFController {
}),
]);
+ // Converter BigInt para Number para evitar erro de serialização JSON
+ const operacoesSerializadas = operacoes.map(op => ({
+ ...op,
+ tamanhoOriginal: Number(op.tamanhoOriginal),
+ tamanhoResultado: op.tamanhoResultado ? Number(op.tamanhoResultado) : null,
+ }));
+
res.json({
success: true,
- operacoes,
+ operacoes: operacoesSerializadas,
pagination: {
page,
limit,
diff --git a/src/server/index.ts b/src/server/index.ts
index 96bf2e4..9463539 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -27,6 +27,7 @@ import indicesMonetariosRoutes from './routes/indicesMonetarios';
import pensaoAlimenticiaRouter from './routes/pensaoAlimenticia'; // Importar router de pensão alimentícia
import financeiroPainelRoutes from './routes/financeiroPainel'; // Importar rotas do financeiro painel
import audienciasRoutes from './routes/audiencias'; // Importar rotas de audiências
+import pdfRoutes from './routes/pdf'; // Importar rotas de PDF
const app = express();
@@ -1481,4 +1482,5 @@ app.use('/api/calculos', calculosJudiciaisRoutes);
app.use('/api/indices', indicesMonetariosRoutes);
app.use('/api/pensao-alimenticia', pensaoAlimenticiaRouter);
app.use('/api/financeiro-painel', financeiroPainelRoutes); // Adicionar uso das rotas do financeiro painel
-app.use('/api/audiencias', audienciasRoutes); // Adicionar uso das rotas de audiências
\ No newline at end of file
+app.use('/api/audiencias', audienciasRoutes); // Adicionar uso das rotas de audiências
+app.use('/api/pdf', pdfRoutes); // Adicionar uso das rotas de PDF
\ No newline at end of file
diff --git a/backend/routes/pdf.ts b/src/server/routes/pdf.ts
similarity index 84%
rename from backend/routes/pdf.ts
rename to src/server/routes/pdf.ts
index 9bcedc5..e80edee 100644
--- a/backend/routes/pdf.ts
+++ b/src/server/routes/pdf.ts
@@ -1,6 +1,6 @@
import express from 'express';
import { PDFController, uploadMiddleware } from '../controllers/pdfController';
-import { authenticateToken } from '../middleware/auth';
+import { authMiddleware } from '../../middleware/auth';
const router = express.Router();
@@ -12,7 +12,7 @@ const router = express.Router();
// Comprime PDFs
router.post(
'/compress',
- authenticateToken,
+ authMiddleware,
uploadMiddleware.array('files', 10),
PDFController.compress
);
@@ -20,7 +20,7 @@ router.post(
// Divide PDF
router.post(
'/split',
- authenticateToken,
+ authMiddleware,
uploadMiddleware.array('files', 1),
PDFController.split
);
@@ -28,7 +28,7 @@ router.post(
// Mescla PDFs
router.post(
'/merge',
- authenticateToken,
+ authMiddleware,
uploadMiddleware.array('files', 50),
PDFController.merge
);
@@ -36,7 +36,7 @@ router.post(
// Converte PDF para imagens
router.post(
'/pdf-to-images',
- authenticateToken,
+ authMiddleware,
uploadMiddleware.array('files', 1),
PDFController.pdfToImages
);
@@ -44,7 +44,7 @@ router.post(
// Converte imagens para PDF
router.post(
'/images-to-pdf',
- authenticateToken,
+ authMiddleware,
uploadMiddleware.array('files', 100),
PDFController.imagesToPDF
);
@@ -52,7 +52,7 @@ router.post(
// Realiza OCR em PDF
router.post(
'/ocr',
- authenticateToken,
+ authMiddleware,
uploadMiddleware.array('files', 1),
PDFController.performOCR
);
@@ -60,7 +60,7 @@ router.post(
// Obtém informações de um PDF
router.post(
'/info',
- authenticateToken,
+ authMiddleware,
uploadMiddleware.array('files', 1),
PDFController.getInfo
);
@@ -68,14 +68,14 @@ router.post(
// Obtém histórico de operações
router.get(
'/history',
- authenticateToken,
+ authMiddleware,
PDFController.getHistory
);
// Download de arquivo processado
router.get(
'/download/:operacaoId/:fileIndex',
- authenticateToken,
+ authMiddleware,
PDFController.downloadFile
);
diff --git a/src/types/express.d.ts b/src/types/express.d.ts
index 3377337..088cd96 100644
--- a/src/types/express.d.ts
+++ b/src/types/express.d.ts
@@ -1,5 +1,3 @@
-import { Request } from 'express';
-
declare global {
namespace Express {
interface Request {
@@ -11,3 +9,5 @@ declare global {
}
}
}
+
+export {};