Feat: location data at /v1/publicip/ip
This commit is contained in:
@@ -2,5 +2,5 @@ package publicip
|
|||||||
|
|
||||||
import "github.com/qdm12/gluetun/internal/publicip/state"
|
import "github.com/qdm12/gluetun/internal/publicip/state"
|
||||||
|
|
||||||
type Getter = state.PublicIPGetter
|
type Getter = state.DataGetter
|
||||||
type SettingsGetSetter = state.SettingsGetSetter
|
type SettingsGetSetter = state.SettingsGetSetter
|
||||||
|
|||||||
@@ -10,21 +10,16 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"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 (
|
var (
|
||||||
ErrTooManyRequests = errors.New("too many requests sent for this month")
|
ErrTooManyRequests = errors.New("too many requests sent for this month")
|
||||||
ErrBadHTTPStatus = errors.New("bad HTTP status received")
|
ErrBadHTTPStatus = errors.New("bad HTTP status received")
|
||||||
)
|
)
|
||||||
|
|
||||||
func Info(ctx context.Context, client *http.Client, ip net.IP) ( //nolint:interfacer
|
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/"
|
const baseURL = "https://ipinfo.io/"
|
||||||
url := baseURL + ip.String()
|
url := baseURL + ip.String()
|
||||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
|
|||||||
27
internal/publicip/models/ipinfo.go
Normal file
27
internal/publicip/models/ipinfo.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/publicip/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MultiInfo obtains the public IP address information for every IP
|
// 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
|
// an error is returned, so the results returned should be considered
|
||||||
// incomplete in this case.
|
// incomplete in this case.
|
||||||
func MultiInfo(ctx context.Context, client *http.Client, ips []net.IP) (
|
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)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
type asyncResult struct {
|
type asyncResult struct {
|
||||||
index int
|
index int
|
||||||
result Result
|
result models.IPInfoData
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
resultsCh := make(chan asyncResult)
|
resultsCh := make(chan asyncResult)
|
||||||
@@ -33,7 +35,7 @@ func MultiInfo(ctx context.Context, client *http.Client, ips []net.IP) (
|
|||||||
}(i, ip)
|
}(i, ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
results = make([]Result, len(ips))
|
results = make([]models.IPInfoData, len(ips))
|
||||||
for i := 0; i < len(ips); i++ {
|
for i := 0; i < len(ips); i++ {
|
||||||
aResult := <-resultsCh
|
aResult := <-resultsCh
|
||||||
if aResult.err != nil {
|
if aResult.err != nil {
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package publicip
|
package publicip
|
||||||
|
|
||||||
import "net"
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/publicip/models"
|
||||||
|
)
|
||||||
|
|
||||||
func (l *Loop) GetPublicIP() (publicIP net.IP) {
|
func (l *Loop) GetData() (data models.IPInfoData) {
|
||||||
return l.state.GetPublicIP()
|
return l.state.GetData()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
|
|||||||
l.stopped <- struct{}{}
|
l.stopped <- struct{}{}
|
||||||
case ip := <-ipCh:
|
case ip := <-ipCh:
|
||||||
getCancel()
|
getCancel()
|
||||||
l.state.SetPublicIP(ip)
|
|
||||||
|
|
||||||
message := "Public IP address is " + ip.String()
|
message := "Public IP address is " + ip.String()
|
||||||
result, err := Info(ctx, l.client, ip)
|
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)
|
l.logger.Info(message)
|
||||||
|
|
||||||
|
result.SetIP(ip)
|
||||||
|
l.state.SetData(result)
|
||||||
|
|
||||||
filepath := l.state.GetSettings().IPFilepath
|
filepath := l.state.GetSettings().IPFilepath
|
||||||
err = persistPublicIP(filepath, ip.String(), l.puid, l.pgid)
|
err = persistPublicIP(filepath, ip.String(), l.puid, l.pgid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,29 +1,26 @@
|
|||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"github.com/qdm12/gluetun/internal/publicip/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PublicIPGetSetter interface {
|
type DataGetSetter interface {
|
||||||
PublicIPGetter
|
DataGetter
|
||||||
SetPublicIP(publicIP net.IP)
|
SetData(data models.IPInfoData)
|
||||||
}
|
}
|
||||||
|
|
||||||
type PublicIPGetter interface {
|
type DataGetter interface {
|
||||||
GetPublicIP() (publicIP net.IP)
|
GetData() (data models.IPInfoData)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) GetPublicIP() (publicIP net.IP) {
|
func (s *State) GetData() (data models.IPInfoData) {
|
||||||
s.publicIPMu.RLock()
|
s.ipDataMu.RLock()
|
||||||
defer s.publicIPMu.RUnlock()
|
defer s.ipDataMu.RUnlock()
|
||||||
publicIP = make(net.IP, len(s.publicIP))
|
return s.ipData.Copy()
|
||||||
copy(publicIP, s.publicIP)
|
|
||||||
return publicIP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) SetPublicIP(publicIP net.IP) {
|
func (s *State) SetData(data models.IPInfoData) {
|
||||||
s.settingsMu.Lock()
|
s.ipDataMu.Lock()
|
||||||
defer s.settingsMu.Unlock()
|
defer s.ipDataMu.Unlock()
|
||||||
s.publicIP = make(net.IP, len(publicIP))
|
s.ipData = data.Copy()
|
||||||
copy(s.publicIP, publicIP)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration"
|
"github.com/qdm12/gluetun/internal/configuration"
|
||||||
"github.com/qdm12/gluetun/internal/loopstate"
|
"github.com/qdm12/gluetun/internal/loopstate"
|
||||||
|
"github.com/qdm12/gluetun/internal/publicip/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Manager = (*State)(nil)
|
var _ Manager = (*State)(nil)
|
||||||
|
|
||||||
type Manager interface {
|
type Manager interface {
|
||||||
SettingsGetSetter
|
SettingsGetSetter
|
||||||
PublicIPGetSetter
|
DataGetSetter
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(statusApplier loopstate.Applier,
|
func New(statusApplier loopstate.Applier,
|
||||||
@@ -31,8 +31,8 @@ type State struct {
|
|||||||
settings configuration.PublicIP
|
settings configuration.PublicIP
|
||||||
settingsMu sync.RWMutex
|
settingsMu sync.RWMutex
|
||||||
|
|
||||||
publicIP net.IP
|
ipData models.IPInfoData
|
||||||
publicIPMu sync.RWMutex
|
ipDataMu sync.RWMutex
|
||||||
|
|
||||||
updateTicker chan<- struct{}
|
updateTicker chan<- struct{}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
func (h *publicIPHandler) getPublicIP(w http.ResponseWriter) {
|
||||||
publicIP := h.looper.GetPublicIP()
|
data := h.looper.GetData()
|
||||||
encoder := json.NewEncoder(w)
|
encoder := json.NewEncoder(w)
|
||||||
data := publicIPWrapper{PublicIP: publicIP.String()}
|
|
||||||
if err := encoder.Encode(data); err != nil {
|
if err := encoder.Encode(data); err != nil {
|
||||||
h.logger.Warn(err.Error())
|
h.logger.Warn(err.Error())
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
|||||||
Reference in New Issue
Block a user