/** * Trouve-KA — contrôles du robot (pause, seeds, recrawl, blocage) * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai */ "use client"; import { useState, type FormEvent } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { adminFetch, UnauthorizedError } from "@/lib/admin"; type Feedback = { kind: "ok" | "err"; text: string } | null; interface ControlsProps { token: string; paused: boolean; onUnauthorized: () => void; /** Rafraîchit la vue d'ensemble après une action réussie. */ onRefresh: () => void; } export function Controls({ token, paused, onUnauthorized, onRefresh, }: ControlsProps) { const [busy, setBusy] = useState(null); const [feedback, setFeedback] = useState(null); const [seeds, setSeeds] = useState(""); const [recrawlTarget, setRecrawlTarget] = useState(""); const [blockDomain, setBlockDomain] = useState(""); async function run( name: string, action: () => Promise, okMessage: string, ): Promise { setBusy(name); setFeedback(null); try { await action(); setFeedback({ kind: "ok", text: okMessage }); onRefresh(); return true; } catch (err) { if (err instanceof UnauthorizedError) { onUnauthorized(); return false; } setFeedback({ kind: "err", text: "L'opération a échoué. Réessayez." }); return false; } finally { setBusy(null); } } function togglePause() { const path = paused ? "/api/admin/resume" : "/api/admin/pause"; run( "pause", () => adminFetch(path, token, { method: "POST" }), paused ? "Robot relancé." : "Robot mis en pause.", ); } function submitSeeds(event: FormEvent) { event.preventDefault(); const urls = seeds .split("\n") .map((line) => line.trim()) .filter((line) => /^https?:\/\//.test(line)); if (urls.length === 0) { setFeedback({ kind: "err", text: "Entrez au moins une URL valide (une par ligne, http:// ou https://).", }); return; } run( "seeds", () => adminFetch("/api/admin/seeds", token, { method: "POST", body: JSON.stringify({ urls }), }), `${urls.length} seed(s) ajouté(s) au frontier.`, ).then((ok) => { if (ok) setSeeds(""); }); } function submitRecrawl(event: FormEvent) { event.preventDefault(); const target = recrawlTarget.trim(); if (!target) return; const body = /^https?:\/\//.test(target) ? { url: target } : { domain: target }; run( "recrawl", () => adminFetch("/api/admin/recrawl", token, { method: "POST", body: JSON.stringify(body), }), "Recrawl demandé.", ).then((ok) => { if (ok) setRecrawlTarget(""); }); } function submitBlock(event: FormEvent) { event.preventDefault(); const domain = blockDomain.trim(); if (!domain) return; run( "block", () => adminFetch("/api/admin/domains/block", token, { method: "POST", body: JSON.stringify({ domain }), }), `Domaine « ${domain} » bloqué.`, ).then((ok) => { if (ok) setBlockDomain(""); }); } return ( Contrôles du robot