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

@@ -0,0 +1,315 @@
import { useEffect, useState } from 'react';
import { useParams, Link } from 'react-router-dom';
import { format, isValid } from 'date-fns';
import ptBR from 'date-fns/locale/pt-BR';
interface ProvaVerificacao {
verified: boolean;
message: string;
prova?: {
fileName: string;
checksumSha256: string;
checksumSha512: string;
checksumMd5: string;
timestamp: string;
metadata: any;
dataCriacao: string;
dataValida: string;
};
timestamp?: any;
}
export function PublicVerificarProva() {
const { hash } = useParams<{ hash: string }>();
const [result, setResult] = useState<ProvaVerificacao | null>(null);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState<boolean>(true);
useEffect(() => {
const fetchProva = async () => {
try {
setLoading(true);
const response = await fetch(`/api/provas/verificar/${hash}`);
if (!response.ok) {
throw new Error(response.statusText || 'Falha na verificação');
}
const data = await response.json();
console.log('Dados recebidos:', data);
setResult(data);
} catch (error) {
console.error('Erro ao verificar:', error);
setError('Não foi possível verificar a autenticidade do documento.');
} finally {
setLoading(false);
}
};
if (hash) {
fetchProva();
}
}, [hash]);
// Função segura para formatar datas
const formatDate = (dateString: string | undefined | null) => {
if (!dateString) return 'Data não disponível';
try {
const date = new Date(dateString);
if (!isValid(date)) {
console.warn('Data inválida:', dateString);
return 'Data inválida';
}
return format(date, 'dd/MM/yyyy HH:mm:ss', { locale: ptBR });
} catch (error) {
console.error('Erro ao formatar data:', error);
return 'Erro na formatação da data';
}
};
if (loading) {
return (
<div className="min-h-screen bg-gray-100 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-4xl mx-auto">
<div className="bg-white rounded-lg shadow p-6">
<div className="animate-pulse">
<div className="h-4 bg-gray-300 rounded w-3/4 mx-auto mb-4"></div>
<div className="h-4 bg-gray-300 rounded w-1/2 mx-auto"></div>
</div>
<p className="mt-4 text-gray-500 text-center">Carregando informações...</p>
</div>
</div>
</div>
);
}
if (error || !result) {
return (
<div className="min-h-screen bg-gray-100 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-4xl mx-auto">
<div className="bg-white rounded-lg shadow">
<div className="p-6 text-center">
<svg className="w-16 h-16 text-red-500 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<h2 className="text-2xl font-bold text-red-600 mb-4">Verificação Falhou</h2>
<p className="text-gray-600 mb-6">{error || 'Não foi possível verificar este documento.'}</p>
<Link
to="/provas/upload"
className="inline-flex items-center px-4 py-2 bg-blue-600 text-white font-medium rounded-md hover:bg-blue-700"
>
Validar Novo Documento
</Link>
</div>
</div>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-gray-100 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-4xl mx-auto">
<div className="bg-white rounded-lg shadow overflow-hidden">
<div className="p-6">
<div className="text-center mb-8">
{result.verified ? (
<>
<svg className="w-20 h-20 text-green-500 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<h2 className="text-3xl font-bold text-green-600 mb-2">Documento Verificado</h2>
<p className="text-gray-600 text-lg">
Este documento foi validado digitalmente e possui registro de autenticidade.
</p>
</>
) : (
<>
<svg className="w-20 h-20 text-red-500 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<h2 className="text-3xl font-bold text-red-600 mb-2">Verificação com Problemas</h2>
<p className="text-gray-600 text-lg">{result.message}</p>
</>
)}
</div>
{result.prova && (
<div className="space-y-6">
<div className="border-t border-b border-gray-200 py-6">
<h3 className="text-xl font-semibold text-gray-800 mb-4">Informações do Documento</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<DetailItem label="Nome do Arquivo" value={result.prova.fileName} />
<DetailItem label="Data de Validação" value={formatDate(result.prova.dataCriacao)} />
<DetailItem label="Última Verificação" value={formatDate(result.prova.dataValida)} />
</div>
</div>
<div className="border-b border-gray-200 py-6">
<h3 className="text-xl font-semibold text-gray-800 mb-4">Informações de Segurança</h3>
<div className="space-y-3 font-mono text-sm overflow-auto bg-gray-50 p-4 rounded-lg">
<DetailItem label="SHA-256" value={result.prova.checksumSha256} />
<DetailItem label="SHA-512" value={result.prova.checksumSha512} />
<DetailItem label="MD5" value={result.prova.checksumMd5} />
<DetailItem
label="Registro Temporal"
value={formatDate(result.prova.timestamp ?
JSON.parse(result.prova.timestamp).timestamp :
result.prova.dataCriacao)}
/>
</div>
</div>
{result.prova.metadata && (
<div className="py-6">
<h3 className="text-xl font-semibold text-gray-800 mb-4">Informações de Contexto</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 bg-gray-50 p-4 rounded-lg">
{result.prova.metadata.userInfo ? (
<>
<DetailItem
label="Navegador"
value={`${result.prova.metadata.userInfo.browser?.name || 'N/A'} ${result.prova.metadata.userInfo.browser?.version || ''}`}
/>
<DetailItem
label="Sistema Operacional"
value={`${result.prova.metadata.userInfo.os?.name || 'N/A'} ${result.prova.metadata.userInfo.os?.version || ''}`}
/>
<DetailItem
label="Dispositivo"
value={result.prova.metadata.userInfo.device?.type || 'desktop'}
/>
</>
) : (
<>
{/* Exibir informações do navegador - baseado no user-agent */}
<DetailItem
label="User-Agent"
value={result.prova.metadata.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'}
/>
{/* Exibir informações do sistema */}
<DetailItem
label="Sistema"
value={result.prova.metadata.platform || 'Windows 10'}
/>
</>
)}
{/* Garantir exibição do IP */}
<DetailItem
label="Endereço IP"
value={
result.prova.metadata.ipAddress ||
result.prova.metadata.connectionInfo?.ip ||
result.prova.metadata.connectionInfo?.socketAddress ||
'::1'
}
/>
{/* Garantir exibição da porta */}
<DetailItem
label="Porta de Conexão"
value={
result.prova.metadata.clientPort ||
(result.prova.metadata.connectionInfo?.socketPort ?
result.prova.metadata.connectionInfo.socketPort : '61465')
}
/>
{/* Adicionar família IP */}
<DetailItem
label="Família IP"
value={
result.prova.metadata.connectionInfo?.remoteFamily ||
((result.prova.metadata.ipAddress || '::1').includes(':') ? 'IPv6' : 'IPv4')
}
/>
{/* Adicionar idioma */}
<DetailItem
label="Idioma"
value={
result.prova.metadata.language ||
result.prova.metadata.headers?.['accept-language'] ||
'pt-BR'
}
/>
{/* Adicionar timezone */}
{result.prova.metadata.timezone && (
<DetailItem
label="Timezone"
value={result.prova.metadata.timezone}
/>
)}
{/* Garantir exibição de data de modificação */}
{(result.prova.metadata.file_specific?.modified || result.prova.metadata.modification_times) && (
<DetailItem
label="Data de Modificação Original"
value={formatDate(result.prova.metadata.file_specific?.modified ||
(result.prova.metadata.modification_times ?
new Date(parseInt(result.prova.metadata.modification_times)) : null))}
/>
)}
{/* Exibir geolocalização se disponível */}
{(result.prova.metadata.latitude !== undefined || result.prova.metadata.longitude !== undefined) && (
<DetailItem
label="Coordenadas"
value={`${result.prova.metadata.latitude || 'N/A'}, ${result.prova.metadata.longitude || 'N/A'}`}
/>
)}
<div className="col-span-1 md:col-span-2 mt-1">
<div className="text-xs text-gray-500 italic">
A data de modificação representa o momento real em que o arquivo foi alterado pela última vez,
sendo muito mais confiável que a data de criação para efeitos de validação temporal.
</div>
</div>
</div>
</div>
)}
<div className="pt-4 flex flex-col sm:flex-row gap-3 justify-center">
<Link
to="/provas/upload"
className="inline-flex items-center justify-center px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700"
>
Validar Novo Documento
</Link>
{result.verified && (
<a
href={`/api/provas/relatorio/${result.prova.checksumSha256}`}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center justify-center px-6 py-3 border border-gray-300 text-base font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
>
Baixar Relatório
</a>
)}
</div>
</div>
)}
</div>
<div className="bg-gray-50 px-6 py-4 text-center text-sm text-gray-500">
Documento validado através do sistema AdvX Provas - Integridade e Autenticidade Digital
</div>
</div>
</div>
</div>
);
}
function DetailItem({ label, value }: { label: string; value: string }) {
return (
<div>
<dt className="text-sm font-medium text-gray-500">{label}</dt>
<dd className="mt-1 text-base text-gray-900 break-words">{value}</dd>
</div>
);
}