From f9cb71027c17760122a45f82fe88660f206d5d92 Mon Sep 17 00:00:00 2001 From: "Quentin McGaw (desktop)" Date: Sun, 5 Sep 2021 22:54:10 +0000 Subject: [PATCH] Feat: location data at `/v1/publicip/ip` --- internal/publicip/alias.go | 2 +- internal/publicip/info.go | 9 ++------- internal/publicip/models/ipinfo.go | 27 +++++++++++++++++++++++++ internal/publicip/multi.go | 8 +++++--- internal/publicip/publicip.go | 8 +++++--- internal/publicip/runner.go | 4 +++- internal/publicip/state/publicip.go | 31 +++++++++++++---------------- internal/publicip/state/state.go | 8 ++++---- internal/server/publicip.go | 7 +------ 9 files changed, 62 insertions(+), 42 deletions(-) create mode 100644 internal/publicip/models/ipinfo.go diff --git a/internal/publicip/alias.go b/internal/publicip/alias.go index 87e13dfe..a68cb12e 100644 --- a/internal/publicip/alias.go +++ b/internal/publicip/alias.go @@ -2,5 +2,5 @@ package publicip import "github.com/qdm12/gluetun/internal/publicip/state" -type Getter = state.PublicIPGetter +type Getter = state.DataGetter type SettingsGetSetter = state.SettingsGetSetter diff --git a/internal/publicip/info.go b/internal/publicip/info.go index 6de30a0d..78f79b48 100644 --- a/internal/publicip/info.go +++ b/internal/publicip/info.go @@ -10,21 +10,16 @@ import ( "strings" "github.com/qdm12/gluetun/internal/constants" + "github.com/qdm12/gluetun/internal/publicip/models" ) -type Result struct { - Region string `json:"region"` - Country string `json:"country"` - City string `json:"city"` -} - var ( ErrTooManyRequests = errors.New("too many requests sent for this month") ErrBadHTTPStatus = errors.New("bad HTTP status received") ) func Info(ctx context.Context, client *http.Client, ip net.IP) ( //nolint:interfacer - result Result, err error) { + result models.IPInfoData, err error) { const baseURL = "https://ipinfo.io/" url := baseURL + ip.String() request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) diff --git a/internal/publicip/models/ipinfo.go b/internal/publicip/models/ipinfo.go new file mode 100644 index 00000000..b074248d --- /dev/null +++ b/internal/publicip/models/ipinfo.go @@ -0,0 +1,27 @@ +package models + +import "net" + +type IPInfoData struct { + IP net.IP `json:"public_ip"` + Region string `json:"region,omitempty"` + Country string `json:"country,omitempty"` + City string `json:"city,omitempty"` + Hostname string `json:"hostname,omitempty"` + Loc string `json:"loc,omitempty"` + Org string `json:"org,omitempty"` + Postal string `json:"postal,omitempty"` + Timezone string `json:"timezone,omitempty"` +} + +func (i IPInfoData) Copy() (copied IPInfoData) { + copied = i + copied.IP = make(net.IP, len(i.IP)) + copy(copied.IP, i.IP) + return copied +} + +func (i *IPInfoData) SetIP(ip net.IP) { + i.IP = make(net.IP, len(ip)) + copy(i.IP, ip) +} diff --git a/internal/publicip/multi.go b/internal/publicip/multi.go index 089f5747..c06243b6 100644 --- a/internal/publicip/multi.go +++ b/internal/publicip/multi.go @@ -4,6 +4,8 @@ import ( "context" "net" "net/http" + + "github.com/qdm12/gluetun/internal/publicip/models" ) // MultiInfo obtains the public IP address information for every IP @@ -13,12 +15,12 @@ import ( // an error is returned, so the results returned should be considered // incomplete in this case. func MultiInfo(ctx context.Context, client *http.Client, ips []net.IP) ( - results []Result, err error) { + results []models.IPInfoData, err error) { ctx, cancel := context.WithCancel(ctx) type asyncResult struct { index int - result Result + result models.IPInfoData err error } resultsCh := make(chan asyncResult) @@ -33,7 +35,7 @@ func MultiInfo(ctx context.Context, client *http.Client, ips []net.IP) ( }(i, ip) } - results = make([]Result, len(ips)) + results = make([]models.IPInfoData, len(ips)) for i := 0; i < len(ips); i++ { aResult := <-resultsCh if aResult.err != nil { diff --git a/internal/publicip/publicip.go b/internal/publicip/publicip.go index ef857be4..da9c1359 100644 --- a/internal/publicip/publicip.go +++ b/internal/publicip/publicip.go @@ -1,7 +1,9 @@ package publicip -import "net" +import ( + "github.com/qdm12/gluetun/internal/publicip/models" +) -func (l *Loop) GetPublicIP() (publicIP net.IP) { - return l.state.GetPublicIP() +func (l *Loop) GetData() (data models.IPInfoData) { + return l.state.GetData() } diff --git a/internal/publicip/runner.go b/internal/publicip/runner.go index 00514e65..855560d8 100644 --- a/internal/publicip/runner.go +++ b/internal/publicip/runner.go @@ -70,7 +70,6 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) { l.stopped <- struct{}{} case ip := <-ipCh: getCancel() - l.state.SetPublicIP(ip) message := "Public IP address is " + ip.String() result, err := Info(ctx, l.client, ip) @@ -81,6 +80,9 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) { } l.logger.Info(message) + result.SetIP(ip) + l.state.SetData(result) + filepath := l.state.GetSettings().IPFilepath err = persistPublicIP(filepath, ip.String(), l.puid, l.pgid) if err != nil { diff --git a/internal/publicip/state/publicip.go b/internal/publicip/state/publicip.go index 4ae29b06..ff27a9df 100644 --- a/internal/publicip/state/publicip.go +++ b/internal/publicip/state/publicip.go @@ -1,29 +1,26 @@ package state import ( - "net" + "github.com/qdm12/gluetun/internal/publicip/models" ) -type PublicIPGetSetter interface { - PublicIPGetter - SetPublicIP(publicIP net.IP) +type DataGetSetter interface { + DataGetter + SetData(data models.IPInfoData) } -type PublicIPGetter interface { - GetPublicIP() (publicIP net.IP) +type DataGetter interface { + GetData() (data models.IPInfoData) } -func (s *State) GetPublicIP() (publicIP net.IP) { - s.publicIPMu.RLock() - defer s.publicIPMu.RUnlock() - publicIP = make(net.IP, len(s.publicIP)) - copy(publicIP, s.publicIP) - return publicIP +func (s *State) GetData() (data models.IPInfoData) { + s.ipDataMu.RLock() + defer s.ipDataMu.RUnlock() + return s.ipData.Copy() } -func (s *State) SetPublicIP(publicIP net.IP) { - s.settingsMu.Lock() - defer s.settingsMu.Unlock() - s.publicIP = make(net.IP, len(publicIP)) - copy(s.publicIP, publicIP) +func (s *State) SetData(data models.IPInfoData) { + s.ipDataMu.Lock() + defer s.ipDataMu.Unlock() + s.ipData = data.Copy() } diff --git a/internal/publicip/state/state.go b/internal/publicip/state/state.go index b350103e..a8ea95b0 100644 --- a/internal/publicip/state/state.go +++ b/internal/publicip/state/state.go @@ -1,18 +1,18 @@ package state import ( - "net" "sync" "github.com/qdm12/gluetun/internal/configuration" "github.com/qdm12/gluetun/internal/loopstate" + "github.com/qdm12/gluetun/internal/publicip/models" ) var _ Manager = (*State)(nil) type Manager interface { SettingsGetSetter - PublicIPGetSetter + DataGetSetter } func New(statusApplier loopstate.Applier, @@ -31,8 +31,8 @@ type State struct { settings configuration.PublicIP settingsMu sync.RWMutex - publicIP net.IP - publicIPMu sync.RWMutex + ipData models.IPInfoData + ipDataMu sync.RWMutex updateTicker chan<- struct{} } diff --git a/internal/server/publicip.go b/internal/server/publicip.go index ce5e61fc..477b1457 100644 --- a/internal/server/publicip.go +++ b/internal/server/publicip.go @@ -38,14 +38,9 @@ func (h *publicIPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } -type publicIPWrapper struct { - PublicIP string `json:"public_ip"` -} - func (h *publicIPHandler) getPublicIP(w http.ResponseWriter) { - publicIP := h.looper.GetPublicIP() + data := h.looper.GetData() encoder := json.NewEncoder(w) - data := publicIPWrapper{PublicIP: publicIP.String()} if err := encoder.Encode(data); err != nil { h.logger.Warn(err.Error()) w.WriteHeader(http.StatusInternalServerError)