Files
RuvdsIpCheck/app/page.tsx
2026-08-09 15:22:28 +03:00

94 lines
7.2 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useMemo, useState } from "react";
type Event = { ip: string; country: string; city: string; network: string; score: number; status: "Review" | "Blocked" | "Allowed"; seen: string; requests: string };
const initialEvents: Event[] = [
{ ip: "185.220.101.42", country: "Germany", city: "Falkenstein", network: "AS60729 · Zwiebelfreunde", score: 96, status: "Review", seen: "12 sec ago", requests: "842" },
{ ip: "45.134.26.91", country: "Netherlands", city: "Amsterdam", network: "AS9009 · M247 Europe", score: 88, status: "Blocked", seen: "1 min ago", requests: "391" },
{ ip: "104.28.208.87", country: "United States", city: "San Francisco", network: "AS13335 · Cloudflare", score: 18, status: "Allowed", seen: "3 min ago", requests: "126" },
{ ip: "193.32.162.14", country: "Romania", city: "Bucharest", network: "AS209605 · UAB Host Baltic", score: 73, status: "Review", seen: "7 min ago", requests: "214" },
{ ip: "34.160.111.145", country: "United States", city: "Mountain View", network: "AS396982 · Google Cloud", score: 7, status: "Allowed", seen: "11 min ago", requests: "98" },
];
function riskLabel(score: number) { return score >= 80 ? "Critical" : score >= 60 ? "Elevated" : "Low"; }
export default function Home() {
const [query, setQuery] = useState("");
const [filter, setFilter] = useState("All traffic");
const [events, setEvents] = useState(initialEvents);
const [selected, setSelected] = useState<Event>(initialEvents[0]);
const [notice, setNotice] = useState("");
const visible = useMemo(() => events.filter((e) => filter === "All traffic" || e.status === filter), [events, filter]);
const inspect = () => {
const value = query.trim();
if (!value) return;
const found = events.find((e) => e.ip === value);
if (found) setSelected(found);
else setSelected({ ip: value, country: "Unknown", city: "Unresolved", network: "No network record", score: 42, status: "Review", seen: "Just now", requests: "1" });
setNotice(`Inspection opened for ${value}`);
setTimeout(() => setNotice(""), 2400);
};
const decide = (status: "Blocked" | "Allowed") => {
setSelected((current) => ({ ...current, status }));
setEvents((current) => current.map((event) => event.ip === selected.ip ? { ...event, status } : event));
setNotice(`${selected.ip} ${status.toLowerCase()}`);
setTimeout(() => setNotice(""), 2400);
};
return (
<main>
<header className="topbar">
<div className="brand"><span className="brandMark">IP</span><span>Sentinel</span></div>
<nav aria-label="Primary navigation">
<button className="navActive">Overview</button><button>Investigations</button><button>Rules</button><button>Reports</button>
</nav>
<div className="topActions"><button className="iconButton" aria-label="Notifications"></button><div className="avatar">MK</div><div><strong>Maya Kim</strong><small>Security Ops</small></div></div>
</header>
<div className="workspace">
<section className="hero">
<div><p className="eyebrow">NETWORK INTELLIGENCE</p><h1>Good morning, Maya.</h1><p>Heres what your perimeter has seen in the last 24 hours.</p></div>
<div className="health"><span className="pulse"/><div><strong>All systems operational</strong><small>Last sync 18 seconds ago</small></div></div>
</section>
<section className="lookup" aria-label="IP address lookup">
<div className="lookupIcon"></div><div className="lookupCopy"><label htmlFor="ip">Inspect an IP address</label><span>Reputation, location, network and recent activity</span></div>
<input id="ip" value={query} onChange={(e) => setQuery(e.target.value)} onKeyDown={(e) => e.key === "Enter" && inspect()} placeholder="Enter IPv4 or IPv6 address" />
<button className="primary" onClick={inspect}>Inspect IP <span></span></button>
</section>
<section className="metrics" aria-label="Traffic summary">
<article><div className="metricTop"><span>REQUESTS SCANNED</span><i className="blue"></i></div><strong>2.48M</strong><p><b>+12.4%</b> from yesterday</p></article>
<article><div className="metricTop"><span>UNIQUE ADDRESSES</span><i className="violet"></i></div><strong>18,294</strong><p><b>+3.1%</b> from yesterday</p></article>
<article><div className="metricTop"><span>THREATS BLOCKED</span><i className="red">!</i></div><strong>1,847</strong><p><em>8.2%</em> from yesterday</p></article>
<article><div className="metricTop"><span>REVIEW QUEUE</span><i className="amber"></i></div><strong>24</strong><p><b>7 critical</b> need attention</p></article>
</section>
<div className="mainGrid">
<section className="panel activity">
<div className="panelHead"><div><h2>Recent activity</h2><p>Addresses with notable behavior</p></div><div className="filters">{["All traffic","Review","Blocked","Allowed"].map((name) => <button key={name} className={filter === name ? "active" : ""} onClick={() => setFilter(name)}>{name}</button>)}</div></div>
<div className="tableWrap"><table><thead><tr><th>IP ADDRESS</th><th>LOCATION / NETWORK</th><th>REQUESTS</th><th>RISK</th><th>STATUS</th><th></th></tr></thead><tbody>{visible.map((event) => <tr key={event.ip} className={selected.ip === event.ip ? "selected" : ""} onClick={() => setSelected(event)}><td><code>{event.ip}</code><small>{event.seen}</small></td><td><strong>{event.city}, {event.country}</strong><small>{event.network}</small></td><td>{event.requests}</td><td><span className={`score ${riskLabel(event.score).toLowerCase()}`}>{event.score}</span></td><td><span className={`status ${event.status.toLowerCase()}`}>{event.status}</span></td><td></td></tr>)}</tbody></table></div>
<button className="viewAll">View all investigations </button>
</section>
<aside className="panel detail">
<div className="detailHead"><div><p>SELECTED ADDRESS</p><code>{selected.ip}</code></div><span className={`status ${selected.status.toLowerCase()}`}>{selected.status}</span></div>
<div className="riskBlock"><div className={`riskRing ${riskLabel(selected.score).toLowerCase()}`}><strong>{selected.score}</strong><span>/100</span></div><div><p>Risk assessment</p><h3>{riskLabel(selected.score)} risk</h3><small>Based on 14 intelligence signals</small></div></div>
<div className="signal"><span>TOR EXIT NODE</span><strong>{selected.score > 80 ? "Detected" : "Not detected"}</strong></div>
<div className="signal"><span>ABUSE REPORTS</span><strong>{selected.score > 80 ? "47 in 30 days" : "None recent"}</strong></div>
<div className="signal"><span>NETWORK</span><strong>{selected.network.split(" · ")[0]}</strong></div>
<div className="signal"><span>LOCATION</span><strong>{selected.city}, {selected.country}</strong></div>
<div className="actions"><button className="danger" onClick={() => decide("Blocked")}>Block address</button><button onClick={() => decide("Allowed")}>Add to allowlist</button></div>
<button className="fullReport">Open full investigation </button>
</aside>
</div>
</div>
{notice && <div className="toast" role="status"><span></span>{notice}</div>}
</main>
);
}