fix(p2p stats): improve own-IP geolocation and world map accuracy (#1138)

* fix(p2p stats): improve own-IP geolocation and world map accuracy

Resolve the user's public endpoint when libp2p only advertises private listen addresses, look up an accurate country flag for "Your IP", snap peer markers to country centroids, and add leeching seeder link plus panel layout tweaks.

* fix(p2p stats): skip caching own-IP lookups cancelled by abort

When the P2P stats panel unmounts mid-request, its AbortSignal cancels
the in-flight fetchOwnPublicEndpoint / fetchOwnIpCountryCode calls. Those
empty results were still cached for 30-60s, so reopening the panel within
that window showed "Your IP" as unavailable or without a country flag even
though nothing had actually failed. Skip caching when the signal aborted so
a later open retries. Addresses Cursor Bugbot finding.
This commit is contained in:
Tommaso Casaburi
2026-05-23 22:47:41 +07:00
committed by GitHub
parent 9c4bb28940
commit f5039285a7
12 changed files with 995 additions and 276 deletions
+93 -23
View File
@@ -1,5 +1,16 @@
import { describe, expect, it } from 'vitest';
import { extractIpv4FromAddress, getApproximateCountryCode, getApproximateLatLon, isPrivateOrReservedIpv4 } from '../peer-geo';
import { describe, expect, it, vi } from 'vitest';
import { COUNTRY_CENTROIDS } from '../../data/country-centroids';
import {
extractIpFromAddress,
extractIpv4FromAddress,
extractIpv6FromAddress,
fetchOwnIpCountryCode,
fetchOwnPublicEndpoint,
getApproximateCountryCode,
getApproximateLatLon,
getFirstPublicIpFromAddresses,
isPrivateOrReservedIpv4,
} from '../peer-geo';
const addr = (ip: string) => `/ip4/${ip}/tcp/4001/ws/p2p/12D3KooWExample`;
@@ -19,6 +30,13 @@ describe('extractIpv4FromAddress', () => {
});
});
describe('extractIpv6FromAddress', () => {
it('extracts the IPv6 from a multiaddr', () => {
expect(extractIpv6FromAddress('/ip6/2001:4860:4860::8888/tcp/4001/ws')).toBe('2001:4860:4860::8888');
expect(extractIpFromAddress('/ip6/2001:4860:4860::8888/tcp/4001/ws')).toBe('2001:4860:4860::8888');
});
});
describe('isPrivateOrReservedIpv4', () => {
it('flags private and reserved ranges', () => {
for (const ip of ['10.0.0.1', '172.16.5.4', '192.168.1.10', '127.0.0.1', '169.254.1.1', '100.64.0.1', '0.0.0.0', '239.255.0.1']) {
@@ -44,27 +62,17 @@ describe('getApproximateLatLon', () => {
expect(getApproximateLatLon(addr('8.8.8.8'))).toEqual(getApproximateLatLon(addr('8.8.8.8')));
});
it('places addresses in the expected continental region', () => {
const na = getApproximateLatLon(addr('8.8.8.8'));
expect(na?.lon).toBeLessThan(-80); // North America
expect(na?.lat).toBeGreaterThan(30);
const eu = getApproximateLatLon(addr('80.80.80.80'));
expect(eu?.lon).toBeGreaterThan(0);
expect(eu?.lon).toBeLessThan(30);
expect(eu?.lat).toBeGreaterThan(40);
const as = getApproximateLatLon(addr('1.1.1.1'));
expect(as?.lon).toBeGreaterThan(90);
const af = getApproximateLatLon(addr('41.0.0.1'));
expect(af?.lon).toBeGreaterThan(8);
expect(af?.lon).toBeLessThan(34);
expect(af?.lat).toBeLessThan(12);
const sa = getApproximateLatLon(addr('200.0.0.1'));
expect(sa?.lon).toBeLessThan(-45);
expect(sa?.lat).toBeLessThan(-5);
it('snaps a peer to the centroid of its flag country', () => {
for (const ip of ['8.8.8.8', '80.80.80.80', '1.1.1.1', '41.0.0.1', '200.0.0.1', '194.110.247.146', '91.234.199.189']) {
const country = getApproximateCountryCode(addr(ip));
expect(country).toBeDefined();
const centroid = COUNTRY_CENTROIDS[country!];
expect(centroid).toBeDefined();
const loc = getApproximateLatLon(addr(ip))!;
// Marker sits at the country centroid, within the small placement jitter.
expect(Math.abs(loc.lat - centroid.lat)).toBeLessThanOrEqual(1);
expect(Math.abs(loc.lon - centroid.lon)).toBeLessThanOrEqual(1.3);
}
});
it('stays within valid coordinate bounds', () => {
@@ -77,6 +85,68 @@ describe('getApproximateLatLon', () => {
});
});
describe('getFirstPublicIpFromAddresses', () => {
it('returns the first public IP from multiaddrs', () => {
expect(getFirstPublicIpFromAddresses(['/ip4/127.0.0.1/tcp/4001', '/ip4/147.75.84.175/tcp/4001/ws'])).toBe('147.75.84.175');
expect(getFirstPublicIpFromAddresses(['/ip4/127.0.0.1/tcp/4001', '/ip6/2001:4860:4860::8888/tcp/443'])).toBe('2001:4860:4860::8888');
});
it('returns undefined when only private addresses are present', () => {
expect(getFirstPublicIpFromAddresses(['/ip4/127.0.0.1/tcp/4001', '/ip4/10.0.0.5/tcp/4001', '/ip6/fd00::1/tcp/4001'])).toBeUndefined();
});
});
describe('fetchOwnPublicEndpoint', () => {
it('caches the fetched public endpoint', async () => {
const fetchMock = vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ country: 'US', ip: '2001:4860:4860::8888' }),
});
vi.stubGlobal('fetch', fetchMock);
await expect(fetchOwnPublicEndpoint()).resolves.toEqual({ countryCode: 'us', ip: '2001:4860:4860::8888' });
await expect(fetchOwnPublicEndpoint()).resolves.toEqual({ countryCode: 'us', ip: '2001:4860:4860::8888' });
expect(fetchMock).toHaveBeenCalledTimes(1);
vi.unstubAllGlobals();
});
});
describe('fetchOwnIpCountryCode', () => {
it("resolves and caches the accurate country for the node's own ip", async () => {
const fetchMock = vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ country: 'VN', ip: '172.225.56.8' }),
});
vi.stubGlobal('fetch', fetchMock);
await expect(fetchOwnIpCountryCode('172.225.56.8')).resolves.toBe('vn');
await expect(fetchOwnIpCountryCode('172.225.56.8')).resolves.toBe('vn');
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(fetchMock.mock.calls[0][0]).toBe('https://api.country.is/172.225.56.8');
vi.unstubAllGlobals();
});
it('does not cache a result from an aborted lookup', async () => {
const fetchMock = vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ country: 'VN', ip: '203.0.113.50' }),
});
vi.stubGlobal('fetch', fetchMock);
const controller = new AbortController();
controller.abort();
// An aborted request is cancellation, not a real result, so it must not be cached.
await fetchOwnIpCountryCode('203.0.113.50', controller.signal);
// A later non-aborted call must perform a fresh lookup instead of a cached blank.
await expect(fetchOwnIpCountryCode('203.0.113.50')).resolves.toBe('vn');
expect(fetchMock).toHaveBeenCalledTimes(2);
vi.unstubAllGlobals();
});
});
describe('getApproximateCountryCode', () => {
it('returns undefined when the peer cannot be placed offline', () => {
expect(getApproximateCountryCode('/ip4/10.0.0.1/tcp/4001')).toBeUndefined();