297 lines
8.1 KiB
TypeScript
297 lines
8.1 KiB
TypeScript
import express from 'express';
|
|
import { WebProofService } from '../services/WebProofService';
|
|
import { prisma } from '../prismaClient';
|
|
import * as path from 'path';
|
|
import * as fs from 'fs';
|
|
|
|
const router = express.Router();
|
|
const webProofService = new WebProofService();
|
|
|
|
// Criar nova sessão de captura web
|
|
router.post('/sessions', async (req, res) => {
|
|
try {
|
|
const {
|
|
sessionId,
|
|
targetUrl,
|
|
description,
|
|
metadata,
|
|
environment,
|
|
screenshots
|
|
} = req.body;
|
|
|
|
// Validar dados de entrada
|
|
if (!sessionId || !targetUrl || !metadata || !environment) {
|
|
return res.status(400).json({
|
|
error: 'Dados obrigatórios não fornecidos',
|
|
required: ['sessionId', 'targetUrl', 'metadata', 'environment']
|
|
});
|
|
}
|
|
|
|
// Validar URL
|
|
try {
|
|
new URL(targetUrl);
|
|
} catch {
|
|
return res.status(400).json({ error: 'URL inválida fornecida' });
|
|
}
|
|
|
|
// Coletar informações de rede do cliente
|
|
const clientInfo = {
|
|
ip: req.headers['x-forwarded-for']?.toString().split(',')[0].trim() ||
|
|
req.ip ||
|
|
req.socket.remoteAddress ||
|
|
'127.0.0.1',
|
|
port: req.socket.remotePort || 0,
|
|
userAgent: req.headers['user-agent'] || '',
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
|
|
// Processar sessão de captura
|
|
const result = await webProofService.processWebCapture({
|
|
sessionId,
|
|
targetUrl,
|
|
description,
|
|
metadata: {
|
|
...metadata,
|
|
clientInfo,
|
|
serverInfo: {
|
|
hostname: require('os').hostname(),
|
|
nodeVersion: process.version,
|
|
timestamp: new Date().toISOString()
|
|
}
|
|
},
|
|
environment,
|
|
screenshots: screenshots || []
|
|
});
|
|
|
|
res.json({
|
|
success: true,
|
|
sessionId: result.id,
|
|
status: result.status,
|
|
message: 'Sessão de captura processada com sucesso'
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Erro ao processar sessão de captura:', error);
|
|
res.status(500).json({
|
|
error: 'Falha ao processar sessão de captura',
|
|
details: error.message
|
|
});
|
|
}
|
|
});
|
|
|
|
// Obter status da sessão
|
|
router.get('/sessions/:sessionId/status', async (req, res) => {
|
|
try {
|
|
const { sessionId } = req.params;
|
|
|
|
const session = await prisma.webProofSession.findUnique({
|
|
where: { sessionId }
|
|
});
|
|
|
|
if (!session) {
|
|
return res.status(404).json({ error: 'Sessão não encontrada' });
|
|
}
|
|
|
|
res.json({
|
|
sessionId: session.sessionId,
|
|
status: session.status,
|
|
targetUrl: session.targetUrl,
|
|
description: session.description,
|
|
createdAt: session.createdAt,
|
|
updatedAt: session.updatedAt,
|
|
reportPath: session.reportPath,
|
|
error: session.error
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Erro ao buscar status da sessão:', error);
|
|
res.status(500).json({ error: 'Erro ao buscar status da sessão' });
|
|
}
|
|
});
|
|
|
|
// Obter timestamp confiável
|
|
router.get('/trusted-timestamp', async (req, res) => {
|
|
try {
|
|
// Simular obtenção de timestamp de autoridade certificadora
|
|
const trustedTimestamp = await webProofService.getTrustedTimestamp();
|
|
|
|
res.json({
|
|
timestamp: trustedTimestamp.timestamp,
|
|
authority: trustedTimestamp.authority,
|
|
signature: trustedTimestamp.signature
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Erro ao obter timestamp confiável:', error);
|
|
res.status(500).json({
|
|
error: 'Erro ao obter timestamp confiável',
|
|
fallback: new Date().toISOString()
|
|
});
|
|
}
|
|
});
|
|
|
|
// Obter informações do cliente
|
|
router.get('/client-info', async (req, res) => {
|
|
try {
|
|
const clientInfo = {
|
|
ip: req.headers['x-forwarded-for']?.toString().split(',')[0].trim() ||
|
|
req.ip ||
|
|
req.socket.remoteAddress ||
|
|
'127.0.0.1',
|
|
port: req.socket.remotePort || 0,
|
|
userAgent: req.headers['user-agent'] || '',
|
|
timestamp: new Date().toISOString(),
|
|
headers: {
|
|
'x-forwarded-for': req.headers['x-forwarded-for'],
|
|
'user-agent': req.headers['user-agent'],
|
|
'accept-language': req.headers['accept-language']
|
|
}
|
|
};
|
|
|
|
res.json(clientInfo);
|
|
|
|
} catch (error) {
|
|
console.error('Erro ao obter informações do cliente:', error);
|
|
res.status(500).json({
|
|
error: 'Erro ao obter informações do cliente',
|
|
fallback: {
|
|
ip: '127.0.0.1',
|
|
port: 0,
|
|
timestamp: new Date().toISOString()
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// Verificar autenticidade de captura web com detalhes aprimorados
|
|
router.get('/verify/:sessionId', async (req, res) => {
|
|
try {
|
|
const { sessionId } = req.params;
|
|
|
|
if (!sessionId || typeof sessionId !== 'string') {
|
|
return res.status(400).json({
|
|
verified: false,
|
|
error: 'SessionId inválido'
|
|
});
|
|
}
|
|
|
|
const verification = await webProofService.verifyWebCapture(sessionId);
|
|
|
|
res.json({
|
|
verified: verification.verified,
|
|
sessionId,
|
|
sessionValid: verification.sessionValid || false,
|
|
contentValid: verification.contentValid || false,
|
|
timestampValid: verification.timestampValid || false,
|
|
verificationDetails: verification.details,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Erro ao verificar captura:', error);
|
|
res.status(500).json({
|
|
verified: false,
|
|
sessionValid: false,
|
|
contentValid: false,
|
|
timestampValid: false,
|
|
error: 'Erro ao verificar captura web'
|
|
});
|
|
}
|
|
});
|
|
|
|
// Baixar relatório com validação aprimorada
|
|
router.get('/relatorio/:sessionId', async (req, res) => {
|
|
try {
|
|
const { sessionId } = req.params;
|
|
|
|
if (!sessionId || typeof sessionId !== 'string') {
|
|
return res.status(400).json({ error: 'SessionId inválido' });
|
|
}
|
|
|
|
const session = await prisma.webProofSession.findUnique({
|
|
where: { sessionId }
|
|
});
|
|
|
|
if (!session) {
|
|
return res.status(404).json({ error: 'Sessão não encontrada' });
|
|
}
|
|
|
|
if (session.status !== 'CONCLUIDO') {
|
|
return res.status(400).json({
|
|
error: 'Relatório ainda não está disponível',
|
|
status: session.status
|
|
});
|
|
}
|
|
|
|
const reportPath = path.join(process.cwd(), 'storage', 'web-proofs', `${sessionId}_report.pdf`);
|
|
|
|
if (!fs.existsSync(reportPath)) {
|
|
console.error('Arquivo de relatório não encontrado:', reportPath);
|
|
return res.status(404).json({ error: 'Arquivo do relatório não encontrado' });
|
|
}
|
|
|
|
res.setHeader('Content-Type', 'application/pdf');
|
|
res.setHeader('Content-Disposition', `inline; filename="captura_web_${sessionId}_relatorio.pdf"`);
|
|
res.setHeader('Cache-Control', 'public, max-age=3600'); // Cache de 1 hora
|
|
|
|
const fileStream = fs.createReadStream(reportPath);
|
|
fileStream.on('error', (error) => {
|
|
console.error('Erro ao ler arquivo de relatório:', error);
|
|
if (!res.headersSent) {
|
|
res.status(500).json({ error: 'Erro ao ler arquivo de relatório' });
|
|
}
|
|
});
|
|
|
|
fileStream.pipe(res);
|
|
|
|
} catch (error) {
|
|
console.error('Erro ao servir relatório:', error);
|
|
if (!res.headersSent) {
|
|
res.status(500).json({ error: 'Erro ao gerar relatório' });
|
|
}
|
|
}
|
|
});
|
|
|
|
// Listar sessões com paginação
|
|
router.get('/admin/sessions', async (req, res) => {
|
|
try {
|
|
const page = parseInt(req.query.page as string) || 1;
|
|
const limit = parseInt(req.query.limit as string) || 100;
|
|
const offset = (page - 1) * limit;
|
|
|
|
const [sessions, total] = await Promise.all([
|
|
prisma.webProofSession.findMany({
|
|
orderBy: { createdAt: 'desc' },
|
|
skip: offset,
|
|
take: limit,
|
|
select: {
|
|
sessionId: true,
|
|
targetUrl: true,
|
|
description: true,
|
|
status: true,
|
|
createdAt: true,
|
|
processedAt: true,
|
|
error: true
|
|
}
|
|
}),
|
|
prisma.webProofSession.count()
|
|
]);
|
|
|
|
res.json({
|
|
sessions,
|
|
pagination: {
|
|
page,
|
|
limit,
|
|
total,
|
|
totalPages: Math.ceil(total / limit)
|
|
}
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Erro ao listar sessões:', error);
|
|
res.status(500).json({ error: 'Erro ao listar sessões' });
|
|
}
|
|
});
|
|
|
|
export default router;
|