fix(dashboard): show enriched network details
CI / validate (push) Successful in 5m39s
Release / release (push) Successful in 7m15s

This commit is contained in:
dmg
2026-08-02 08:05:57 -04:00
parent aa0b757814
commit 24808b0f8c
12 changed files with 106 additions and 14 deletions
@@ -13,6 +13,10 @@ describe("UserWorldMap", () => {
longitude: -122.0775,
location: "Mountain View, California, US",
classification: "clear",
networkProvider: "Comcast Cable Communications, LLC",
networkAsn: "AS7922",
connectionType: "Residential",
proxy: false,
source: "game",
observedAt: new Date("2026-08-01T12:00:00Z"),
}, {
@@ -23,7 +27,11 @@ describe("UserWorldMap", () => {
latitude: 37.4057,
longitude: -122.0774,
location: "Mountain View, California, US",
classification: "clear",
classification: "vpn",
networkProvider: "Proton AG",
networkAsn: "AS62371",
connectionType: "VPN",
proxy: true,
source: "web",
observedAt: new Date("2026-08-01T13:00:00Z"),
}]} unavailableCount={2} />);
@@ -38,6 +46,13 @@ describe("UserWorldMap", () => {
expect(markup).toMatch(/<text[^>]*>2<\/text>/);
expect(markup).toContain('<details class="mt-5 border-t border-line pt-4" id="map-location-list" open="">');
expect(markup).toContain("Mountain View, California, US");
expect(markup).toContain("Comcast Cable Communications, LLC");
expect(markup).toContain("AS7922");
expect(markup).toContain("Residential");
expect(markup).toContain("Proton AG");
expect(markup).toContain(">Proxy/VPN<");
expect(markup).toContain(">Yes<");
expect(markup).toContain(">No<");
expect(markup).toContain("World overview");
expect(markup).toContain("Interactive OpenStreetMap");
expect(markup).toContain("OpenStreetMap, which receives your IP address");
+9 -5
View File
@@ -23,6 +23,10 @@ export interface UserMapLocation {
longitude: number;
location: string;
classification: string;
networkProvider: string | null;
networkAsn: string | null;
connectionType: string | null;
proxy: boolean | null;
source: string;
observedAt: Date;
}
@@ -98,12 +102,12 @@ export function UserWorldMap({ locations, unavailableCount }: { locations: UserM
<details className="mt-5 border-t border-line pt-4" id="map-location-list" open={locationGroups.some((group) => group.count > 1)}>
<summary className="w-fit cursor-pointer font-mono text-[10px] font-bold uppercase underline underline-offset-4">View accessible location list</summary>
<div className="mt-4 overflow-x-auto">
<table className="w-full min-w-[680px] border-collapse text-left text-xs">
<caption className="sr-only">Latest approximate registered-user locations</caption>
<thead className="border-b border-line font-mono text-[9px] uppercase tracking-wider text-muted"><tr><th className="py-3 pr-4" scope="col">User</th><th className="p-3" scope="col">Location</th><th className="p-3" scope="col">Network</th><th className="p-3" scope="col">Source</th><th className="py-3 pl-4" scope="col">Last observed</th></tr></thead>
<table className="w-full min-w-[980px] border-collapse text-left text-xs">
<caption className="sr-only">Latest approximate registered-user locations and enriched network details</caption>
<thead className="border-b border-line font-mono text-[9px] uppercase tracking-wider text-muted"><tr><th className="py-3 pr-4" scope="col">User</th><th className="p-3" scope="col">Location</th><th className="p-3" scope="col">Network</th><th className="p-3" scope="col">Connection</th><th className="p-3" scope="col">Proxy/VPN</th><th className="p-3" scope="col">Risk</th><th className="p-3" scope="col">Source</th><th className="py-3 pl-4" scope="col">Last observed</th></tr></thead>
<tbody className="divide-y divide-line">
{locations.map((user) => <tr key={user.userId}><th className="py-3 pr-4 text-left" scope="row"><Link className="font-mono font-bold underline underline-offset-4" href={`/admin/users/${user.userId}`}>{user.nickname}</Link><span className="mt-1 block font-mono text-[9px] font-normal text-muted">@{user.discordUsername}</span></th><td className="p-3">{user.location}</td><td className="p-3 font-mono uppercase">{user.classification}</td><td className="p-3">{user.source}</td><td className="py-3 pl-4 font-mono text-[9px]"><time dateTime={user.observedAt.toISOString()}>{user.observedAt.toISOString()}</time></td></tr>)}
{!locations.length && <tr><td className="py-6 text-muted" colSpan={5}>No user observations currently include valid coordinates.</td></tr>}
{locations.map((user) => <tr key={user.userId}><th className="py-3 pr-4 text-left" scope="row"><Link className="font-mono font-bold underline underline-offset-4" href={`/admin/users/${user.userId}`}>{user.nickname}</Link><span className="mt-1 block font-mono text-[9px] font-normal text-muted">@{user.discordUsername}</span></th><td className="p-3">{user.location}</td><td className="p-3"><span className="block">{user.networkProvider ?? "Unknown"}</span>{user.networkAsn && <span className="mt-1 block font-mono text-[9px] text-muted">{user.networkAsn}</span>}</td><td className="p-3">{user.connectionType ?? "Unknown"}</td><td className="p-3 font-mono font-bold uppercase">{user.proxy === null ? "Unknown" : user.proxy ? "Yes" : "No"}</td><td className="p-3 font-mono uppercase">{user.classification}</td><td className="p-3">{user.source}</td><td className="py-3 pl-4 font-mono text-[9px]"><time dateTime={user.observedAt.toISOString()}>{user.observedAt.toISOString()}</time></td></tr>)}
{!locations.length && <tr><td className="py-6 text-muted" colSpan={8}>No user observations currently include valid coordinates.</td></tr>}
</tbody>
</table>
</div>