Spaces:
Sleeping
Sleeping
| import React, { useState, useEffect, useRef } from 'react'; | |
| import { | |
| ShieldCheck, | |
| Cpu, | |
| Key, | |
| Terminal, | |
| Lock, | |
| Loader2, | |
| CheckCircle2, | |
| Zap, | |
| ShieldAlert, | |
| ArrowRight, | |
| Fingerprint, | |
| FileCheck, | |
| UserPlus | |
| } from 'lucide-react'; | |
| import { apiClient } from '../services/api.ts'; | |
| const Login: React.FC = () => { | |
| const [loading, setLoading] = useState(false); | |
| const [isRegistering, setIsRegistering] = useState(false); | |
| const [stage, setStage] = useState<'IDLE' | 'AUTHORIZING' | 'CONSENT' | 'EXCHANGING' | 'VERIFIED'>('IDLE'); | |
| const [logs, setLogs] = useState<string[]>(["LQI Auth Engine v1.3.0 (Persistent DB) initializing..."]); | |
| const [error, setError] = useState<string | null>(null); | |
| const [successMsg, setSuccessMsg] = useState<string | null>(null); | |
| const logEndRef = useRef<HTMLDivElement>(null); | |
| const [params, setParams] = useState({ | |
| username: '', | |
| password: '', | |
| }); | |
| useEffect(() => { | |
| logEndRef.current?.scrollIntoView({ behavior: 'smooth' }); | |
| }, [logs]); | |
| const addLog = (msg: string) => setLogs(prev => [...prev, `${new Date().toLocaleTimeString()} - ${msg}`]); | |
| const handleAuth = async (e: React.FormEvent) => { | |
| e.preventDefault(); | |
| if (loading) return; | |
| setLoading(true); | |
| setError(null); | |
| setSuccessMsg(null); | |
| addLog(`INIT: Establishing ${isRegistering ? 'Registration' : 'Login'} protocol handshake...`); | |
| try { | |
| if (isRegistering) { | |
| const { success, error: regError } = await apiClient.auth.register(params.username, params.password); | |
| if (success) { | |
| addLog("DB_WRITE: New identity node successfully written to disk."); | |
| setSuccessMsg("Registration complete. Initialize handshake to sign in."); | |
| setIsRegistering(false); | |
| } else { | |
| throw new Error(regError || "Registration handshake denied."); | |
| } | |
| } else { | |
| const { success, user, error: loginError } = await apiClient.auth.login(params.username, params.password); | |
| if (success && user) { | |
| addLog("DB_QUERY: Identity parity confirmed via cryptographic hash."); | |
| setStage('VERIFIED'); | |
| setTimeout(() => { | |
| window.dispatchEvent(new Event('auth-update')); | |
| }, 1000); | |
| } else { | |
| throw new Error(loginError || "Handshake rejected by identity node."); | |
| } | |
| } | |
| } catch (err: any) { | |
| addLog(`CRITICAL: ${err.message}`); | |
| setError(err.message); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| return ( | |
| <div className="min-h-screen bg-[#020202] flex items-center justify-center p-6 relative overflow-hidden font-sans"> | |
| <div className="absolute inset-0 z-0 opacity-10"> | |
| <div className="absolute top-0 left-0 w-full h-full bg-[radial-gradient(circle_at_50%_50%,_#1e1b4b_0%,_transparent_50%)]"></div> | |
| <div className="matrix-line"></div> | |
| </div> | |
| <div className="w-full max-w-5xl grid grid-cols-1 lg:grid-cols-2 bg-zinc-950 border border-zinc-900 rounded-[48px] shadow-2xl relative z-10 overflow-hidden backdrop-blur-3xl"> | |
| <div className="p-12 bg-zinc-900/50 flex flex-col justify-between border-r border-zinc-900"> | |
| <div> | |
| <div className="flex items-center space-x-3 mb-8"> | |
| <div className="w-12 h-12 bg-white rounded-2xl flex items-center justify-center shadow-xl"> | |
| <Cpu size={28} className="text-black" /> | |
| </div> | |
| <div> | |
| <h1 className="text-2xl font-black italic tracking-tighter text-white uppercase leading-none"> | |
| Lumina <span className="text-blue-500 not-italic">Quantum</span> | |
| </h1> | |
| <p className="text-[10px] uppercase tracking-[0.4em] font-bold text-zinc-500">Identity v1.3.0</p> | |
| </div> | |
| </div> | |
| <div className="space-y-6"> | |
| <h2 className="text-4xl font-black text-white leading-tight tracking-tighter uppercase italic"> | |
| {isRegistering ? 'Register' : 'Handshake'} <br /> | |
| <span className="text-blue-500 not-italic">Protocol</span> | |
| </h2> | |
| <p className="text-zinc-500 text-sm leading-relaxed max-w-xs font-medium italic"> | |
| Establish a secure session fabric using persistent node storage and RSA-OAEP-4096 hashing. | |
| </p> | |
| </div> | |
| </div> | |
| <div className="mt-10"> | |
| <div className="flex items-center gap-2 mb-4"> | |
| <Terminal size={14} className="text-blue-500" /> | |
| <span className="text-[9px] font-black uppercase text-zinc-600 tracking-widest">Quantum Trace Stream</span> | |
| </div> | |
| <div className="h-48 bg-black/80 rounded-3xl p-6 overflow-y-auto font-mono text-[10px] border border-zinc-900 custom-scrollbar"> | |
| {logs.map((log, i) => ( | |
| <div key={i} className={`mb-1 ${ | |
| log.includes('SUCCESS') || log.includes('DB_WRITE') ? 'text-emerald-400 font-bold' : | |
| log.includes('INIT') || log.includes('DB_QUERY') ? 'text-blue-400' : | |
| log.includes('CRITICAL') ? 'text-rose-500' : 'text-zinc-600' | |
| }`}> | |
| {log} | |
| </div> | |
| ))} | |
| <div ref={logEndRef} /> | |
| </div> | |
| </div> | |
| </div> | |
| <div className="p-12 flex flex-col justify-center bg-black/20"> | |
| {stage === 'VERIFIED' ? ( | |
| <div className="text-center animate-in zoom-in-95 duration-500"> | |
| <div className="w-24 h-24 bg-emerald-500/10 border border-emerald-500/20 rounded-full flex items-center justify-center mx-auto mb-6 shadow-[0_0_50px_rgba(16,185,129,0.2)]"> | |
| <CheckCircle2 size={48} className="text-emerald-500" /> | |
| </div> | |
| <h3 className="text-2xl font-black text-white uppercase italic tracking-tighter mb-2">Authenticated</h3> | |
| <p className="text-zinc-500 text-[10px] font-black uppercase tracking-[0.4em] animate-pulse">Initializing Subspace Ledger...</p> | |
| </div> | |
| ) : ( | |
| <div className="animate-in fade-in duration-500"> | |
| <div className="mb-10 flex items-center justify-between"> | |
| <h3 className="text-2xl font-black text-white uppercase italic tracking-tighter"> | |
| {isRegistering ? 'Create Identity' : 'Identity Core'} | |
| </h3> | |
| <div className="flex items-center gap-2 px-3 py-1 bg-blue-600/10 border border-blue-500/20 rounded-full"> | |
| <Fingerprint size={12} className="text-blue-500" /> | |
| <span className="text-[9px] font-black text-blue-500 uppercase">Secure Link</span> | |
| </div> | |
| </div> | |
| <form onSubmit={handleAuth} className="space-y-6"> | |
| <div className="space-y-4"> | |
| <div className="space-y-2"> | |
| <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Universal UID</label> | |
| <div className="relative"> | |
| <Key className="absolute left-4 top-1/2 -translate-y-1/2 text-zinc-700" size={16} /> | |
| <input | |
| value={params.username} | |
| onChange={(e) => setParams({...params, username: e.target.value})} | |
| className="w-full bg-black border border-zinc-800 rounded-2xl py-4 pl-12 pr-4 text-white font-mono text-sm outline-none focus:border-blue-500 transition-all shadow-inner" | |
| placeholder="Node Identifier" | |
| required | |
| /> | |
| </div> | |
| </div> | |
| <div className="space-y-2"> | |
| <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Private Secret</label> | |
| <div className="relative"> | |
| <Lock className="absolute left-4 top-1/2 -translate-y-1/2 text-zinc-700" size={16} /> | |
| <input | |
| type="password" | |
| value={params.password} | |
| onChange={(e) => setParams({...params, password: e.target.value})} | |
| className="w-full bg-black border border-zinc-800 rounded-2xl py-4 pl-12 pr-4 text-white font-mono text-sm outline-none focus:border-blue-500 transition-all shadow-inner" | |
| placeholder="••••••••" | |
| required | |
| /> | |
| </div> | |
| </div> | |
| </div> | |
| {error && ( | |
| <div className="flex items-center gap-3 text-rose-500 text-xs p-4 bg-rose-500/10 rounded-2xl border border-rose-500/20 animate-pulse"> | |
| <ShieldAlert size={16} /> {error} | |
| </div> | |
| )} | |
| {successMsg && ( | |
| <div className="flex items-center gap-3 text-emerald-500 text-xs p-4 bg-emerald-500/10 rounded-2xl border border-emerald-500/20"> | |
| <CheckCircle2 size={16} /> {successMsg} | |
| </div> | |
| )} | |
| <button | |
| type="submit" | |
| disabled={loading} | |
| className="w-full bg-blue-600 hover:bg-blue-500 text-white rounded-[2rem] py-5 font-black text-xs uppercase tracking-[0.3em] transition-all flex items-center justify-center gap-3 shadow-xl group disabled:opacity-50" | |
| > | |
| {loading ? ( | |
| <> | |
| <Loader2 className="animate-spin" size={18} /> | |
| <span>Processing...</span> | |
| </> | |
| ) : ( | |
| <> | |
| <span>{isRegistering ? 'Register Node' : 'Initialize Handshake'}</span> | |
| {isRegistering ? <UserPlus size={18} /> : <ArrowRight size={18} className="group-hover:translate-x-1 transition-transform" />} | |
| </> | |
| )} | |
| </button> | |
| <button | |
| type="button" | |
| onClick={() => { | |
| setIsRegistering(!isRegistering); | |
| setError(null); | |
| setSuccessMsg(null); | |
| }} | |
| className="w-full text-[10px] font-black text-zinc-600 hover:text-white uppercase tracking-widest transition-colors" | |
| > | |
| {isRegistering ? 'Abort Registration / Return to Hub' : 'Request New Identity Node'} | |
| </button> | |
| </form> | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| export default Login; | |