check / check (push) Canceled after 0s
Each handler took the latest time per key in a batch and inserted it as a new row's first_seen, so a key updated twice in one batch, seconds apart, was stored first seen at the later time, and the IP lookup could answer with a prefix first_seen after the route's last_seen. The prefix handler now keeps the earliest time of all of the batch's updates for each prefix. The AS, peer and peering handlers keep the earliest and the latest time per key in database.SeenTimes, and GetOrCreateASNBatch, UpdatePeerBatch and RecordPeering store a new row first seen at the earliest and last seen at the latest. A stored row still has only its last_seen set, to the latest. Model: opus-5-5
173 lines
5.3 KiB
Go
173 lines
5.3 KiB
Go
package database
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// ASN represents an Autonomous System Number with its metadata including
|
|
// handle, description, WHOIS data, and first/last seen timestamps.
|
|
//
|
|
//nolint:tagliatelle // field names are the existing JSON API
|
|
type ASN struct {
|
|
ASN int `json:"asn"`
|
|
Handle string `json:"handle"`
|
|
Description string `json:"description"`
|
|
// WHOIS parsed fields
|
|
ASName string `json:"as_name,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"`
|
|
AbusePhone string `json:"abuse_phone,omitempty"`
|
|
TechEmail string `json:"tech_email,omitempty"`
|
|
TechPhone string `json:"tech_phone,omitempty"`
|
|
RIR string `json:"rir,omitempty"` // ARIN, RIPE, APNIC, LACNIC, AFRINIC
|
|
RIRRegDate *time.Time `json:"rir_registration_date,omitempty"`
|
|
RIRLastMod *time.Time `json:"rir_last_modified,omitempty"`
|
|
WHOISRaw string `json:"whois_raw,omitempty"`
|
|
// Timestamps
|
|
FirstSeen time.Time `json:"first_seen"`
|
|
LastSeen time.Time `json:"last_seen"`
|
|
WHOISUpdatedAt *time.Time `json:"whois_updated_at,omitempty"`
|
|
}
|
|
|
|
// Prefix represents an IP prefix (CIDR block) with its IP version (4 or 6)
|
|
// and the time it was first seen.
|
|
type Prefix struct {
|
|
Prefix string
|
|
IPVersion int // 4 or 6
|
|
FirstSeen time.Time
|
|
}
|
|
|
|
// Announcement represents a BGP announcement or withdrawal event,
|
|
// containing the prefix, AS path, origin ASN, peer ASN, next hop, and timestamp.
|
|
type Announcement struct {
|
|
ID uuid.UUID
|
|
PrefixID uuid.UUID
|
|
PeerASN int
|
|
OriginASN int
|
|
Path string // JSON-encoded AS path
|
|
NextHop string
|
|
Timestamp time.Time
|
|
IsWithdrawal bool
|
|
}
|
|
|
|
// ASNPeering represents a peering relationship between two ASNs,
|
|
// stored with the lower ASN as ASA and the higher as ASB.
|
|
type ASNPeering struct {
|
|
ID uuid.UUID
|
|
ASA int
|
|
ASB int
|
|
FirstSeen time.Time
|
|
LastSeen time.Time
|
|
}
|
|
|
|
// LiveRoute represents a route in the live routing table
|
|
//
|
|
//nolint:tagliatelle // field names are the existing JSON API
|
|
type LiveRoute struct {
|
|
Prefix string `json:"prefix"`
|
|
MaskLength int `json:"mask_length"`
|
|
IPVersion int `json:"ip_version"`
|
|
OriginASN int `json:"origin_asn"`
|
|
PeerIP string `json:"peer_ip"`
|
|
ASPath []int `json:"as_path"`
|
|
NextHop string `json:"next_hop"`
|
|
LastUpdated time.Time `json:"last_updated"`
|
|
}
|
|
|
|
// PrefixDistribution represents the distribution of prefixes by mask length
|
|
//
|
|
//nolint:tagliatelle // field names are the existing JSON API
|
|
type PrefixDistribution struct {
|
|
MaskLength int `json:"mask_length"`
|
|
Count int `json:"count"`
|
|
}
|
|
|
|
// IPInfo represents comprehensive IP information for the /ip endpoint
|
|
//
|
|
//nolint:tagliatelle // field names are the existing JSON API
|
|
type IPInfo struct {
|
|
IP string `json:"ip"`
|
|
PTR []string `json:"ptr,omitempty"`
|
|
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
|
|
FirstSeen time.Time `json:"first_seen"`
|
|
LastSeen time.Time `json:"last_seen"`
|
|
// Indicates if WHOIS data needs refresh (not serialized)
|
|
NeedsWHOISRefresh bool `json:"-"`
|
|
}
|
|
|
|
// LiveRouteDeletion represents parameters for deleting a live route
|
|
type LiveRouteDeletion struct {
|
|
Prefix string
|
|
PeerIP string
|
|
IPVersion int
|
|
}
|
|
|
|
// SeenTimes is the earliest and the latest time one batch of updates saw an AS,
|
|
// a peer or a peering. A row the batch adds is stored first seen at First and
|
|
// last seen at Last; a row already stored keeps its first_seen and has its
|
|
// last_seen set to Last.
|
|
type SeenTimes struct {
|
|
First time.Time
|
|
Last time.Time
|
|
}
|
|
|
|
// Add makes t the earliest time of s when it is earlier than First, and the
|
|
// latest when it is later than Last. The zero SeenTimes holds no time yet, so
|
|
// the first Add sets both.
|
|
func (s *SeenTimes) Add(t time.Time) {
|
|
if s.First.IsZero() || t.Before(s.First) {
|
|
s.First = t
|
|
}
|
|
|
|
if s.Last.IsZero() || t.After(s.Last) {
|
|
s.Last = t
|
|
}
|
|
}
|
|
|
|
// PeerUpdate represents parameters for updating a peer. PeerASN and
|
|
// MessageType are those of the peer's latest update.
|
|
type PeerUpdate struct {
|
|
PeerIP string
|
|
PeerASN int
|
|
MessageType string
|
|
Seen SeenTimes
|
|
}
|
|
|
|
// ASNWHOISUpdate contains WHOIS data for updating an ASN record.
|
|
type ASNWHOISUpdate struct {
|
|
ASN int
|
|
ASName string
|
|
OrgName string
|
|
OrgID string
|
|
Address string
|
|
CountryCode string
|
|
AbuseEmail string
|
|
AbusePhone string
|
|
TechEmail string
|
|
TechPhone string
|
|
RIR string
|
|
RIRRegDate *time.Time
|
|
RIRLastMod *time.Time
|
|
WHOISRaw string
|
|
}
|