'use client'; import { useState, useEffect } from 'react'; import { Search, Copy, Check, Hash, Key, AlertCircle, Loader2, Database } from 'lucide-react'; interface SearchResult { found: boolean; hashType?: string; hash?: string; isPlaintext?: boolean; plaintext?: string; wasGenerated?: boolean; hashes?: { md5: string; sha1: string; sha256: string; sha512: string; bcrypt: string; }; results?: Array<{ plaintext: string; hashes: { md5: string; sha1: string; sha256: string; sha512: string; bcrypt: string; }; }>; message?: string; } interface IndexStats { documentCount: number; indexSize: number; } function formatBytes(bytes: number): string { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } function formatNumber(num: number): string { return num.toLocaleString(); } export default function Home() { const [query, setQuery] = useState(''); const [result, setResult] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const [copiedField, setCopiedField] = useState(null); const [stats, setStats] = useState(null); useEffect(() => { const fetchStats = async () => { try { const response = await fetch('/api/health'); if (response.ok) { const data = await response.json(); if (data.index?.stats) { setStats(data.index.stats); } } } catch (_err) { // Silently fail - stats are not critical } }; fetchStats(); }, [result]); // Refresh stats after each search result const handleSearch = async (e: React.FormEvent) => { e.preventDefault(); if (!query.trim()) return; setLoading(true); setError(''); setResult(null); try { const response = await fetch('/api/search', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: query.trim() }) }); if (!response.ok) { throw new Error('Search failed'); } const data = await response.json(); setResult(data); } catch (_err) { setError('Failed to perform search. Please check your connection.'); } finally { setLoading(false); } }; const copyToClipboard = (text: string, field: string) => { navigator.clipboard.writeText(text); setCopiedField(field); setTimeout(() => setCopiedField(null), 2000); }; const HashDisplay = ({ label, value, field }: { label: string; value: string; field: string }) => (
{label}
{value}
); return (
{/* Header */}

Hasher

Search for hashes or generate them from plaintext

Supports MD5, SHA1, SHA256, SHA512, and Bcrypt

{stats && (
{formatNumber(stats.documentCount)} hashes
{formatBytes(stats.indexSize)} indexed
)}
{/* Search Form */}
setQuery(e.target.value)} placeholder="Enter a hash or plaintext..." className="w-full px-6 py-4 pr-14 text-lg rounded-2xl border-2 border-gray-200 focus:border-blue-500 focus:ring-4 focus:ring-blue-100 outline-none transition-all shadow-sm" />
{/* Error Message */} {error && (

Error

{error}

)} {/* Results */} {result && (
{result.isPlaintext ? ( <>

Generated Hashes

For plaintext: {result.plaintext}

{result.wasGenerated && (

✨ These hashes have been saved to the database for future lookups.

)} ) : result.found && result.results && result.results.length > 0 ? ( <>

Hash Found!

Type: {result.hashType}

{result.results.map((item, idx) => (
Plaintext
{item.plaintext}
))} ) : ( <>

Hash Not Found

Type: {result.hashType}

This hash is not in our database. Try searching with plaintext to generate hashes.

)}
)} {/* Info Cards */} {!result && !loading && (

Search Hashes

Enter a hash to find its original plaintext value. Our database contains commonly used words and phrases.

Generate Hashes

Enter any plaintext to instantly generate MD5, SHA1, SHA256, SHA512, and Bcrypt hashes. Results are saved automatically.

)} {/* Footer */}
); }