import React from "react"; import { createRoot } from "react-dom/client"; import { useState, useEffect } from 'react'; import { ApiaryAPI, LoginAttempt, DummyApiaryAPIClient, ApiaryAPIClient, TotalStats, StatsResult } from "./api"; import { BrowserRouter, NavLink, Routes, Route } from "react-router"; import { Chart as ChartJS, Tooltip, ArcElement, Legend } from "chart.js"; import { Pie } from "react-chartjs-2"; ChartJS.register(Tooltip, ArcElement, Legend); interface AppProps { api: ApiaryAPI } let chartColors = [ "#1abc9c", "#2ecc71", "#3498db", "#9b59b6", "#34495e", "#16a085", "#27ae60", "#2980b9", "#8e44ad", "#2c3e50", "#f1c40f", "#e67e22", "#e74c3c", "#ecf0f1", "#95a5a6", "#f39c12", "#d35400", "#c0392b", "#bdc3c7", "#7f8c8d" ] export function App({ api }: AppProps) { return ( <>
} /> } /> } /> } />
); } export function Stats({ api }: AppProps) { const [stats, setStats] = useState([]); const [statsType, setStatsType] = useState("password") const activeMenu = (name: string): boolean => { if (statsType === name) { return true } return false } const subMenuItems: SubMenuProps = { items: [ { name: "Passwords", active: () => { return activeMenu("password") }, onClick: () => setStatsType("password") }, { name: "Usernames", active: () => { return activeMenu("username") }, onClick: () => setStatsType("username") }, { name: "IPs", active: () => { return activeMenu("ip") }, onClick: () => setStatsType("ip") }, ] } useEffect(() => { async function getStats() { try { let newStats = await api.stats(statsType, 10); if (JSON.stringify(newStats) !== JSON.stringify(stats)) { setStats(newStats) } } catch (e) { console.log("Error getting stats", e) } } getStats() const interval = setInterval(() => { getStats() }, 5000) return () => { clearInterval(interval); } }, [stats, statsType]) return ( <> {stats.length > 0 ? :

Loading...

} ); } export interface StatsPieProps { data: StatsResult[] } export function StatsPie({ data }: StatsPieProps) { const labels = data.map((d) => d.name); const values = data.map((d) => d.count); const piedata = { labels, datasets: [{ label: "# of attempts", data: values, backgroundColor: chartColors, borderWidth: 1 }] }; return (
) } export function Home({ api }: AppProps) { const [totals, setTotals] = useState(null) useEffect(() => { async function getTotals() { let totals = await api.totals(); setTotals(totals); } if (!totals) { getTotals(); } const interval = setInterval(() => { getTotals(); }, 5000) return () => { clearInterval(interval); } }) return ( <> {totals ? :

Loading...

} ); } export interface TotalsProps { totals: TotalStats } export function Totals({ totals }: TotalsProps) { return (

Unique passwords

Unique username

Unique IPs

Total attempts

{totals.password}

{totals.username}

{totals.ip}

{totals.attempts}

) } export function Live({ api }: AppProps) { let list: LoginAttempt[] = []; let [liveList, setLiveList] = useState(list); useEffect(() => { const cleanup = api.live((a) => { setLiveList((list) => { return [...list, a]; }); }); return cleanup }, [liveList, api]); return ( <> ); } export interface LiveListProps { list: LoginAttempt[] }; export function LiveList({ list }: LiveListProps) { let items = list.map((a) => { return ( {a.date} {a.username} {a.password} {a.remoteIP} {a.country} ) }) return ( <> {items}
Date Username Password IP Country
) }; export function Query({ api }: AppProps) { const [liveList, setLiveList] = useState([]); const [queryErr, setQueryErr] = useState(null); async function handleSubmit(event: React.FormEvent) { event.preventDefault(); const value = event.currentTarget.query.value; if (value === "") { setQueryErr(new Error("Query cannot be empty")); return } try { const results = await api.query("", value) setQueryErr(null); setLiveList(results); } catch (e) { if (e instanceof Error) { setQueryErr(e); } } } return ( <>
{queryErr ? : null} ); } interface ErrorBoxProps { message: string | null }; export function ErrorBox({ message }: ErrorBoxProps) { return (

Error: {message}

) } export function Header() { return (
); } export interface SubMenuProps { items: Array<{ name: string, active: () => boolean, onClick: () => void }> } export function SubMenu({ items }: SubMenuProps) { return ( ) } const rootElement = document.getElementById('root'); if (rootElement) { const root = createRoot(rootElement); let api: ApiaryAPI; if (process.env.NODE_ENV === "production") { api = new ApiaryAPIClient(); } else { api = new DummyApiaryAPIClient(); } root.render( ); }