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'; const router = express.Router(); const storage = multer.memoryStorage(); const upload = multer({ storage }); const provaService = new ProvaService(); // Upload de arquivo para validação router.post('/upload', upload.array('files'), async (req, res) => { try { 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' }); } // 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; // Metadata avançada seguindo RFC 3227 const metadata = { userAgent: req.body.user_agent, language: req.body.language, viewportWidth: req.body.viewport_width, viewportHeight: req.body.viewport_height, ipAddress: req.ip || req.socket.remoteAddress, clientPort: req.socket.remotePort || 'desconhecida', connectionInfo: { ip: req.ip || req.socket.remoteAddress, forwardedIp: forwarded, socketAddress: socketInfo?.address, socketPort: socketInfo?.port, remoteFamily: req.socket.remoteFamily }, timestamp: new Date(), modification_times: req.body.modification_times, referrer: req.body.referrer, timezone: req.body.timezone, timezone_offset: req.body.timezone_offset, platform: req.body.platform, cookies_enabled: req.body.cookies_enabled, browser_plugins: req.body.browser_plugins, latitude: req.body.latitude, longitude: req.body.longitude, 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'], }, server: { timestamp: new Date().toISOString(), 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); // Adicionar informações de segurança const securityInfo = { requestMethod: req.method, protocol: req.protocol, isSecure: req.secure, originalUrl: req.originalUrl, contentType: req.headers['content-type'], }; console.log('Informações de segurança:', securityInfo); // Processar cada arquivo const results = []; for (const file of req.files as Express.Multer.File[]) { 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; if (modificationTime) { try { // Converter timestamp para objeto Date const timestamp = parseInt(modificationTime); if (!isNaN(timestamp)) { fileModifiedDate = new Date(timestamp); } } catch (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, modified: fileModifiedDate ? fileModifiedDate.toISOString() : null, modifiedTimestamp: modificationTime }, 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 }); 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 }); } } res.json({ success: true, results, id: results[0].id }); } catch (error) { 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) => { try { const { id } = req.params; const prova = await prisma.prova.findUnique({ where: { id } }); if (!prova) { 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' }); } }); // Rota de verificação router.get('/verificar/:hash', async (req, res) => { try { const { hash } = req.params; // Coletar informações de conexão do cliente const clientInfo = { ipAddress: req.ip, remoteAddress: req.socket?.remoteAddress, headers: req.headers, userAgent: req.headers['user-agent'], verificadoEm: new Date().toISOString() }; // Instanciar o serviço e verificar a prova const provaService = new ProvaService(); const result = await provaService.verifyFile(hash); // Registrar a verificação no log de acessos (opcional) try { await prisma.verificacaoProva.create({ data: { provaId: result.prova?.id, hash, ipAddress: req.headers['x-forwarded-for'] || req.ip || '127.0.0.1', userAgent: req.headers['user-agent'] || 'Desconhecido', timestamp: new Date(), success: result.verified, metadata: clientInfo } }); console.log('Verificação registrada com sucesso'); } catch (logError) { console.error('Erro ao registrar verificação:', logError); // Não interromper o fluxo por erro de log } // Retornar os dados da verificação return res.status(200).json({ ...result, verificacao: { timestamp: new Date(), ip: req.headers['x-forwarded-for'] || req.ip || '127.0.0.1' } }); } catch (error) { 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) => { try { const { hash } = req.params; // Buscar a prova pelo hash const prova = await prisma.prova.findFirst({ where: { checksumSha256: hash } }); 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`); // 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' }); } // Enviar o arquivo como resposta 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' }); } }); // Listar provas (admin) router.get('/admin', async (req, res) => { try { const provas = await prisma.prova.findMany({ orderBy: { dataCriacao: 'desc' } }); res.json(provas); } catch (error) { console.error('Erro ao listar provas:', error); res.status(500).json({ error: 'Erro ao listar provas' }); } }); export default router;