pdf: add new feature
Signed-off-by: Rodrigo Bertoldo de Sousa Martins <rodrigo.sousa.577@gmail.com>
This commit is contained in:
1 parent
a63aaeb655
commit
61a1631519
14 files changed
+3091
-1
No files matched your search
@@ -5,6 +5,7 @@ import usuariosRoutes from "./routes/usuarios";
|
||||
import clientesRoutes from "./routes/clientes";
|
||||
import processosRoutes from "./routes/processos";
|
||||
import audienciasRoutes from "./routes/audiencias";
|
||||
import pdfRoutes from "./routes/pdf";
|
||||
|
||||
const app = express();
|
||||
const prisma = new PrismaClient();
|
||||
@@ -16,6 +17,7 @@ app.use("/api/usuarios", usuariosRoutes);
|
||||
app.use("/api/clientes", clientesRoutes);
|
||||
app.use("/api/processos", processosRoutes);
|
||||
app.use("/api/audiencias", audienciasRoutes);
|
||||
app.use("/api/pdf", pdfRoutes);
|
||||
|
||||
app.get("/", (req, res) => {
|
||||
res.json({ message: "API is running" });
|
||||
|
||||
@@ -0,0 +1,798 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { PDFService } from '../services/pdfService';
|
||||
import multer from 'multer';
|
||||
import path from 'path';
|
||||
import fs from 'fs/promises';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
// Configuração do multer para upload de arquivos
|
||||
const storage = multer.diskStorage({
|
||||
destination: async (req, file, cb) => {
|
||||
const uploadDir = path.join(process.cwd(), 'storage', 'temp', 'uploads');
|
||||
await fs.mkdir(uploadDir, { recursive: true });
|
||||
cb(null, uploadDir);
|
||||
},
|
||||
filename: (req, file, cb) => {
|
||||
const uniqueName = `${uuidv4()}${path.extname(file.originalname)}`;
|
||||
cb(null, uniqueName);
|
||||
},
|
||||
});
|
||||
|
||||
const upload = multer({
|
||||
storage,
|
||||
limits: {
|
||||
fileSize: 100 * 1024 * 1024, // 100MB
|
||||
},
|
||||
fileFilter: (req, file, cb) => {
|
||||
const allowedMimes = [
|
||||
'application/pdf',
|
||||
'image/png',
|
||||
'image/jpeg',
|
||||
'image/jpg',
|
||||
'image/webp',
|
||||
];
|
||||
|
||||
if (allowedMimes.includes(file.mimetype)) {
|
||||
cb(null, true);
|
||||
} else {
|
||||
cb(new Error('Tipo de arquivo não permitido. Use PDF ou imagens (PNG, JPG, WEBP).'));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const uploadMiddleware = upload;
|
||||
|
||||
/**
|
||||
* Controller para operações de PDF
|
||||
*/
|
||||
export class PDFController {
|
||||
/**
|
||||
* Comprime um ou mais PDFs
|
||||
*/
|
||||
static async compress(req: Request, res: Response): Promise<void> {
|
||||
const startTime = Date.now();
|
||||
let operacaoId: string | null = null;
|
||||
|
||||
try {
|
||||
const files = req.files as Express.Multer.File[];
|
||||
const userId = (req as any).user?.id;
|
||||
|
||||
if (!files || files.length === 0) {
|
||||
res.status(400).json({ error: 'Nenhum arquivo enviado' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userId) {
|
||||
res.status(401).json({ error: 'Usuário não autenticado' });
|
||||
return;
|
||||
}
|
||||
|
||||
const quality = req.body.quality ? parseInt(req.body.quality) : 95;
|
||||
const maxSizeMB = req.body.maxSizeMB ? parseInt(req.body.maxSizeMB) : undefined;
|
||||
|
||||
const arquivosOriginais: string[] = [];
|
||||
const arquivosResultado: string[] = [];
|
||||
let tamanhoOriginal = BigInt(0);
|
||||
let tamanhoResultado = BigInt(0);
|
||||
|
||||
// Cria registro da operação no banco
|
||||
const operacao = await prisma.operacaoPDF.create({
|
||||
data: {
|
||||
tipoOperacao: 'COMPRIMIR',
|
||||
status: 'PROCESSANDO',
|
||||
usuarioId: userId,
|
||||
arquivosOriginais: [],
|
||||
arquivosResultado: [],
|
||||
tamanhoOriginal: BigInt(0),
|
||||
parametros: { quality, maxSizeMB },
|
||||
},
|
||||
});
|
||||
|
||||
operacaoId = operacao.id;
|
||||
|
||||
// Processa cada arquivo
|
||||
const results = [];
|
||||
for (const file of files) {
|
||||
arquivosOriginais.push(file.path);
|
||||
tamanhoOriginal += BigInt(file.size);
|
||||
|
||||
const result = await PDFService.compressPDF(file.path, undefined, {
|
||||
quality,
|
||||
maxSizeMB,
|
||||
});
|
||||
|
||||
arquivosResultado.push(result.outputPath);
|
||||
tamanhoResultado += BigInt(result.compressedSize);
|
||||
|
||||
results.push({
|
||||
originalName: file.originalname,
|
||||
originalSize: result.originalSize,
|
||||
compressedSize: result.compressedSize,
|
||||
reduction: result.reduction,
|
||||
outputPath: result.outputPath,
|
||||
});
|
||||
|
||||
// Remove o arquivo original
|
||||
await fs.unlink(file.path).catch(() => {});
|
||||
}
|
||||
|
||||
const tempoProcessamento = Date.now() - startTime;
|
||||
|
||||
// Atualiza a operação no banco
|
||||
await prisma.operacaoPDF.update({
|
||||
where: { id: operacaoId },
|
||||
data: {
|
||||
status: 'CONCLUIDO',
|
||||
arquivosOriginais,
|
||||
arquivosResultado,
|
||||
tamanhoOriginal,
|
||||
tamanhoResultado,
|
||||
tempoProcessamento,
|
||||
},
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
operacaoId,
|
||||
results,
|
||||
totalOriginalSize: Number(tamanhoOriginal),
|
||||
totalCompressedSize: Number(tamanhoResultado),
|
||||
totalReduction: ((Number(tamanhoOriginal) - Number(tamanhoResultado)) / Number(tamanhoOriginal)) * 100,
|
||||
processingTime: tempoProcessamento,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao comprimir PDF:', error);
|
||||
|
||||
if (operacaoId) {
|
||||
await prisma.operacaoPDF.update({
|
||||
where: { id: operacaoId },
|
||||
data: {
|
||||
status: 'ERRO',
|
||||
mensagemErro: error instanceof Error ? error.message : 'Erro desconhecido',
|
||||
tempoProcessamento: Date.now() - startTime,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
res.status(500).json({
|
||||
error: 'Falha ao comprimir PDF',
|
||||
message: error instanceof Error ? error.message : 'Erro desconhecido',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Divide um PDF
|
||||
*/
|
||||
static async split(req: Request, res: Response): Promise<void> {
|
||||
const startTime = Date.now();
|
||||
let operacaoId: string | null = null;
|
||||
|
||||
try {
|
||||
const file = (req.files as Express.Multer.File[])?.[0];
|
||||
const userId = (req as any).user?.id;
|
||||
|
||||
if (!file) {
|
||||
res.status(400).json({ error: 'Nenhum arquivo enviado' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userId) {
|
||||
res.status(401).json({ error: 'Usuário não autenticado' });
|
||||
return;
|
||||
}
|
||||
|
||||
const { splitType, pages, ranges, chunkSize } = req.body;
|
||||
|
||||
if (!splitType) {
|
||||
res.status(400).json({ error: 'Tipo de divisão não especificado' });
|
||||
return;
|
||||
}
|
||||
|
||||
const options: any = { splitType };
|
||||
|
||||
if (splitType === 'pages' && pages) {
|
||||
options.pages = JSON.parse(pages);
|
||||
} else if (splitType === 'range' && ranges) {
|
||||
options.ranges = JSON.parse(ranges);
|
||||
} else if (splitType === 'chunks' && chunkSize) {
|
||||
options.chunkSize = parseInt(chunkSize);
|
||||
}
|
||||
|
||||
// Cria registro da operação
|
||||
const operacao = await prisma.operacaoPDF.create({
|
||||
data: {
|
||||
tipoOperacao: 'DIVIDIR',
|
||||
status: 'PROCESSANDO',
|
||||
usuarioId: userId,
|
||||
arquivosOriginais: [file.path],
|
||||
arquivosResultado: [],
|
||||
tamanhoOriginal: BigInt(file.size),
|
||||
parametros: options,
|
||||
},
|
||||
});
|
||||
|
||||
operacaoId = operacao.id;
|
||||
|
||||
// Divide o PDF
|
||||
const outputPaths = await PDFService.splitPDF(file.path, options);
|
||||
|
||||
// Calcula tamanho total dos arquivos resultantes
|
||||
let tamanhoResultado = BigInt(0);
|
||||
for (const outputPath of outputPaths) {
|
||||
const stats = await fs.stat(outputPath);
|
||||
tamanhoResultado += BigInt(stats.size);
|
||||
}
|
||||
|
||||
const tempoProcessamento = Date.now() - startTime;
|
||||
|
||||
// Atualiza a operação
|
||||
await prisma.operacaoPDF.update({
|
||||
where: { id: operacaoId },
|
||||
data: {
|
||||
status: 'CONCLUIDO',
|
||||
arquivosResultado: outputPaths,
|
||||
tamanhoResultado,
|
||||
tempoProcessamento,
|
||||
},
|
||||
});
|
||||
|
||||
// Remove o arquivo original
|
||||
await fs.unlink(file.path).catch(() => {});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
operacaoId,
|
||||
filesCreated: outputPaths.length,
|
||||
outputPaths,
|
||||
processingTime: tempoProcessamento,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao dividir PDF:', error);
|
||||
|
||||
if (operacaoId) {
|
||||
await prisma.operacaoPDF.update({
|
||||
where: { id: operacaoId },
|
||||
data: {
|
||||
status: 'ERRO',
|
||||
mensagemErro: error instanceof Error ? error.message : 'Erro desconhecido',
|
||||
tempoProcessamento: Date.now() - startTime,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
res.status(500).json({
|
||||
error: 'Falha ao dividir PDF',
|
||||
message: error instanceof Error ? error.message : 'Erro desconhecido',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mescla múltiplos PDFs
|
||||
*/
|
||||
static async merge(req: Request, res: Response): Promise<void> {
|
||||
const startTime = Date.now();
|
||||
let operacaoId: string | null = null;
|
||||
|
||||
try {
|
||||
const files = req.files as Express.Multer.File[];
|
||||
const userId = (req as any).user?.id;
|
||||
|
||||
if (!files || files.length < 2) {
|
||||
res.status(400).json({ error: 'São necessários pelo menos 2 arquivos para mesclar' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userId) {
|
||||
res.status(401).json({ error: 'Usuário não autenticado' });
|
||||
return;
|
||||
}
|
||||
|
||||
const filePaths = files.map((f) => f.path);
|
||||
let tamanhoOriginal = BigInt(0);
|
||||
|
||||
for (const file of files) {
|
||||
tamanhoOriginal += BigInt(file.size);
|
||||
}
|
||||
|
||||
// Cria registro da operação
|
||||
const operacao = await prisma.operacaoPDF.create({
|
||||
data: {
|
||||
tipoOperacao: 'MESCLAR',
|
||||
status: 'PROCESSANDO',
|
||||
usuarioId: userId,
|
||||
arquivosOriginais: filePaths,
|
||||
arquivosResultado: [],
|
||||
tamanhoOriginal,
|
||||
parametros: { filesCount: files.length },
|
||||
},
|
||||
});
|
||||
|
||||
operacaoId = operacao.id;
|
||||
|
||||
// Mescla os PDFs
|
||||
const outputPath = await PDFService.mergePDFs({
|
||||
filePaths,
|
||||
outputPath: path.join(process.cwd(), 'storage', 'pdf-output', `merged-${uuidv4()}.pdf`),
|
||||
});
|
||||
|
||||
// Calcula tamanho do arquivo resultante
|
||||
const stats = await fs.stat(outputPath);
|
||||
const tamanhoResultado = BigInt(stats.size);
|
||||
|
||||
const tempoProcessamento = Date.now() - startTime;
|
||||
|
||||
// Atualiza a operação
|
||||
await prisma.operacaoPDF.update({
|
||||
where: { id: operacaoId },
|
||||
data: {
|
||||
status: 'CONCLUIDO',
|
||||
arquivosResultado: [outputPath],
|
||||
tamanhoResultado,
|
||||
tempoProcessamento,
|
||||
},
|
||||
});
|
||||
|
||||
// Remove os arquivos originais
|
||||
for (const filePath of filePaths) {
|
||||
await fs.unlink(filePath).catch(() => {});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
operacaoId,
|
||||
outputPath,
|
||||
originalSize: Number(tamanhoOriginal),
|
||||
mergedSize: Number(tamanhoResultado),
|
||||
processingTime: tempoProcessamento,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao mesclar PDFs:', error);
|
||||
|
||||
if (operacaoId) {
|
||||
await prisma.operacaoPDF.update({
|
||||
where: { id: operacaoId },
|
||||
data: {
|
||||
status: 'ERRO',
|
||||
mensagemErro: error instanceof Error ? error.message : 'Erro desconhecido',
|
||||
tempoProcessamento: Date.now() - startTime,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
res.status(500).json({
|
||||
error: 'Falha ao mesclar PDFs',
|
||||
message: error instanceof Error ? error.message : 'Erro desconhecido',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converte PDF em imagens
|
||||
*/
|
||||
static async pdfToImages(req: Request, res: Response): Promise<void> {
|
||||
const startTime = Date.now();
|
||||
let operacaoId: string | null = null;
|
||||
|
||||
try {
|
||||
const file = (req.files as Express.Multer.File[])?.[0];
|
||||
const userId = (req as any).user?.id;
|
||||
|
||||
if (!file) {
|
||||
res.status(400).json({ error: 'Nenhum arquivo enviado' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userId) {
|
||||
res.status(401).json({ error: 'Usuário não autenticado' });
|
||||
return;
|
||||
}
|
||||
|
||||
const format = req.body.format || 'png';
|
||||
const quality = req.body.quality ? parseInt(req.body.quality) : 95;
|
||||
const density = req.body.density ? parseInt(req.body.density) : 300;
|
||||
const pages = req.body.pages ? JSON.parse(req.body.pages) : 'all';
|
||||
|
||||
// Cria registro da operação
|
||||
const operacao = await prisma.operacaoPDF.create({
|
||||
data: {
|
||||
tipoOperacao: 'PDF_PARA_IMAGEM',
|
||||
status: 'PROCESSANDO',
|
||||
usuarioId: userId,
|
||||
arquivosOriginais: [file.path],
|
||||
arquivosResultado: [],
|
||||
tamanhoOriginal: BigInt(file.size),
|
||||
parametros: { format, quality, density, pages },
|
||||
},
|
||||
});
|
||||
|
||||
operacaoId = operacao.id;
|
||||
|
||||
// Converte para imagens
|
||||
const outputPaths = await PDFService.pdfToImages(file.path, {
|
||||
format,
|
||||
quality,
|
||||
density,
|
||||
pages,
|
||||
});
|
||||
|
||||
// Calcula tamanho total
|
||||
let tamanhoResultado = BigInt(0);
|
||||
for (const outputPath of outputPaths) {
|
||||
const stats = await fs.stat(outputPath);
|
||||
tamanhoResultado += BigInt(stats.size);
|
||||
}
|
||||
|
||||
const tempoProcessamento = Date.now() - startTime;
|
||||
|
||||
// Atualiza a operação
|
||||
await prisma.operacaoPDF.update({
|
||||
where: { id: operacaoId },
|
||||
data: {
|
||||
status: 'CONCLUIDO',
|
||||
arquivosResultado: outputPaths,
|
||||
tamanhoResultado,
|
||||
tempoProcessamento,
|
||||
},
|
||||
});
|
||||
|
||||
// Remove o arquivo original
|
||||
await fs.unlink(file.path).catch(() => {});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
operacaoId,
|
||||
imagesCreated: outputPaths.length,
|
||||
outputPaths,
|
||||
processingTime: tempoProcessamento,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao converter PDF para imagens:', error);
|
||||
|
||||
if (operacaoId) {
|
||||
await prisma.operacaoPDF.update({
|
||||
where: { id: operacaoId },
|
||||
data: {
|
||||
status: 'ERRO',
|
||||
mensagemErro: error instanceof Error ? error.message : 'Erro desconhecido',
|
||||
tempoProcessamento: Date.now() - startTime,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
res.status(500).json({
|
||||
error: 'Falha ao converter PDF para imagens',
|
||||
message: error instanceof Error ? error.message : 'Erro desconhecido',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converte imagens em PDF
|
||||
*/
|
||||
static async imagesToPDF(req: Request, res: Response): Promise<void> {
|
||||
const startTime = Date.now();
|
||||
let operacaoId: string | null = null;
|
||||
|
||||
try {
|
||||
const files = req.files as Express.Multer.File[];
|
||||
const userId = (req as any).user?.id;
|
||||
|
||||
if (!files || files.length === 0) {
|
||||
res.status(400).json({ error: 'Nenhuma imagem enviada' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userId) {
|
||||
res.status(401).json({ error: 'Usuário não autenticado' });
|
||||
return;
|
||||
}
|
||||
|
||||
const imagePaths = files.map((f) => f.path);
|
||||
let tamanhoOriginal = BigInt(0);
|
||||
|
||||
for (const file of files) {
|
||||
tamanhoOriginal += BigInt(file.size);
|
||||
}
|
||||
|
||||
const fitToPage = req.body.fitToPage === 'true';
|
||||
const pageSize = req.body.pageSize ? JSON.parse(req.body.pageSize) : undefined;
|
||||
|
||||
// Cria registro da operação
|
||||
const operacao = await prisma.operacaoPDF.create({
|
||||
data: {
|
||||
tipoOperacao: 'IMAGEM_PARA_PDF',
|
||||
status: 'PROCESSANDO',
|
||||
usuarioId: userId,
|
||||
arquivosOriginais: imagePaths,
|
||||
arquivosResultado: [],
|
||||
tamanhoOriginal,
|
||||
parametros: { fitToPage, pageSize, imagesCount: files.length },
|
||||
},
|
||||
});
|
||||
|
||||
operacaoId = operacao.id;
|
||||
|
||||
// Converte para PDF
|
||||
const outputPath = await PDFService.imagesToPDF({
|
||||
imagePaths,
|
||||
outputPath: path.join(process.cwd(), 'storage', 'pdf-output', `images-to-pdf-${uuidv4()}.pdf`),
|
||||
fitToPage,
|
||||
pageSize,
|
||||
});
|
||||
|
||||
// Calcula tamanho do arquivo resultante
|
||||
const stats = await fs.stat(outputPath);
|
||||
const tamanhoResultado = BigInt(stats.size);
|
||||
|
||||
const tempoProcessamento = Date.now() - startTime;
|
||||
|
||||
// Atualiza a operação
|
||||
await prisma.operacaoPDF.update({
|
||||
where: { id: operacaoId },
|
||||
data: {
|
||||
status: 'CONCLUIDO',
|
||||
arquivosResultado: [outputPath],
|
||||
tamanhoResultado,
|
||||
tempoProcessamento,
|
||||
},
|
||||
});
|
||||
|
||||
// Remove os arquivos originais
|
||||
for (const imagePath of imagePaths) {
|
||||
await fs.unlink(imagePath).catch(() => {});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
operacaoId,
|
||||
outputPath,
|
||||
imagesProcessed: files.length,
|
||||
pdfSize: Number(tamanhoResultado),
|
||||
processingTime: tempoProcessamento,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao converter imagens para PDF:', error);
|
||||
|
||||
if (operacaoId) {
|
||||
await prisma.operacaoPDF.update({
|
||||
where: { id: operacaoId },
|
||||
data: {
|
||||
status: 'ERRO',
|
||||
mensagemErro: error instanceof Error ? error.message : 'Erro desconhecido',
|
||||
tempoProcessamento: Date.now() - startTime,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
res.status(500).json({
|
||||
error: 'Falha ao converter imagens para PDF',
|
||||
message: error instanceof Error ? error.message : 'Erro desconhecido',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Realiza OCR em PDF
|
||||
*/
|
||||
static async performOCR(req: Request, res: Response): Promise<void> {
|
||||
const startTime = Date.now();
|
||||
let operacaoId: string | null = null;
|
||||
|
||||
try {
|
||||
const file = (req.files as Express.Multer.File[])?.[0];
|
||||
const userId = (req as any).user?.id;
|
||||
|
||||
if (!file) {
|
||||
res.status(400).json({ error: 'Nenhum arquivo enviado' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userId) {
|
||||
res.status(401).json({ error: 'Usuário não autenticado' });
|
||||
return;
|
||||
}
|
||||
|
||||
const language = req.body.language || 'por';
|
||||
const pages = req.body.pages ? JSON.parse(req.body.pages) : 'all';
|
||||
|
||||
// Cria registro da operação
|
||||
const operacao = await prisma.operacaoPDF.create({
|
||||
data: {
|
||||
tipoOperacao: 'OCR',
|
||||
status: 'PROCESSANDO',
|
||||
usuarioId: userId,
|
||||
arquivosOriginais: [file.path],
|
||||
arquivosResultado: [],
|
||||
tamanhoOriginal: BigInt(file.size),
|
||||
parametros: { language, pages },
|
||||
},
|
||||
});
|
||||
|
||||
operacaoId = operacao.id;
|
||||
|
||||
// Realiza OCR
|
||||
const ocrResult = await PDFService.performOCR(file.path, {
|
||||
language,
|
||||
pages,
|
||||
});
|
||||
|
||||
const tempoProcessamento = Date.now() - startTime;
|
||||
|
||||
// Atualiza a operação
|
||||
await prisma.operacaoPDF.update({
|
||||
where: { id: operacaoId },
|
||||
data: {
|
||||
status: 'CONCLUIDO',
|
||||
detalhesOCR: ocrResult as any,
|
||||
tempoProcessamento,
|
||||
},
|
||||
});
|
||||
|
||||
// Remove o arquivo original
|
||||
await fs.unlink(file.path).catch(() => {});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
operacaoId,
|
||||
result: ocrResult,
|
||||
processingTime: tempoProcessamento,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao realizar OCR:', error);
|
||||
|
||||
if (operacaoId) {
|
||||
await prisma.operacaoPDF.update({
|
||||
where: { id: operacaoId },
|
||||
data: {
|
||||
status: 'ERRO',
|
||||
mensagemErro: error instanceof Error ? error.message : 'Erro desconhecido',
|
||||
tempoProcessamento: Date.now() - startTime,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
res.status(500).json({
|
||||
error: 'Falha ao realizar OCR',
|
||||
message: error instanceof Error ? error.message : 'Erro desconhecido',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtém informações sobre um PDF
|
||||
*/
|
||||
static async getInfo(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const file = (req.files as Express.Multer.File[])?.[0];
|
||||
|
||||
if (!file) {
|
||||
res.status(400).json({ error: 'Nenhum arquivo enviado' });
|
||||
return;
|
||||
}
|
||||
|
||||
const info = await PDFService.getPDFInfo(file.path);
|
||||
|
||||
// Remove o arquivo
|
||||
await fs.unlink(file.path).catch(() => {});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
info,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao obter informações do PDF:', error);
|
||||
res.status(500).json({
|
||||
error: 'Falha ao obter informações do PDF',
|
||||
message: error instanceof Error ? error.message : 'Erro desconhecido',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtém histórico de operações do usuário
|
||||
*/
|
||||
static async getHistory(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const userId = (req as any).user?.id;
|
||||
|
||||
if (!userId) {
|
||||
res.status(401).json({ error: 'Usuário não autenticado' });
|
||||
return;
|
||||
}
|
||||
|
||||
const page = parseInt(req.query.page as string) || 1;
|
||||
const limit = parseInt(req.query.limit as string) || 20;
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
const [operacoes, total] = await Promise.all([
|
||||
prisma.operacaoPDF.findMany({
|
||||
where: { usuarioId: userId },
|
||||
orderBy: { criadoEm: 'desc' },
|
||||
skip,
|
||||
take: limit,
|
||||
select: {
|
||||
id: true,
|
||||
tipoOperacao: true,
|
||||
status: true,
|
||||
tamanhoOriginal: true,
|
||||
tamanhoResultado: true,
|
||||
tempoProcessamento: true,
|
||||
mensagemErro: true,
|
||||
criadoEm: true,
|
||||
parametros: true,
|
||||
},
|
||||
}),
|
||||
prisma.operacaoPDF.count({
|
||||
where: { usuarioId: userId },
|
||||
}),
|
||||
]);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
operacoes,
|
||||
pagination: {
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
pages: Math.ceil(total / limit),
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao obter histórico:', error);
|
||||
res.status(500).json({
|
||||
error: 'Falha ao obter histórico',
|
||||
message: error instanceof Error ? error.message : 'Erro desconhecido',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download de arquivo processado
|
||||
*/
|
||||
static async downloadFile(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { operacaoId, fileIndex } = req.params;
|
||||
const userId = (req as any).user?.id;
|
||||
|
||||
if (!userId) {
|
||||
res.status(401).json({ error: 'Usuário não autenticado' });
|
||||
return;
|
||||
}
|
||||
|
||||
const operacao = await prisma.operacaoPDF.findUnique({
|
||||
where: { id: operacaoId },
|
||||
});
|
||||
|
||||
if (!operacao) {
|
||||
res.status(404).json({ error: 'Operação não encontrada' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (operacao.usuarioId !== userId) {
|
||||
res.status(403).json({ error: 'Acesso negado' });
|
||||
return;
|
||||
}
|
||||
|
||||
const index = parseInt(fileIndex);
|
||||
const filePath = operacao.arquivosResultado[index];
|
||||
|
||||
if (!filePath) {
|
||||
res.status(404).json({ error: 'Arquivo não encontrado' });
|
||||
return;
|
||||
}
|
||||
|
||||
res.download(filePath, path.basename(filePath));
|
||||
} catch (error) {
|
||||
console.error('Erro ao fazer download:', error);
|
||||
res.status(500).json({
|
||||
error: 'Falha ao fazer download',
|
||||
message: error instanceof Error ? error.message : 'Erro desconhecido',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
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<void> => {
|
||||
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<void> => {
|
||||
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' });
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
import express from 'express';
|
||||
import { PDFController, uploadMiddleware } from '../controllers/pdfController';
|
||||
import { authenticateToken } from '../middleware/auth';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
/**
|
||||
* Rotas para manipulação de PDF
|
||||
* Todas as rotas requerem autenticação
|
||||
*/
|
||||
|
||||
// Comprime PDFs
|
||||
router.post(
|
||||
'/compress',
|
||||
authenticateToken,
|
||||
uploadMiddleware.array('files', 10),
|
||||
PDFController.compress
|
||||
);
|
||||
|
||||
// Divide PDF
|
||||
router.post(
|
||||
'/split',
|
||||
authenticateToken,
|
||||
uploadMiddleware.array('files', 1),
|
||||
PDFController.split
|
||||
);
|
||||
|
||||
// Mescla PDFs
|
||||
router.post(
|
||||
'/merge',
|
||||
authenticateToken,
|
||||
uploadMiddleware.array('files', 50),
|
||||
PDFController.merge
|
||||
);
|
||||
|
||||
// Converte PDF para imagens
|
||||
router.post(
|
||||
'/pdf-to-images',
|
||||
authenticateToken,
|
||||
uploadMiddleware.array('files', 1),
|
||||
PDFController.pdfToImages
|
||||
);
|
||||
|
||||
// Converte imagens para PDF
|
||||
router.post(
|
||||
'/images-to-pdf',
|
||||
authenticateToken,
|
||||
uploadMiddleware.array('files', 100),
|
||||
PDFController.imagesToPDF
|
||||
);
|
||||
|
||||
// Realiza OCR em PDF
|
||||
router.post(
|
||||
'/ocr',
|
||||
authenticateToken,
|
||||
uploadMiddleware.array('files', 1),
|
||||
PDFController.performOCR
|
||||
);
|
||||
|
||||
// Obtém informações de um PDF
|
||||
router.post(
|
||||
'/info',
|
||||
authenticateToken,
|
||||
uploadMiddleware.array('files', 1),
|
||||
PDFController.getInfo
|
||||
);
|
||||
|
||||
// Obtém histórico de operações
|
||||
router.get(
|
||||
'/history',
|
||||
authenticateToken,
|
||||
PDFController.getHistory
|
||||
);
|
||||
|
||||
// Download de arquivo processado
|
||||
router.get(
|
||||
'/download/:operacaoId/:fileIndex',
|
||||
authenticateToken,
|
||||
PDFController.downloadFile
|
||||
);
|
||||
|
||||
export default router;
|
||||
Reference in new issue
Block a user