import { useEffect, useState } from "react"; import { api } from "../api.js"; import { setSession } from "../utils/auth.js"; const SQLI_RE = /(\b(select|insert|update|delete|drop|union|exec|cast|convert|declare|xp_|char|nchar|varchar|alter|create|truncate|sleep|benchmark|waitfor|information_schema)\b|--|;|\/\*|\*\/|0x[0-9a-fA-F]+|\bor\b\s+.{0,30}[=<>]|\band\b\s+.{0,30}[=<>])/i; function hasSQLI(val) { return SQLI_RE.test(val); } function getPasswordStrength(pw) { const score = [/.{8,}/, /[A-Z]/, /[a-z]/, /[0-9]/, /[^A-Za-z0-9]/].filter(r => r.test(pw)).length; const widths = ['0%', '25%', '45%', '70%', '100%']; const colors = ['#ef4444', '#f97316', '#eab308', '#22c55e', '#7c3aed']; return { width: widths[score] || '0%', color: colors[score - 1] || '#334155' }; } export default function AuthModal({ open, onClose, onAuthed }) { const [mode, setMode] = useState("login"); // "login" | "register" const [identifier, setIdentifier] = useState(""); const [username, setUsername] = useState(""); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState(""); const [busy, setBusy] = useState(false); const [pwStrength, setPwStrength] = useState({ width: '0%', color: '#334155' }); // Reset state + close on Escape. useEffect(() => { if (!open) return; setError(""); setBusy(false); setPassword(""); const onKey = (e) => e.key === "Escape" && onClose(); window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [open, onClose]); if (!open) return null; async function handleSubmit(e) { e.preventDefault(); setError(""); if (mode === "login") { if (!identifier.trim() || !password) { setError("Enter your username/email and password."); return; } if (hasSQLI(identifier) || hasSQLI(password)) { setError("Invalid input detected."); return; } } else { if (username.trim().length < 3) { setError("Username must be at least 3 characters."); return; } if (username.trim().length > 15) { setError("Username must be 15 characters or less."); return; } if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email.trim())) { setError("Enter a valid email address."); return; } if (email.trim().length > 20) { setError("Email must be 20 characters or less."); return; } if (password.length < 6) { setError("Password must be at least 6 characters."); return; } if (hasSQLI(username) || hasSQLI(email) || hasSQLI(password)) { setError("Invalid input detected."); return; } } setBusy(true); try { const res = mode === "login" ? await api.authLogin(identifier.trim(), password) : await api.authRegister(username.trim(), email.trim(), password); setSession(res.token, res.user); onAuthed?.(res.user); onClose(); } catch (err) { setError(err.message || "Something went wrong. Try again."); } finally { setBusy(false); } } return (
e.stopPropagation()} role="dialog" aria-modal="true">
ANICOVE
{mode === "login" ? ( ) : ( <> )} {error ?
{error}
: null}
OR
{mode === "login" ? (

No account? { setMode("register"); setError(""); }}>Create one

) : (

Already have an account? { setMode("login"); setError(""); }}>Sign in

)}
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
); }