Enrich all destination and remote IP address views with GeoIP details

This commit is contained in:
vanne committed 2026-07-01 15:15:49 +07:00
1 parent 448736bc03
commit 6c8874afdc
5 files changed
+100 -14

No files matched your search

+28 -3
View File
@@ -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) },
+10 -1
View File
@@ -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 || '-' },
+34 -6
View File
@@ -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">
{f.dst_ip || "—"}{f.dst_port ? <span className="text-slate-500">:{f.dst_port}</span> : ""} <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> : ""}
</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>
+18 -3
View File
@@ -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 && (