'use client'; import { useState, useEffect, useCallback } from 'react'; import { useSearchParams, useRouter } from 'next/navigation'; import { Search, Copy, Check, Hash, Key, AlertCircle, Loader2, Database, Link } 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; }; results?: Array<{ plaintext: string; hashes: { md5: string; sha1: string; sha256: string; sha512: 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 searchParams = useSearchParams(); const router = useRouter(); 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); const [copiedLink, setCopiedLink] = useState(false); const performSearch = useCallback(async (searchQuery: string) => { if (!searchQuery.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: searchQuery.trim() }) }); if (!response.ok) { throw new Error('Search failed'); } const data = await response.json(); setResult(data); // Update URL with search query const newUrl = new URL(window.location.href); newUrl.searchParams.set('q', searchQuery.trim()); router.replace(newUrl.pathname + newUrl.search, { scroll: false }); } catch (_err) { setError('Failed to perform search. Please check your connection.'); } finally { setLoading(false); } }, [router]); // Load query from URL on mount useEffect(() => { const urlQuery = searchParams.get('q'); if (urlQuery) { setQuery(urlQuery); performSearch(urlQuery); } }, [searchParams, performSearch]); 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(); performSearch(query); }; const copyToClipboard = (text: string, field: string) => { navigator.clipboard.writeText(text); setCopiedField(field); setTimeout(() => setCopiedField(null), 2000); }; const copyShareLink = () => { const url = new URL(window.location.href); url.searchParams.set('q', query.trim()); navigator.clipboard.writeText(url.toString()); setCopiedLink(true); setTimeout(() => setCopiedLink(false), 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, and SHA512

{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-28 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" />
{query.trim() && ( )}
{/* 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, and SHA512 hashes. Results are saved automatically.

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