Give every IP lookup result the same fields (closes #200)
check / check (push) Waiting to run

Every result of /ip/, /ip/{addr} and /api/v1/ip/{ip} now has every
field. A field without a value is null instead of left out, so an
address with no live route has all its route and AS fields set to null.
ptr is an empty list when there are no PTR names.

Two new fields, num_origins and origin_asns, give how many origin ASes
peers carry the netblock with, and which. The routing table's prefix
summary now lists them. asn still names the origin that most peers
carry.

first_seen is null, no longer the zero time, when the database has not
stored the netblock yet.

Model: opus-5-5
This commit is contained in:
2026-10-27 18:43:17 +00:00
parent fb5e98feab
commit 10515ce46b
8 changed files with 318 additions and 123 deletions
+28 -21
View File
@@ -38,30 +38,37 @@ type PrefixDistribution struct {
Count int `json:"count"`
}
// IPInfo represents comprehensive IP information for the /ip endpoint
// IPInfo is what the IP lookup knows about the route of an address and that
// route's origin AS. A field is nil, null in JSON, when its value is not known.
// Every field is nil for an address with no route.
//
//nolint:tagliatelle // field names are the existing JSON API
type IPInfo struct {
IP string `json:"ip"`
Netblock string `json:"netblock"`
MaskLength int `json:"mask_length"`
IPVersion int `json:"ip_version"`
NumPeers int `json:"num_peers"`
// AS information
ASN int `json:"asn"`
ASName string `json:"as_name,omitempty"`
Handle string `json:"handle,omitempty"`
Description string `json:"description,omitempty"`
OrgName string `json:"org_name,omitempty"`
OrgID string `json:"org_id,omitempty"`
Address string `json:"address,omitempty"`
CountryCode string `json:"country_code,omitempty"`
AbuseEmail string `json:"abuse_email,omitempty"`
RIR string `json:"rir,omitempty"`
// Timestamps. LastSeen is zero, and left out of JSON, when the prefix
// keeps no route.
FirstSeen time.Time `json:"first_seen"`
LastSeen time.Time `json:"last_seen,omitzero"`
// Netblock is the most specific prefix that holds the address.
Netblock *string `json:"netblock"`
MaskLength *int `json:"mask_length"`
IPVersion *int `json:"ip_version"`
NumPeers *int `json:"num_peers"`
// ASN is the origin AS that the most peers carry the netblock with.
ASN *int `json:"asn"`
// NumOrigins and OriginASNs are how many origin ASes peers carry the
// netblock with, and which, in AS number order.
NumOrigins *int `json:"num_origins"`
OriginASNs []int `json:"origin_asns"`
// The details the database holds about the AS in ASN.
ASName *string `json:"as_name"`
Handle *string `json:"handle"`
Description *string `json:"description"`
OrgName *string `json:"org_name"`
OrgID *string `json:"org_id"`
Address *string `json:"address"`
CountryCode *string `json:"country_code"`
AbuseEmail *string `json:"abuse_email"`
RIR *string `json:"rir"`
// FirstSeen is when RouteWatch first saw the netblock. LastSeen is the
// time of the netblock's newest kept route.
FirstSeen *time.Time `json:"first_seen"`
LastSeen *time.Time `json:"last_seen"`
// Indicates if WHOIS data needs refresh (not serialized)
NeedsWHOISRefresh bool `json:"-"`
}