/* FoxBot Pro — Auth wizard (design ZIP câblé sur les vrais endpoints). Flux : auth(login/signup) -> [signup] okx -> telegram -> webauthn -> recap -> app. Le login entre direct dans l'app. Le signup lance l'onboarding (flag STORE.ONBOARDING qui retient le wizard monté malgré l'auto-login). Exposé en window.AuthWizard. */ (function () { const { useState, useEffect, useRef } = React; const I = { Mail: () => , Lock: () => , User: () => , Key: () => , Eye: () => , EyeOff:() => , Arrow: () => , ArrowL:() => , Shield:() => , Check: () => , Copy: () => , Tg: () => , Finger:() => , }; function Field({ label, type = "text", placeholder, icon, value, onChange, autoComplete, noFill }) { const [show, setShow] = useState(false); const [ro, setRo] = useState(!!noFill); // readonly au montage = bloque l'autofill du navigateur const isPwd = type === "password"; const guard = noFill ? { autoComplete: "off", readOnly: ro, onFocus: () => setRo(false), name: "fld_" + String(label || "x").replace(/[^a-z]/gi, ""), "data-lpignore": "true", "data-1p-ignore": "true" } : { autoComplete }; return (
{icon && {icon}} onChange(e.target.value)} style={isPwd ? { paddingRight: 44 } : undefined} /> {isPwd && }
); } const Skip = ({ children, onClick }) => ; function Stepper({ current }) { return (
{[1, 2, 3].map((n, i) => (
{n < current ? : n}
{i < 2 &&
} ))}
); } const StepHeader = ({ badge, title, sub }) => (
{badge &&
{badge}
}

{title}

{sub &&

{sub}

}
); /* ─── STEP 1 : OKX ─── */ function StepOKX({ onNext, onSkip }) { const [apiKey, setApiKey] = useState(""); const [secret, setSecret] = useState(""); const [pass, setPass] = useState(""); const [mode, setMode] = useState("paper"); const [loading, setLoad] = useState(false); const [err, setErr] = useState(""); const canSubmit = apiKey.length > 4 && secret.length > 4 && pass.length > 0 && !loading; async function save(e) { e.preventDefault(); setErr(""); setLoad(true); try { const j = await fetch("/api/okx/credentials", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ api_key: apiKey, secret, passphrase: pass, is_demo: mode === "paper" }), }).then((r) => r.json()); if (j.ok) onNext("configured"); else setErr(j.msg || "Connexion OKX échouée"); } catch (e2) { setErr(e2.message || "Erreur réseau"); } setLoad(false); } return ( <>
} value={apiKey} onChange={setApiKey} noFill /> } value={secret} onChange={setSecret} noFill /> } value={pass} onChange={setPass} noFill />
Chiffrement AES-256 · Permissions Read + Trade uniquement, jamais Withdraw.
{err &&
{err}
}
onSkip("skipped")}>Configurer plus tard
); } /* ─── STEP 2 : Telegram ─── */ function StepTelegram({ onNext, onSkip }) { const [code, setCode] = useState(null); // {value, bot} const [linked, setLink] = useState(false); const [copied, setCop] = useState(false); const [loading, setLoad]= useState(false); const [err, setErr] = useState(""); const pollRef = useRef(null); async function generate() { setErr(""); setLoad(true); try { const j = await fetch("/api/telegram/link/generate", { method: "POST", credentials: "include" }).then((r) => r.json()); if (j.ok) setCode({ value: j.code, bot: j.bot_username || "Fox_pro75_bot" }); else setErr(j.msg || "Erreur"); } catch (e2) { setErr(e2.message); } setLoad(false); } useEffect(() => { if (!code || linked) return; pollRef.current = setInterval(async () => { try { const s = await fetch("/api/telegram/link/status", { credentials: "include" }).then((r) => r.json()); if (s && s.linked) { setLink(true); clearInterval(pollRef.current); } } catch {} }, 3000); return () => clearInterval(pollRef.current); }, [code, linked]); function copy() { try { navigator.clipboard.writeText(code.value); } catch {} setCop(true); setTimeout(() => setCop(false), 1600); } return ( <> {!code ? (
{err &&
{err}
}
onSkip("skipped")}>Configurer plus tard
) : (
TON CODE DE LIAISON
{code.value}
  1. Ouvre Telegram et cherche @{code.bot}.
  2. Envoie-lui /link {code.value}
  3. Reviens ici, la connexion se fait toute seule.
Ouvrir Telegram
{linked ? <> Telegram connecté ! : "En attente de ta connexion Telegram…"}
onSkip("skipped")}>Passer pour l'instant
)} ); } /* ─── STEP 3 : WebAuthn ─── */ function StepWebAuthn({ onNext, onSkip }) { const supported = typeof window.isWebauthnSupported === "function" ? window.isWebauthnSupported() : !!window.PublicKeyCredential; const [state, setState] = useState("idle"); // idle|requesting|enabled|error const [err, setErr] = useState(""); const ua = navigator.userAgent || ""; const label = /iPhone|iPad/.test(ua) ? "Face ID" : /Mac/.test(ua) ? "Touch ID" : /Windows/.test(ua) ? "Windows Hello" : "biométrie"; async function activate() { if (!supported) return; setErr(""); setState("requesting"); try { const j = await window.fbWebauthnRegister(label); if (j && j.ok) { setState("enabled"); setTimeout(() => onNext("enabled"), 900); } else { setState("error"); setErr((j && j.msg) || "Activation impossible"); } } catch (e2) { setState("error"); setErr(e2.message || "Activation annulée"); } } return ( <>
{state === "enabled" ? : }
{!supported &&
Pas disponible sur cet appareil. Tu peux continuer avec ton mot de passe.
} {err &&
{err}
}
onSkip(supported ? "skipped" : "unsupported")}>Plus tard
); } /* ─── RECAP ─── */ function StepRecap({ progress, onFinish }) { const items = [ { k: "account", l: "Compte créé", done: true, sub: "Profil actif" }, { k: "okx", l: "OKX connecté", done: progress.okx === "configured", sub: progress.okx === "configured" ? "Clés chiffrées AES-256" : "Non configuré" }, { k: "telegram", l: "Telegram lié", done: progress.telegram === "linked", sub: progress.telegram === "linked" ? "Alertes activées" : "Non lié" }, { k: "webauthn", l: "Biométrie activée", done: progress.webauthn === "enabled", sub: progress.webauthn === "enabled" ? "Connexion rapide" : "Non activée" }, ]; return ( <>
{items.map((it) => (
{it.done ? : "○"}
{it.l}
{it.sub}
))}
); } /* ─── AuthCard (login/signup) ─── */ function AuthCard({ onSignupSuccess }) { const [mode, setMode] = useState("login"); const [first, setFirst] = useState(""); const [last, setLast] = useState(""); const [email, setEmail] = useState(""); const [pwd, setPwd] = useState(""); const [agree, setAgree] = useState(false); const [loading, setLoad]= useState(false); const [err, setErr] = useState(""); const isSignup = mode === "signup"; const webauthnOK = typeof window.isWebauthnSupported === "function" ? window.isWebauthnSupported() : !!window.PublicKeyCredential; async function submit(e) { e.preventDefault(); setErr(""); setLoad(true); try { if (isSignup) { const display_name = `${first} ${last}`.trim() || undefined; window.fbSetOnboarding(true); // AVANT le signup : retient le wizard malgré l'auto-login const res = await window.fbSignup(email, pwd, display_name); if (res && res.ok) onSignupSuccess(); else { window.fbSetOnboarding(false); setErr((res && res.msg) || "Création échouée"); } } else { const res = await window.fbLogin(email, pwd); if (!res || !res.ok) setErr((res && res.msg) || "Identifiants invalides"); } } catch (e2) { window.fbSetOnboarding(false); setErr(e2.message || "Erreur réseau"); } setLoad(false); } async function webauthn() { setErr(""); setLoad(true); try { const j = await window.fbWebauthnLogin(email); if (j && j.ok) { await window.fbAuthRefresh(); await window.fbRefresh(); } else setErr((j && j.msg) || "Échec biométrique"); } catch (e2) { setErr(e2.message || "Échec biométrique"); } setLoad(false); } const canSubmit = email && pwd && (!isSignup || agree) && !loading; return ( <>
FoxBot Pro

{isSignup ? "Créez votre compte" : "Re-bonjour, trader."}

{isSignup ? "Vos clés, votre exchange, votre contrôle." : "Connectez-vous pour accéder à vos signaux et positions."}

{isSignup && (
} value={first} onChange={setFirst} autoComplete="given-name" /> } value={last} onChange={setLast} autoComplete="family-name" />
)} } value={email} onChange={setEmail} autoComplete={isSignup ? "email" : "username"} /> } value={pwd} onChange={setPwd} autoComplete={isSignup ? "new-password" : "current-password"} /> {isSignup && ( )} {err &&
{err}
} {!isSignup && webauthnOK && ( <>
OU
)} ); } /* ─── Routeur ─── */ function AuthWizard() { const [step, setStep] = useState("auth"); // auth|okx|telegram|webauthn|recap const [progress, setProgress] = useState({ okx: null, telegram: null, webauthn: null }); const prog = (k, v) => setProgress((p) => ({ ...p, [k]: v })); function finish() { if (typeof window.fbSetOnboarding === "function") window.fbSetOnboarding(false); if (typeof window.fbRefresh === "function") window.fbRefresh(); } return (
{step === "auth" && setStep("okx")} />} {step === "okx" && { prog("okx", s); setStep("telegram"); }} onSkip={(s) => { prog("okx", s); setStep("telegram"); }} />} {step === "telegram" && { prog("telegram", s); setStep("webauthn"); }} onSkip={(s) => { prog("telegram", s); setStep("webauthn"); }} />} {step === "webauthn" && { prog("webauthn", s); setStep("recap"); }} onSkip={(s) => { prog("webauthn", s); setStep("recap"); }} />} {step === "recap" && }
{step === "auth" && (
· CHIFFREMENT AES-256 · PERMISSIONS NO-WITHDRAW · VOS CLÉS, VOTRE EXCHANGE
)}
); } window.AuthWizard = AuthWizard; })();