feat: initial commit - CertConverter PFX & AppWPP (React)

This commit is contained in:
2026-10-15 12:05:56 -03:00
commit 512cb4ea7e
42 changed files with 7150 additions and 0 deletions
+243
View File
@@ -0,0 +1,243 @@
import React, { useState } from 'react';
import {
ShieldCheck,
ArrowRight,
RotateCcw,
AlertCircle,
Sparkles,
Lock,
Loader2,
MessageSquare
} from 'lucide-react';
import Header from './components/Header';
import SecurityBadge from './components/SecurityBadge';
import UploadZone from './components/UploadZone';
import PasswordInput from './components/PasswordInput';
import CertificateInfo from './components/CertificateInfo';
import OutputFiles from './components/OutputFiles';
import OpenSslReference from './components/OpenSslReference';
import HostGuideModal from './components/HostGuideModal';
import AppWPP from './components/wpp/AppWPP';
import { convertPfx } from './utils/certConverter';
export default function App() {
const [activeTab, setActiveTab] = useState('cert'); // 'cert' | 'wpp'
const [file, setFile] = useState(null);
const [arrayBuffer, setArrayBuffer] = useState(null);
const [baseName, setBaseName] = useState('certificado');
const [password, setPassword] = useState('');
const [isConverting, setIsConverting] = useState(false);
const [conversionResult, setConversionResult] = useState(null);
const [errorMessage, setErrorMessage] = useState('');
const [isHostingGuideOpen, setIsHostingGuideOpen] = useState(false);
// Manipula seleção de arquivo .pfx
const handleFileSelected = (selectedFile) => {
setFile(selectedFile);
setConversionResult(null);
setErrorMessage('');
// Extrai nome base sem a extensão
const nameWithoutExt = selectedFile.name.replace(/\.[^/.]+$/, '');
setBaseName(nameWithoutExt || 'certificado');
// Lê os bytes brutos do arquivo
const reader = new FileReader();
reader.onload = (e) => {
setArrayBuffer(e.target.result);
};
reader.onerror = () => {
setErrorMessage('Falha ao ler o arquivo selecionado no seu computador.');
};
reader.readAsArrayBuffer(selectedFile);
};
// Limpa tudo para recomeçar
const handleFileCleared = () => {
setFile(null);
setArrayBuffer(null);
setPassword('');
setConversionResult(null);
setErrorMessage('');
setBaseName('certificado');
};
// Executa a conversão criptográfica
const handleConvert = () => {
if (!arrayBuffer) {
setErrorMessage('Por favor, selecione um arquivo de certificado .pfx primeiro.');
return;
}
setIsConverting(true);
setErrorMessage('');
// Executa em pequeno timeout para permitir renderizar o estado de carregamento
setTimeout(() => {
try {
const result = convertPfx(arrayBuffer, password);
if (result.success) {
setConversionResult(result);
setErrorMessage('');
} else {
setConversionResult(null);
setErrorMessage(result.error);
}
} catch (err) {
setConversionResult(null);
setErrorMessage('Ocorreu um erro inesperado ao converter: ' + (err.message || ''));
} finally {
setIsConverting(false);
}
}, 80);
};
return (
<div className="app-layout">
{/* Barra de Navegação Superior com Alternador de Apps */}
<Header
activeTab={activeTab}
onSelectTab={setActiveTab}
onOpenHostingGuide={() => setIsHostingGuideOpen(true)}
/>
{/* Conteúdo Principal */}
<main className="main-content">
{activeTab === 'cert' ? (
<div className="container">
{/* Banner de Segurança & Privacidade */}
<SecurityBadge />
{/* Card Principal de Conversão */}
<section className="card-converter">
<div className="card-header-bar">
<div className="card-header-title">
<Sparkles size={20} className="text-cyan" />
<h2>Conversão de Certificado Digital (.pfx)</h2>
</div>
<span className="step-badge">Etapa 1 de 2</span>
</div>
{/* Zona de Upload / Drag & Drop */}
<UploadZone
file={file}
onFileSelected={handleFileSelected}
onFileCleared={handleFileCleared}
/>
{/* Campo de Senha (habilitado quando há arquivo) */}
{file && (
<div className="form-controls-wrapper">
<PasswordInput
password={password}
onChange={setPassword}
onSubmit={handleConvert}
disabled={isConverting}
/>
{/* Botões de Ação */}
<div className="actions-bar">
<button
type="button"
className="btn-convert"
onClick={handleConvert}
disabled={isConverting}
>
{isConverting ? (
<>
<Loader2 size={18} className="animate-spin" />
<span>Processando Criptografia...</span>
</>
) : (
<>
<Lock size={18} />
<span>Converter para .CRT, .PEM e .KEY</span>
<ArrowRight size={18} />
</>
)}
</button>
<button
type="button"
className="btn-reset"
onClick={handleFileCleared}
disabled={isConverting}
title="Limpar e selecionar outro arquivo"
>
<RotateCcw size={16} />
<span>Limpar</span>
</button>
</div>
</div>
)}
{/* Mensagem de Erro amigável */}
{errorMessage && (
<div className="alert-error-box">
<AlertCircle size={20} />
<div className="alert-error-text">
<strong>Atenção:</strong> {errorMessage}
</div>
</div>
)}
</section>
{/* Exibição dos Resultados após conversão */}
{conversionResult && (
<section className="results-container">
{/* Metadados e Validade do Certificado */}
<CertificateInfo metadata={conversionResult.metadata} />
{/* Arquivos Gerados com Abas, Cópia e Download */}
<OutputFiles
results={conversionResult}
baseName={baseName}
metadata={conversionResult.metadata}
/>
{/* Referência Técnica dos Comandos OpenSSL equivalentes */}
<OpenSslReference baseName={baseName} />
</section>
)}
</div>
) : (
/* Aplicativo WPPConnect Integrado */
<div className="wpp-view-wrapper">
<AppWPP />
</div>
)}
</main>
{/* Rodapé Informativo */}
<footer className="footer">
<div className="footer-content">
<p>
{activeTab === 'cert'
? 'CertConverter Web — Conversão PKCS#12 / X.509 em conformidade com padrões ICP-Brasil e OpenSSL.'
: 'AppWPP Web — Gerenciador de Sessões WPPConnect integrado ao portal de utilitários.'}
</p>
<div className="footer-links">
<button
type="button"
className="link-plain"
onClick={() => setIsHostingGuideOpen(true)}
>
Como Hospedar esta Aplicação
</button>
<span>•</span>
<span className="footer-shield">
<ShieldCheck size={14} /> 100% no cliente (Executa em qualquer navegador)
</span>
</div>
</div>
</footer>
{/* Modal com Guia Passo a Passo de Hospedagem */}
<HostGuideModal
isOpen={isHostingGuideOpen}
onClose={() => setIsHostingGuideOpen(false)}
/>
</div>
);
}