Enrich all destination and remote IP address views with GeoIP details
This commit is contained in:
1 parent
448736bc03
commit
6c8874afdc
5 files changed
+99
-13
No files matched your search
@@ -9,6 +9,7 @@ import {
|
|||||||
} from "@/lib/api-advanced";
|
} from "@/lib/api-advanced";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/Card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/Card";
|
||||||
import { DataTable, Column } from "@/components/ui/DataTable";
|
import { DataTable, Column } from "@/components/ui/DataTable";
|
||||||
|
import { IpDetails } from "@/components/ui/IpDetails";
|
||||||
import { Loader2, Bitcoin, Shield, Server, Lock, AlertTriangle, Key, Globe, Network, ShieldAlert } from "lucide-react";
|
import { Loader2, Bitcoin, Shield, Server, Lock, AlertTriangle, Key, Globe, Network, ShieldAlert } from "lucide-react";
|
||||||
|
|
||||||
export default function IntelligencePage() {
|
export default function IntelligencePage() {
|
||||||
@@ -123,7 +124,15 @@ export default function IntelligencePage() {
|
|||||||
columns={[
|
columns={[
|
||||||
{ header: "IP Address", accessor: (row: any) => row.ip_address },
|
{ header: "IP Address", accessor: (row: any) => row.ip_address },
|
||||||
{ header: "VPN Type", accessor: (row: any) => row.vpn_type },
|
{ header: "VPN Type", accessor: (row: any) => row.vpn_type },
|
||||||
{ header: "Remote IP", accessor: (row: any) => row.remote_ip },
|
{
|
||||||
|
header: "Remote IP",
|
||||||
|
accessor: (row: any) => row.remote_ip ? (
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
<span className="font-mono text-xs text-blue-400">{row.remote_ip}</span>
|
||||||
|
<IpDetails ip={row.remote_ip} />
|
||||||
|
</div>
|
||||||
|
) : "—"
|
||||||
|
},
|
||||||
{ header: "Country", accessor: (row: any) => row.country },
|
{ header: "Country", accessor: (row: any) => row.country },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
@@ -133,7 +142,15 @@ export default function IntelligencePage() {
|
|||||||
<DataTable
|
<DataTable
|
||||||
data={ipReputation} isLoading={loadingIpRep}
|
data={ipReputation} isLoading={loadingIpRep}
|
||||||
columns={[
|
columns={[
|
||||||
{ header: "IP Address", accessor: (row: any) => row.ip_address },
|
{
|
||||||
|
header: "IP Address",
|
||||||
|
accessor: (row: any) => row.ip_address ? (
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
<span className="font-mono text-xs text-blue-400">{row.ip_address}</span>
|
||||||
|
<IpDetails ip={row.ip_address} />
|
||||||
|
</div>
|
||||||
|
) : "—"
|
||||||
|
},
|
||||||
{ header: "Reputation", accessor: (row: any) => row.reputation },
|
{ header: "Reputation", accessor: (row: any) => row.reputation },
|
||||||
{ header: "App", accessor: (row: any) => row.app_label },
|
{ header: "App", accessor: (row: any) => row.app_label },
|
||||||
{ header: "Country", accessor: (row: any) => row.country },
|
{ header: "Country", accessor: (row: any) => row.country },
|
||||||
@@ -190,7 +207,15 @@ export default function IntelligencePage() {
|
|||||||
<DataTable
|
<DataTable
|
||||||
data={serverDiscovery} isLoading={loadingServer}
|
data={serverDiscovery} isLoading={loadingServer}
|
||||||
columns={[
|
columns={[
|
||||||
{ header: "IP Address", accessor: (row: any) => row.ip_address },
|
{
|
||||||
|
header: "IP Address",
|
||||||
|
accessor: (row: any) => row.ip_address ? (
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
<span className="font-mono text-xs text-blue-400">{row.ip_address}</span>
|
||||||
|
<IpDetails ip={row.ip_address} />
|
||||||
|
</div>
|
||||||
|
) : "—"
|
||||||
|
},
|
||||||
{ header: "Server Type", accessor: (row: any) => row.server_type },
|
{ header: "Server Type", accessor: (row: any) => row.server_type },
|
||||||
{ header: "Port", accessor: (row: any) => row.port },
|
{ header: "Port", accessor: (row: any) => row.port },
|
||||||
{ header: "OS", accessor: (row: any) => row.os_label },
|
{ header: "OS", accessor: (row: any) => row.os_label },
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
useIpVersions, useRemoteIps, useMacBandwidth,
|
useIpVersions, useRemoteIps, useMacBandwidth,
|
||||||
useFlowTypes, useFlowOrigins
|
useFlowTypes, useFlowOrigins
|
||||||
} from "@/lib/api-advanced";
|
} from "@/lib/api-advanced";
|
||||||
|
import { IpDetails } from "@/components/ui/IpDetails";
|
||||||
|
|
||||||
export default function NetworkInfrastructurePage() {
|
export default function NetworkInfrastructurePage() {
|
||||||
// Routing & Geography Tab
|
// Routing & Geography Tab
|
||||||
@@ -135,7 +136,15 @@ export default function NetworkInfrastructurePage() {
|
|||||||
data={remoteIps}
|
data={remoteIps}
|
||||||
isLoading={loadingRemoteIps}
|
isLoading={loadingRemoteIps}
|
||||||
columns={[
|
columns={[
|
||||||
{ header: "IP Address", accessor: (row: any) => row.remote_ip },
|
{
|
||||||
|
header: "IP Address",
|
||||||
|
accessor: (row: any) => row.remote_ip ? (
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
<span className="font-mono text-xs text-blue-400">{row.remote_ip}</span>
|
||||||
|
<IpDetails ip={row.remote_ip} />
|
||||||
|
</div>
|
||||||
|
) : "—"
|
||||||
|
},
|
||||||
{ header: "IP Version", accessor: (row) => `IPv${row.ip_version}` },
|
{ header: "IP Version", accessor: (row) => `IPv${row.ip_version}` },
|
||||||
{ header: "Download", accessor: (row) => formatBytes(row.download) },
|
{ header: "Download", accessor: (row) => formatBytes(row.download) },
|
||||||
{ header: "Upload", accessor: (row) => formatBytes(row.upload) },
|
{ header: "Upload", accessor: (row) => formatBytes(row.upload) },
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { Badge } from "@/components/ui/Badge";
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { ShieldCheck, Loader2 } from "lucide-react";
|
import { ShieldCheck, Loader2 } from "lucide-react";
|
||||||
import { useThreats, ThreatStat } from "@/lib/api";
|
import { useThreats, ThreatStat } from "@/lib/api";
|
||||||
|
import { IpDetails } from "@/components/ui/IpDetails";
|
||||||
|
|
||||||
export default function ThreatsPage() {
|
export default function ThreatsPage() {
|
||||||
const [mounted, setMounted] = useState(false);
|
const [mounted, setMounted] = useState(false);
|
||||||
@@ -31,7 +32,15 @@ export default function ThreatsPage() {
|
|||||||
{ header: "Type", accessor: (row) => row.threat_type || 'Unknown' },
|
{ header: "Type", accessor: (row) => row.threat_type || 'Unknown' },
|
||||||
{ header: "Severity", accessor: (row) => getSeverityBadge(row.severity) },
|
{ header: "Severity", accessor: (row) => getSeverityBadge(row.severity) },
|
||||||
{ header: "Source IP", accessor: (row) => <span className="font-medium text-blue-400">{row.ip_address || '-'}</span> },
|
{ header: "Source IP", accessor: (row) => <span className="font-medium text-blue-400">{row.ip_address || '-'}</span> },
|
||||||
{ header: "Dest IP", accessor: (row) => row.dst_ip || '-' },
|
{
|
||||||
|
header: "Dest IP",
|
||||||
|
accessor: (row) => row.dst_ip ? (
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
<span className="font-mono text-xs text-blue-400">{row.dst_ip}</span>
|
||||||
|
<IpDetails ip={row.dst_ip} />
|
||||||
|
</div>
|
||||||
|
) : '-'
|
||||||
|
},
|
||||||
{ header: "MAC Address", accessor: (row) => row.mac_address ? <code className="text-xs bg-secondary/50 px-1 py-0.5 rounded text-muted-foreground">{row.mac_address}</code> : '-' },
|
{ header: "MAC Address", accessor: (row) => row.mac_address ? <code className="text-xs bg-secondary/50 px-1 py-0.5 rounded text-muted-foreground">{row.mac_address}</code> : '-' },
|
||||||
{ header: "App", accessor: (row) => row.app_label || '-' },
|
{ header: "App", accessor: (row) => row.app_label || '-' },
|
||||||
{ header: "Domain", accessor: (row) => row.domain || '-' },
|
{ header: "Domain", accessor: (row) => row.domain || '-' },
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useState, useEffect } from "react";
|
|||||||
import { fetchAgentDetails, AgentDetails } from "@/lib/api";
|
import { fetchAgentDetails, AgentDetails } from "@/lib/api";
|
||||||
import { fmtBytes } from "@/lib/utils";
|
import { fmtBytes } from "@/lib/utils";
|
||||||
import { DeviceDetailModal } from "@/components/ui/DeviceDetailModal";
|
import { DeviceDetailModal } from "@/components/ui/DeviceDetailModal";
|
||||||
|
import { IpDetails } from "@/components/ui/IpDetails";
|
||||||
import {
|
import {
|
||||||
Loader2, X, Server, Monitor, Activity, Globe, Shield,
|
Loader2, X, Server, Monitor, Activity, Globe, Shield,
|
||||||
Bell, Wifi, AlertTriangle, Lock, Unlock, ChevronRight,
|
Bell, Wifi, AlertTriangle, Lock, Unlock, ChevronRight,
|
||||||
@@ -242,8 +243,13 @@ export function AgentDetailModal({ agentUuid, agentLabel, onClose }: Props) {
|
|||||||
{data.flows.map((f, i) => (
|
{data.flows.map((f, i) => (
|
||||||
<tr key={i} className="hover:bg-white/[0.03] transition-colors">
|
<tr key={i} className="hover:bg-white/[0.03] transition-colors">
|
||||||
<td className="px-3 py-2 font-mono text-xs text-blue-400 whitespace-nowrap">{f.src_ip || "—"}</td>
|
<td className="px-3 py-2 font-mono text-xs text-blue-400 whitespace-nowrap">{f.src_ip || "—"}</td>
|
||||||
<td className="px-3 py-2 font-mono text-xs text-slate-300 whitespace-nowrap">
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
<span className="font-mono text-xs text-slate-300">
|
||||||
{f.dst_ip || "—"}{f.dst_port ? <span className="text-slate-500">:{f.dst_port}</span> : ""}
|
{f.dst_ip || "—"}{f.dst_port ? <span className="text-slate-500">:{f.dst_port}</span> : ""}
|
||||||
|
</span>
|
||||||
|
{f.dst_ip && <IpDetails ip={f.dst_ip} />}
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 text-xs text-purple-300 max-w-[140px] truncate">{f.app_label || f.domain || "—"}</td>
|
<td className="px-3 py-2 text-xs text-purple-300 max-w-[140px] truncate">{f.app_label || f.domain || "—"}</td>
|
||||||
<td className="px-3 py-2 text-xs text-slate-400">{f.protocol || "—"}</td>
|
<td className="px-3 py-2 text-xs text-slate-400">{f.protocol || "—"}</td>
|
||||||
@@ -350,7 +356,14 @@ export function AgentDetailModal({ agentUuid, agentLabel, onClose }: Props) {
|
|||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 text-xs text-purple-300 truncate max-w-[100px]">{r.app_label || "—"}</td>
|
<td className="px-3 py-2 text-xs text-purple-300 truncate max-w-[100px]">{r.app_label || "—"}</td>
|
||||||
<td className="px-3 py-2 font-mono text-xs text-slate-400">{r.dst_ip ? `${r.dst_ip}:${r.dst_port ?? "?"}` : "—"}</td>
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
<span className="font-mono text-xs text-slate-400">
|
||||||
|
{r.dst_ip ? `${r.dst_ip}:${r.dst_port ?? "?"}` : "—"}
|
||||||
|
</span>
|
||||||
|
{r.dst_ip && <IpDetails ip={r.dst_ip} />}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
<td className="px-3 py-2 text-right text-xs text-cyan-400">{fmtBytes(r.download)}</td>
|
<td className="px-3 py-2 text-right text-xs text-cyan-400">{fmtBytes(r.download)}</td>
|
||||||
<td className="px-3 py-2 text-right text-xs text-green-400">{fmtBytes(r.upload)}</td>
|
<td className="px-3 py-2 text-right text-xs text-green-400">{fmtBytes(r.upload)}</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -373,7 +386,14 @@ export function AgentDetailModal({ agentUuid, agentLabel, onClose }: Props) {
|
|||||||
return (
|
return (
|
||||||
<tr key={i} className="hover:bg-white/[0.03]">
|
<tr key={i} className="hover:bg-white/[0.03]">
|
||||||
<td className="px-3 py-2 font-mono text-xs text-blue-400">{r.ip_address || "—"}</td>
|
<td className="px-3 py-2 font-mono text-xs text-blue-400">{r.ip_address || "—"}</td>
|
||||||
<td className="px-3 py-2 font-mono text-xs text-slate-400">{r.dst_ip ? `${r.dst_ip}:${r.dst_port ?? "?"}` : "—"}</td>
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
<span className="font-mono text-xs text-slate-400">
|
||||||
|
{r.dst_ip ? `${r.dst_ip}:${r.dst_port ?? "?"}` : "—"}
|
||||||
|
</span>
|
||||||
|
{r.dst_ip && <IpDetails ip={r.dst_ip} />}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
<td className="px-3 py-2 text-xs text-slate-300">{r.protocol || "—"}</td>
|
<td className="px-3 py-2 text-xs text-slate-300">{r.protocol || "—"}</td>
|
||||||
<td className="px-3 py-2 text-xs text-yellow-300 font-mono">{r.username || "—"}</td>
|
<td className="px-3 py-2 text-xs text-yellow-300 font-mono">{r.username || "—"}</td>
|
||||||
<td className="px-3 py-2">
|
<td className="px-3 py-2">
|
||||||
@@ -452,7 +472,10 @@ export function AgentDetailModal({ agentUuid, agentLabel, onClose }: Props) {
|
|||||||
<span className="font-mono text-blue-400">{r.ip_address || "—"}</span>
|
<span className="font-mono text-blue-400">{r.ip_address || "—"}</span>
|
||||||
<span className="text-purple-400">{r.vpn_type || "—"}</span>
|
<span className="text-purple-400">{r.vpn_type || "—"}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-slate-500 mt-0.5">Remote: {r.remote_ip || "—"} • {r.country || "—"}</div>
|
<div className="text-slate-500 mt-0.5 flex flex-col gap-0.5">
|
||||||
|
<span>Remote: {r.remote_ip || "—"} • {r.country || "—"}</span>
|
||||||
|
{r.remote_ip && <IpDetails ip={r.remote_ip} />}
|
||||||
|
</div>
|
||||||
{r.confidence && <div className="text-slate-500">Confidence: {(r.confidence * 100).toFixed(0)}%</div>}
|
{r.confidence && <div className="text-slate-500">Confidence: {(r.confidence * 100).toFixed(0)}%</div>}
|
||||||
</div>
|
</div>
|
||||||
))}</div>
|
))}</div>
|
||||||
@@ -470,7 +493,12 @@ export function AgentDetailModal({ agentUuid, agentLabel, onClose }: Props) {
|
|||||||
<TableWrap head={["IP", "Hostname", "Server Type", "Port", "Protocol", "OS"]}>
|
<TableWrap head={["IP", "Hostname", "Server Type", "Port", "Protocol", "OS"]}>
|
||||||
{data.server_discovery!.map((r, i) => (
|
{data.server_discovery!.map((r, i) => (
|
||||||
<tr key={i} className="hover:bg-white/[0.03]">
|
<tr key={i} className="hover:bg-white/[0.03]">
|
||||||
<td className="px-3 py-2 font-mono text-xs text-blue-400">{r.ip_address || "—"}</td>
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
<span className="font-mono text-xs text-blue-400">{r.ip_address || "—"}</span>
|
||||||
|
{r.ip_address && <IpDetails ip={r.ip_address} />}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
<td className="px-3 py-2 text-xs text-slate-300 truncate max-w-[120px]">{r.hostname || "—"}</td>
|
<td className="px-3 py-2 text-xs text-slate-300 truncate max-w-[120px]">{r.hostname || "—"}</td>
|
||||||
<td className="px-3 py-2 text-xs text-cyan-400">{r.server_type || "—"}</td>
|
<td className="px-3 py-2 text-xs text-cyan-400">{r.server_type || "—"}</td>
|
||||||
<td className="px-3 py-2 text-xs text-slate-400">{r.port ?? "—"}</td>
|
<td className="px-3 py-2 text-xs text-slate-400">{r.port ?? "—"}</td>
|
||||||
|
|||||||
@@ -546,7 +546,12 @@ export function DeviceDetailModal({ ip, deviceLabel, macAddress, onClose }: Prop
|
|||||||
<TableWrap head={["Destination IP", "Port", "Protocol", "Username", "Severity", "↓"]}>
|
<TableWrap head={["Destination IP", "Port", "Protocol", "Username", "Severity", "↓"]}>
|
||||||
{data.unencrypted_passwords.map((p, i) => (
|
{data.unencrypted_passwords.map((p, i) => (
|
||||||
<tr key={i} className="hover:bg-white/[0.03] transition-colors">
|
<tr key={i} className="hover:bg-white/[0.03] transition-colors">
|
||||||
<td className="px-3 py-2 font-mono text-xs text-red-400">{p.dst_ip || "—"}</td>
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
<span className="font-mono text-xs text-red-400">{p.dst_ip || "—"}</span>
|
||||||
|
{p.dst_ip && <IpDetails ip={p.dst_ip} />}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
<td className="px-3 py-2 text-xs text-slate-400">{p.dst_port ?? "—"}</td>
|
<td className="px-3 py-2 text-xs text-slate-400">{p.dst_port ?? "—"}</td>
|
||||||
<td className="px-3 py-2 text-xs text-slate-300">{p.protocol || "—"}</td>
|
<td className="px-3 py-2 text-xs text-slate-300">{p.protocol || "—"}</td>
|
||||||
<td className="px-3 py-2 text-xs text-yellow-300 font-mono">{p.username || "—"}</td>
|
<td className="px-3 py-2 text-xs text-yellow-300 font-mono">{p.username || "—"}</td>
|
||||||
@@ -569,7 +574,12 @@ export function DeviceDetailModal({ ip, deviceLabel, macAddress, onClose }: Prop
|
|||||||
<TableWrap head={["Remote IP", "Reputation", "Score", "Country", "App", "Blacklisted", "↓"]}>
|
<TableWrap head={["Remote IP", "Reputation", "Score", "Country", "App", "Blacklisted", "↓"]}>
|
||||||
{data.ip_reputation.map((r, i) => (
|
{data.ip_reputation.map((r, i) => (
|
||||||
<tr key={i} className="hover:bg-white/[0.03] transition-colors">
|
<tr key={i} className="hover:bg-white/[0.03] transition-colors">
|
||||||
<td className="px-3 py-2 font-mono text-xs text-red-400">{r.remote_ip || "—"}</td>
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
<span className="font-mono text-xs text-red-400">{r.remote_ip || "—"}</span>
|
||||||
|
{r.remote_ip && <IpDetails ip={r.remote_ip} />}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
<td className="px-3 py-2 text-xs text-slate-300">{r.reputation || "—"}</td>
|
<td className="px-3 py-2 text-xs text-slate-300">{r.reputation || "—"}</td>
|
||||||
<td className="px-3 py-2 text-xs text-yellow-400">{r.score ?? "—"}</td>
|
<td className="px-3 py-2 text-xs text-yellow-400">{r.score ?? "—"}</td>
|
||||||
<td className="px-3 py-2 text-xs text-slate-400">{r.country || "—"}</td>
|
<td className="px-3 py-2 text-xs text-slate-400">{r.country || "—"}</td>
|
||||||
@@ -605,7 +615,12 @@ export function DeviceDetailModal({ ip, deviceLabel, macAddress, onClose }: Prop
|
|||||||
<span className="text-blue-400 font-medium">{v.vpn_type || "Unknown VPN"}</span>
|
<span className="text-blue-400 font-medium">{v.vpn_type || "Unknown VPN"}</span>
|
||||||
<span className="text-slate-500">{v.country || "—"}</span>
|
<span className="text-slate-500">{v.country || "—"}</span>
|
||||||
</div>
|
</div>
|
||||||
{v.remote_ip && <p className="text-slate-400 font-mono">→ {v.remote_ip}</p>}
|
{v.remote_ip && (
|
||||||
|
<div className="text-slate-400 font-mono flex flex-col gap-0.5 mt-0.5">
|
||||||
|
<span>→ {v.remote_ip}</span>
|
||||||
|
<IpDetails ip={v.remote_ip} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="flex gap-2 text-slate-500">
|
<div className="flex gap-2 text-slate-500">
|
||||||
<span>{v.protocol || "—"}</span>
|
<span>{v.protocol || "—"}</span>
|
||||||
{v.confidence !== null && v.confidence !== undefined && (
|
{v.confidence !== null && v.confidence !== undefined && (
|
||||||
|
|||||||
Reference in new issue
Block a user