package updater import ( "context" "encoding/json" "errors" "fmt" "net/http" "net/netip" ) var ErrHTTPStatusCodeNotOK = errors.New("HTTP status code not OK") type apiData struct { LogicalServers []logicalServer `json:"LogicalServers"` } type logicalServer struct { Name string `json:"Name"` ExitCountry string `json:"ExitCountry"` Region *string `json:"Region"` City *string `json:"City"` Servers []physicalServer `json:"Servers"` Features uint16 `json:"Features"` Tier *uint8 `json:"Tier,omitempty"` } type physicalServer struct { EntryIP netip.Addr `json:"EntryIP"` ExitIP netip.Addr `json:"ExitIP"` Domain string `json:"Domain"` Status uint8 `json:"Status"` X25519PublicKey string `json:"X25519PublicKey"` } func fetchAPI(ctx context.Context, client *http.Client) ( data apiData, err error, ) { const url = "https://api.protonmail.ch/vpn/logicals" request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return data, err } response, err := client.Do(request) if err != nil { return data, err } defer response.Body.Close() if response.StatusCode != http.StatusOK { return data, fmt.Errorf("%w: %d %s", ErrHTTPStatusCodeNotOK, response.StatusCode, response.Status) } decoder := json.NewDecoder(response.Body) if err := decoder.Decode(&data); err != nil { return data, fmt.Errorf("decoding response body: %w", err) } if err := response.Body.Close(); err != nil { return data, err } return data, nil }