first commit

This commit is contained in:
rodrigo committed 2025-04-10 23:07:21 -03:00
commit c9fb67e751
423 files changed
+53287

No files matched your search

+51
View File
@@ -0,0 +1,51 @@
import { UsuarioRepository } from "../repositories/UsuarioRepository";
import bcrypt from "bcryptjs";
import jwt from "jsonwebtoken";
const JWT_SECRET = "seu-segredo-super-secreto"; // Em produção, use variável de ambiente
export class AuthService {
constructor(private usuarioRepository: UsuarioRepository) {}
async login(email: string, senha: string) {
console.log("AuthService - Login tentativa:", { email }); // Debug
const usuario = await this.usuarioRepository.findByEmail(email);
console.log("AuthService - Usuário encontrado:", usuario); // Debug
if (!usuario) {
throw new Error("Usuário não encontrado");
}
if (!usuario.ativo) {
throw new Error("Usuário inativo");
}
const senhaValida = await bcrypt.compare(senha, usuario.senha);
console.log("AuthService - Senha válida:", senhaValida); // Debug
if (!senhaValida) {
throw new Error("Senha inválida");
}
const token = jwt.sign(
{
id: usuario.id,
perfil: usuario.perfil,
email: usuario.email,
},
process.env.JWT_SECRET || "default-secret",
{ expiresIn: "1d" },
);
return {
user: {
id: usuario.id,
nome: usuario.nome,
email: usuario.email,
perfil: usuario.perfil,
},
token,
};
}
}
+73
View File
@@ -0,0 +1,73 @@
import OpenTimestamps from 'opentimestamps-javascript'; // Atualizar import
import { createHash } from 'crypto';
import * as fs from 'fs/promises';
import * as path from 'path';
export class BlockchainService {
private readonly storageDir: string;
constructor() {
this.storageDir = path.join(process.cwd(), 'storage', 'timestamps');
this.ensureStorageDir();
}
private async ensureStorageDir() {
try {
await fs.mkdir(this.storageDir, { recursive: true });
} catch (error) {
console.error('Erro ao criar diretório de timestamps:', error);
}
}
async createTimestamp(buffer: Buffer) {
try {
// 1. Criar hash do arquivo
const fileHash = createHash('sha256').update(buffer).digest();
// 2. Criar timestamp
const timestamp = await OpenTimestamps.stamp(fileHash);
// 3. Salvar arquivo .ots
const otsPath = path.join(this.storageDir, `${fileHash.toString('hex')}.ots`);
await fs.writeFile(otsPath, Buffer.from(timestamp));
return {
hash: fileHash.toString('hex'),
timestamp,
otsPath
};
} catch (error) {
console.error('Erro ao criar timestamp:', error);
throw new Error('Falha ao criar timestamp na blockchain');
}
}
async verifyTimestamp(hash: string, timestamp: Buffer) {
try {
// Usar a API correta do OpenTimestamps
const result = await OpenTimestamps.verify(
Buffer.from(timestamp),
Buffer.from(hash, 'hex')
);
if (!result) {
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'
};
} catch (error) {
console.error('Erro ao verificar timestamp:', error);
return {
verified: false,
message: 'Erro ao verificar timestamp'
};
}
}
}
+22
View File
@@ -0,0 +1,22 @@
import { google } from "googleapis";
import { Client } from "@microsoft/microsoft-graph-client";
export class CalendarioService {
async agendarAudiencia(audiencia: any): Promise<string> {
// Implementar integração com Google Calendar ou Outlook
const eventoId = await this.criarEvento({
titulo: `Audiência - ${audiencia.processo.numero}`,
inicio: audiencia.data,
fim: new Date(audiencia.data.getTime() + 3600000),
descricao: audiencia.observacoes,
local: audiencia.local,
});
return eventoId;
}
private async criarEvento(dados: any): Promise<string> {
// Implementar criação do evento
return "";
}
}
+109
View File
@@ -0,0 +1,109 @@
import { documentTemplates } from '../utils/documentTemplates';
interface DocumentoData {
tipo: keyof typeof documentTemplates.templates;
variaveis: Record<string, any>;
config: {
cabecalho: string;
rodape: string;
};
}
interface TemplateData {
cliente?: any;
atendimento?: any;
processo?: any;
advogado?: any;
escritorio?: any;
}
export class DocumentoService {
static processarVariaveisData(): Record<string, string> {
const hoje = new Date();
const formatador = new Intl.DateTimeFormat('pt-BR', {
day: 'numeric',
month: 'long',
year: 'numeric'
});
return {
'data.atual': hoje.toLocaleDateString('pt-BR'),
'data.atualExtenso': formatador.format(hoje), // Usar o formatador corretamente
'data.dia': hoje.getDate().toString(),
'data.mes': (hoje.getMonth() + 1).toString(),
'data.mesExtenso': new Intl.DateTimeFormat('pt-BR', { month: 'long' }).format(hoje),
'data.ano': hoje.getFullYear().toString()
};
}
static preencherTemplate(template: string, variaveis: Record<string, any>): string {
return template.replace(/\{\{([^}]+)\}\}/g, (match, key) => {
const valor = key.split('.').reduce((obj: any, k: string) => obj?.[k], variaveis);
return valor ?? match;
});
}
static processarVariaveis(template: string, data: TemplateData): string {
const dataProcessada = {
...data,
data: this.processarVariaveisData()
};
return template.replace(/\{\{([^}]+)\}\}/g, (match, path) => {
try {
const valor = path.split('.').reduce((obj, key) => obj?.[key], dataProcessada);
if (valor === undefined || valor === null) {
console.warn(`Variável não encontrada ou nula: ${path}`);
return match; // Retornar o match original em vez de string vazia
}
console.log(`Substituindo ${path} por:`, valor); // Debug log
return String(valor);
} catch (error) {
console.warn(`Erro ao processar variável ${path}:`, error);
return match; // Retornar o match original em vez de string vazia
}
});
}
static async gerarDocumentoFinal(documentoId: string): Promise<string> {
// Buscar documento e dados relacionados
const [documento, config] = await Promise.all([
api.get(`/documentos/${documentoId}`),
api.get('/documentos/configuracoes')
]);
// Buscar dados relacionados
const [cliente, atendimento, processo, advogado] = await Promise.all([
documento.data.clienteId ? api.get(`/clientes/${documento.data.clienteId}`) : null,
documento.data.atendimentoId ? api.get(`/atendimentos/${documento.data.atendimentoId}`) : null,
documento.data.processoId ? api.get(`/processos/${documento.data.processoId}`) : null,
api.get('/auth/me')
]);
// Processar template com as variáveis
const conteudoProcessado = this.processarVariaveis(documento.data.conteudo, {
cliente: cliente?.data,
atendimento: atendimento?.data,
processo: processo?.data,
advogado: advogado.data,
escritorio: config.data
});
return conteudoProcessado;
}
static gerarDocumento(dados: DocumentoData): string {
// 1. Pegar o template específico
const templateConteudo = documentTemplates.templates[dados.tipo];
// 2. Preencher o conteúdo com as variaveis
const conteudoPreenchido = this.preencherTemplate(templateConteudo, dados.variaveis);
// 3. Montar documento final com template base
return this.preencherTemplate(documentTemplates.base, {
cabecalho: dados.config.cabecalho,
conteudo: conteudoPreenchido,
rodape: dados.config.rodape
});
}
}
+122
View File
@@ -0,0 +1,122 @@
import { createHash } from 'crypto';
import { ExifTool } from 'exiftool-vendored';
import imageSize from 'image-size';
import PDFParser from 'pdf2json';
import { detectFileType } from 'detect-file-type';
export class FileValidator {
private exiftool: ExifTool;
constructor() {
this.exiftool = new ExifTool();
}
async validateFile(file: Buffer, mimeType: string) {
// Validação básica de integridade
const basicValidation = await this.performBasicValidation(file);
if (!basicValidation.valid) {
return basicValidation;
}
// Validação específica por tipo
switch (mimeType) {
case 'application/pdf':
return await this.validatePDF(file);
case 'image/jpeg':
case 'image/png':
return await this.validateImage(file, mimeType);
default:
return { valid: false, error: 'Tipo de arquivo não suportado' };
}
}
private async performBasicValidation(file: Buffer) {
// Verificar tamanho mínimo
if (file.length < 100) {
return { valid: false, error: 'Arquivo muito pequeno ou vazio' };
}
// Verificar assinatura do arquivo
const fileType = await detectFileType(file);
if (!fileType) {
return { valid: false, error: 'Tipo de arquivo não reconhecido' };
}
return { valid: true };
}
private async validatePDF(file: Buffer) {
return new Promise((resolve) => {
const pdfParser = new PDFParser();
pdfParser.on('pdfParser_dataReady', () => {
resolve({ valid: true, metadata: pdfParser.getAllFieldsTypes() });
});
pdfParser.on('pdfParser_dataError', (error) => {
resolve({ valid: false, error: `PDF inválido: ${error}` });
});
pdfParser.parseBuffer(file);
});
}
private async validateImage(file: Buffer, mimeType: string) {
try {
// Verificar dimensões
const dimensions = imageSize(file);
if (!dimensions.width || !dimensions.height) {
return { valid: false, error: 'Dimensões da imagem inválidas' };
}
// Extrair metadados EXIF
const metadata = await this.exiftool.readBuffer(file);
// Verificações específicas para JPEG
if (mimeType === 'image/jpeg') {
if (!metadata.FileType || metadata.FileType !== 'JPEG') {
return { valid: false, error: 'Assinatura JPEG inválida' };
}
}
return {
valid: true,
metadata: {
dimensions,
exif: metadata
}
};
} catch (error) {
return { valid: false, error: `Erro na validação da imagem: ${error.message}` };
}
}
async validateMetadata(metadata: any) {
const requiredFields = ['userAgent', 'ipAddress', 'timestamp'];
const missingFields = requiredFields.filter(field => !metadata[field]);
if (missingFields.length > 0) {
return {
valid: false,
error: `Campos obrigatórios ausentes: ${missingFields.join(', ')}`
};
}
return { valid: true };
}
generateHashes(file: Buffer) {
return {
sha256: createHash('sha256').update(file).digest('hex'),
sha512: createHash('sha512').update(file).digest('hex'),
md5: createHash('md5').update(file).digest('hex'),
blake2b: createHash('blake2b512').update(file).digest('hex'), // Hash adicional
whirlpool: createHash('whirlpool').update(file).digest('hex'), // Hash adicional
};
}
async cleanup() {
await this.exiftool.end();
}
}
+26
View File
@@ -0,0 +1,26 @@
import { Alerta } from "@prisma/client";
export class NotificacaoService {
async enviarNotificacao(
usuarioId: string,
tipo: string,
mensagem: string,
): Promise<void> {
// Implementar push notifications (ex: usando Firebase Cloud Messaging)
await this.salvarAlerta({
usuarioId,
tipo,
mensagem,
dataAlerta: new Date(),
});
}
private async salvarAlerta(dados: Partial<Alerta>): Promise<Alerta> {
// Implementar salvamento no banco
return null as any;
}
async verificarPrazos(): Promise<void> {
// Verificar prazos próximos e enviar alertas
}
}
+63
View File
@@ -0,0 +1,63 @@
import {
Processo,
StatusProcesso,
NaturezaProcesso,
TipoParte,
} from "../types/entities";
import axios from "axios";
export class ProcessoService {
constructor(private repository: any) {}
async criarProcesso(
processo: Omit<Processo, "id" | "dataCriacao" | "dataAtualizacao">,
): Promise<Processo> {
const novoProcesso: Processo = {
...processo,
id: crypto.randomUUID(),
dataCriacao: new Date(),
dataAtualizacao: new Date(),
prazos: [],
documentos: [],
};
return await this.repository.create(novoProcesso);
}
async atualizarStatus(id: string, status: StatusProcesso): Promise<Processo> {
return await this.repository.update(id, {
status,
dataAtualizacao: new Date(),
});
}
async adicionarPrazo(processoId: string, prazo: any): Promise<Processo> {
const processo = await this.repository.findById(processoId);
if (!processo) throw new Error("Processo não encontrado");
processo.prazos.push({
...prazo,
id: crypto.randomUUID(),
processoId,
});
return await this.repository.update(processoId, processo);
}
async consultarProcessoTribunal(numero: string): Promise<any> {
// Implementar integração com APIs dos tribunais
// Ex: TJ, TRF, etc
}
async agendarAudiencia(data: any): Promise<void> {
// Integração com Google Calendar/Outlook
}
async gerarPrazos(processo: Processo): Promise<void> {
// Gerar prazos automaticamente baseado na natureza
}
async monitorarMovimentacoes(processo: Processo): Promise<void> {
// Webscraping ou integração com APIs dos tribunais
}
}
+99
View File
@@ -0,0 +1,99 @@
import { provaQueue } from '../workers/ProvaWorker';
import { prisma } from '../config/database';
import { createHash } from 'crypto';
import { PDFDocument, StandardFonts } from 'pdf-lib';
import * as OpenTimestamps from 'opentimestamps';
import sharp from 'sharp';
export class ProvaService {
private fileValidator: FileValidator;
private reportGenerator: ReportGenerator;
constructor() {
this.fileValidator = new FileValidator();
this.reportGenerator = new ReportGenerator();
}
async processFile(file: Express.Multer.File, metadata: any) {
try {
console.log('Iniciando processamento do arquivo:', file.originalname); // Debug log
// Validar arquivo e metadados
if (!file.buffer || !file.originalname) {
throw new Error('Arquivo inválido');
}
// Criar registro inicial
const prova = await prisma.prova.create({
data: {
fileName: file.originalname,
mimeType: file.mimetype,
status: 'PENDENTE',
metadata: {
...metadata,
fileSize: file.size,
originalName: file.originalname
}
}
});
console.log('Registro criado com sucesso:', prova.id); // Debug log
// Adicionar à fila
await provaQueue.add('processar-prova', {
provaId: prova.id,
fileData: file.buffer.toString('base64'),
metadata
});
return prova;
} catch (error) {
console.error('Erro detalhado no processamento:', error);
throw new Error(`Erro ao processar arquivo ${file.originalname}: ${error.message}`);
}
}
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')
};
}
private async registerTimestamp(buffer: Buffer) {
const digest = createHash('sha256').update(buffer).digest();
const timestamp = await OpenTimestamps.stamp(digest);
return timestamp;
}
private async processFileByType(file: Express.Multer.File) {
if (file.mimetype.startsWith('image/')) {
return await this.processImage(file.buffer);
}
return file.buffer;
}
private async processImage(buffer: Buffer) {
return await sharp(buffer)
.resize(1200, 900, {
fit: 'inside',
withoutEnlargement: true
})
.toBuffer();
}
private async generateReport(data: any) {
const pdfDoc = await PDFDocument.create();
const page = pdfDoc.addPage();
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
// Adicionar conteúdo ao PDF
// ... implementar a geração do relatório ...
return {
path: '/path/to/report.pdf',
buffer: await pdfDoc.save()
};
}
}
+96
View File
@@ -0,0 +1,96 @@
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';
import QRCode from 'qrcode';
import { format } from 'date-fns';
import ptBR from 'date-fns/locale/pt-BR';
export class ReportGenerator {
async generateReport(data: {
file: Express.Multer.File;
checksums: { sha256: string; sha512: string; md5: string };
timestamp: Buffer;
metadata: any;
processedFile: Buffer;
verificationUrl: string;
}) {
const pdfDoc = await PDFDocument.create();
const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica);
const helveticaBold = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
// Página 1 - Resumo
const page1 = pdfDoc.addPage();
const { width, height } = page1.getSize();
// Gerar QR Code para verificação
const qrCode = await QRCode.toDataURL(data.verificationUrl);
const qrCodeImage = await pdfDoc.embedPng(qrCode);
// Cabeçalho
page1.drawText('Relatório de Validação Digital', {
x: 50,
y: height - 50,
size: 24,
font: helveticaBold,
});
// Informações do Arquivo
const fileInfo = [
`Nome do Arquivo: ${data.file.originalname}`,
`Tipo: ${data.file.mimetype}`,
`Tamanho: ${(data.file.size / 1024).toFixed(2)} KB`,
`Data Upload: ${format(new Date(), "dd/MM/yyyy HH:mm:ss", { locale: ptBR })}`,
`Data Validação: ${format(new Date(), "dd/MM/yyyy HH:mm:ss", { locale: ptBR })}`
];
// Checksums
const checksumInfo = [
'Checksums:',
`SHA-256: ${data.checksums.sha256}`,
`SHA-512: ${data.checksums.sha512}`,
`MD5: ${data.checksums.md5}`
];
// Informações do Dispositivo
const deviceInfo = [
'Informações do Dispositivo:',
`Navegador: ${data.metadata.userAgent}`,
`IP: ${data.metadata.ipAddress}`,
`Idioma: ${data.metadata.language}`,
`Resolução: ${data.metadata.viewportWidth}x${data.metadata.viewportHeight}`
];
// Desenhar seções
this.drawSection(page1, fileInfo, height - 100, helveticaFont);
this.drawSection(page1, checksumInfo, height - 250, helveticaFont);
this.drawSection(page1, deviceInfo, height - 400, helveticaFont);
// QR Code e instruções de verificação
page1.drawImage(qrCodeImage, {
x: width - 150,
y: height - 150,
width: 100,
height: 100,
});
// Página 2 - Detalhes Técnicos
const page2 = pdfDoc.addPage();
// Adicionar detalhes técnicos, como estrutura do arquivo, metadados extras, etc.
// ...
// Salvar PDF
const pdfBytes = await pdfDoc.save();
return pdfBytes;
}
private drawSection(page: any, lines: string[], startY: number, font: any) {
lines.forEach((line, index) => {
page.drawText(line, {
x: 50,
y: startY - (index * 20),
size: 12,
font,
color: rgb(0, 0, 0),
});
});
}
}
+37
View File
@@ -0,0 +1,37 @@
import axios from "axios";
import { MovimentacaoTribunal } from "@prisma/client";
export class TribunalService {
private apiKey: string;
constructor(apiKey: string) {
this.apiKey = apiKey;
}
async consultarProcesso(
numero: string,
tribunal: string,
): Promise<MovimentacaoTribunal[]> {
const endpoints = {
TJ: "https://api.tribunal.jus.br/processo",
TRT: "https://api.trt.jus.br/processo",
TRF: "https://api.trf.jus.br/processo",
};
try {
const response = await axios.get(`${endpoints[tribunal]}/${numero}`, {
headers: { Authorization: `Bearer ${this.apiKey}` },
});
return this.formatarMovimentacoes(response.data);
} catch (error) {
console.error(`Erro ao consultar ${tribunal}:`, error);
throw error;
}
}
private formatarMovimentacoes(data: any): MovimentacaoTribunal[] {
// Implementar formatação específica para cada tribunal
return [];
}
}
+68
View File
@@ -0,0 +1,68 @@
import axios from 'axios';
export const api = axios.create({
baseURL: '/api',
timeout: 30000, // Aumentar para 30 segundos
timeoutErrorMessage: 'Tempo limite da requisição excedido. Por favor, tente novamente.'
});
// Interceptor para adicionar token em todas as requisições
api.interceptors.request.use(config => {
const token = localStorage.getItem('@AdvX:token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
api.interceptors.response.use(
response => response,
error => {
if (axios.isAxiosError(error)) {
if (error.code === 'ECONNABORTED') {
// Erro específico de timeout
console.error('Timeout da requisição:', {
url: error.config?.url,
method: error.config?.method,
timeout: error.config?.timeout
});
return Promise.reject(new Error('A operação demorou muito para responder. Por favor, tente novamente.'));
}
// Outros erros do Axios
console.error('API Error:', {
message: error.message,
status: error.response?.status,
data: error.response?.data,
url: error.config?.url,
method: error.config?.method
});
} else if (error.response) {
console.error('API Error Response:', {
status: error.response.status,
statusText: error.response.statusText,
data: error.response.data,
url: error.config?.url,
method: error.config?.method
});
} else if (error.request) {
console.error('API Error Request:', error.request);
} else {
console.error('API Error:', error.message);
}
if (error.response && error.response.status === 401) {
// Remover token inválido
localStorage.removeItem('@AdvX:token');
// Redirecionar para login se não estiver na página de login
if (window.location.pathname !== '/login') {
window.location.href = '/login';
}
}
return Promise.reject(error);
}
);
export default api;
+26
View File
@@ -0,0 +1,26 @@
import axios from "axios";
import { DatajudSearchResponse } from "../types/datajud";
export const datajudApi = {
async consultarProcesso(numeroProcesso: string, tribunal: string) {
try {
const response = await axios.post<DatajudSearchResponse>(
"/api/datajud/consulta",
{
numeroProcesso,
tribunal,
},
{
headers: {
"Content-Type": "application/json",
},
},
);
return response.data.hits.hits[0]?._source;
} catch (error) {
console.error("Erro ao consultar DataJud:", error);
throw error;
}
},
};
+60
View File
@@ -0,0 +1,60 @@
import { createClient } from 'webdav';
import { Buffer } from 'buffer';
const client = createClient('https://cloud.martinsevieira.adv.br/remote.php/dav/files/rodrigo/CLIENTES', {
username: 'rodrigo',
password: '34641747'
});
export class NextCloudService {
static async uploadFile(file: Buffer, clienteNome: string, tipo: string = '', atendimentoProtocolo?: string): Promise<string> {
try {
// Limpar nome do cliente para usar como pasta (remover acentos e caracteres especiais)
const pastaCliente = clienteNome
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.toUpperCase()
.replace(/[^a-zA-Z0-9]/g, '_');
// Se tiver protocolo, extrair apenas os números
const pastaProtocolo = atendimentoProtocolo
? atendimentoProtocolo.replace(/\D/g, '')
: '';
// Construir caminho da pasta garantindo a estrutura correta
let pasta = pastaCliente;
if (tipo) pasta += `/${tipo.toLowerCase()}`; // Pasta atendimento em minúsculo
if (pastaProtocolo) pasta += `/${pastaProtocolo}`; // Apenas números do protocolo
console.log('Criando pasta:', pasta); // Debug
// Criar pasta se não existir
await client.createDirectory(pasta, { recursive: true });
// Gerar nome do arquivo com timestamp
const fileName = `${Date.now()}_${file.name.replace(/[^a-zA-Z0-9.-]/g, '_')}`;
const filePath = `${pasta}/${fileName}`;
console.log('Uploadando arquivo:', filePath); // Debug
// Upload do arquivo
await client.putFileContents(filePath, file);
return filePath;
} catch (error) {
console.error('Erro no upload para NextCloud:', error);
throw error;
}
}
static async deleteFile(filePath: string): Promise<void> {
try {
if (await client.exists(filePath)) {
await client.deleteFile(filePath);
}
} catch (error) {
console.error('Erro ao deletar arquivo:', error);
throw new Error('Não foi possível deletar o arquivo do Nextcloud');
}
}
}
+47
View File
@@ -0,0 +1,47 @@
import api from './api';
import { NextCloudService } from './nextcloud';
export async function uploadFile(
file: File,
clienteNome: string,
tipo: string = '',
protocolo?: string
): Promise<any> {
try {
const formData = new FormData();
formData.append('file', file);
formData.append('clienteNome', clienteNome);
if (tipo) formData.append('tipo', tipo);
if (protocolo) formData.append('protocolo', protocolo);
// Fazer upload para o NextCloud
const uploadResponse = await api.post('/atendimentos/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
});
// Criar anexo no banco
const anexoData = {
nome: file.name,
tipo: tipo || 'GERAL',
tamanho: file.size,
url: uploadResponse.data.url
};
const anexoResponse = await api.post('/anexos', anexoData);
return anexoResponse.data;
} catch (error) {
console.error('Erro no upload:', error);
throw new Error('Falha no upload do arquivo');
}
}
export const deleteFile = async (anexoId: string, filePath: string) => {
try {
await NextCloudService.deleteFile(filePath);
await api.delete(`/anexos/${anexoId}`);
} catch (error) {
console.error('Erro ao deletar arquivo:', error);
throw new Error('Falha ao deletar arquivo');
}
};