● Zion Boggan
repos/Sightline/frontend/src/app/enrichment/page.tsx
zionboggan.com ↗
63 lines · tsx
History for this file →
1
"use client";
2
import { useEffect, useState } from "react";
3
import { api } from "@/lib/api";
4
 
5
export default function Enrichment() {
6
  const [enrichers, setEnrichers] = useState<{ name: string; kind: string }[]>([]);
7
  const [enricher, setEnricher] = useState("dns");
8
  const [target, setTarget] = useState("example.com");
9
  const [result, setResult] = useState<any | null>(null);
10
  const [err, setErr] = useState<string | null>(null);
11
  const [loading, setLoading] = useState(false);
12
 
13
  useEffect(() => {
14
    api.enrichers().then((e) => { setEnrichers(e); if (e[0]) setEnricher(e[0].name); })
15
      .catch((e) => setErr(e.status === 401 ? "Sign in to run enrichment." : String(e)));
16
  }, []);
17
 
18
  async function run() {
19
    setErr(null); setResult(null); setLoading(true);
20
    try { setResult((await api.runEnrichment(enricher, target)).result); }
21
    catch (e: any) { setErr(e.status === 401 ? "Sign in to run enrichment." : "Enrichment failed."); }
22
    finally { setLoading(false); }
23
  }
24
 
25
  return (
26
    <div>
27
      <h1>OSINT Enrichment</h1>
28
      <p className="muted">Passive, public-source enrichment for investigations that cross into the digital domain. Secondary to the ALPR core.</p>
29
      <div className="row" style={{ margin: "12px 0" }}>
30
        <select value={enricher} onChange={(e) => setEnricher(e.target.value)}>
31
          {enrichers.map((e) => <option key={e.name} value={e.name}>{e.name} ({e.kind})</option>)}
32
        </select>
33
        <input value={target} onChange={(e) => setTarget(e.target.value)} placeholder="example.com" />
34
        <button className="primary" onClick={run} disabled={loading}>{loading ? "Running…" : "Run"}</button>
35
      </div>
36
      {err && <div className="badge danger">{err}</div>}
37
      {result && (
38
        <div className="card">
39
          <div className="row" style={{ justifyContent: "space-between" }}>
40
            <strong>{result.enricher} → {result.target}</strong>
41
            <span className="badge">confidence {result.confidence}</span>
42
          </div>
43
          {result.error && <div className="badge danger" style={{ marginTop: 8 }}>{result.error}</div>}
44
          <h2>Entities</h2>
45
          <table>
46
            <thead><tr><th>Kind</th><th>Value</th></tr></thead>
47
            <tbody>{(result.entities || []).map((e: any, i: number) => (
48
              <tr key={i}><td><span className="badge">{e.kind}</span></td><td>{e.value}</td></tr>
49
            ))}</tbody>
50
          </table>
51
          {result.relationships?.length > 0 && (
52
            <>
53
              <h2>Relationships</h2>
54
              <ul>{result.relationships.map((r: any, i: number) => (
55
                <li key={i} className="muted">{r.src_value} -{r.relation}→ {r.dst_value}</li>
56
              ))}</ul>
57
            </>
58
          )}
59
        </div>
60
      )}
61
    </div>
62
  );
63
}