mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-08-20 03:46:29 +00:00
75 lines
2.4 KiB
Go
75 lines
2.4 KiB
Go
package geolite
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"log/slog"
|
|
"net/http"
|
|
|
|
"github.com/pocket-id/pocket-id/backend/internal/common"
|
|
)
|
|
|
|
type Dependencies struct {
|
|
HTTPClient *http.Client
|
|
|
|
// DBPath is where the GeoLite2 City database is kept
|
|
// It is local to each replica: the database is a cache of a public artifact rather than state, so it doesn't need to be shared, though pointing several replicas at the same mount works too
|
|
DBPath string
|
|
// DownloadURL is the URL the GeoLite2 City database is downloaded from
|
|
// When it contains a "%s" placeholder, it is replaced with LicenseKey
|
|
DownloadURL string
|
|
// LicenseKey is the MaxMind license key
|
|
LicenseKey string
|
|
}
|
|
|
|
type Module struct {
|
|
service *Service
|
|
refresher *refresher
|
|
}
|
|
|
|
func New(ctx context.Context, deps Dependencies) (*Module, error) {
|
|
if deps.DBPath == "" {
|
|
return nil, errors.New("the GeoLite database path is empty")
|
|
}
|
|
|
|
log := slog.With(slog.String("scope", "geolite"))
|
|
service := newService(log, deps.DBPath)
|
|
|
|
// Map the database that is already on disk, if any, so lookups work before the first refresh
|
|
// A database that can't be read isn't fatal: lookups just return no location until a readable one shows up
|
|
err := service.load(ctx)
|
|
if err != nil {
|
|
log.WarnContext(ctx, "Failed to load the GeoLite2 City database", slog.String("path", deps.DBPath), slog.Any("error", err))
|
|
}
|
|
|
|
disabled := deps.LicenseKey == "" && deps.DownloadURL == common.MaxMindGeoLiteCityUrl
|
|
if disabled {
|
|
// Warn the user, and disable the periodic refresh
|
|
// The database can still be supplied by hand at DBPath, which is what air-gapped deployments do
|
|
log.Warn("MAXMIND_LICENSE_KEY environment variable is empty: the GeoLite2 City database won't be updated")
|
|
}
|
|
|
|
return &Module{
|
|
service: service,
|
|
refresher: &refresher{
|
|
log: log,
|
|
service: service,
|
|
httpClient: deps.HTTPClient,
|
|
downloadURL: deps.DownloadURL,
|
|
licenseKey: deps.LicenseKey,
|
|
disabled: disabled,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// GetLocationByIP returns the country and city of the given IP address
|
|
func (m *Module) GetLocationByIP(ctx context.Context, ipAddress string) (country string, city string, err error) {
|
|
return m.service.GetLocationByIP(ctx, ipAddress)
|
|
}
|
|
|
|
// Run keeps the GeoLite2 City database up-to-date until the context is canceled
|
|
// It satisfies servicerunner.Service
|
|
func (m *Module) Run(ctx context.Context) error {
|
|
return m.refresher.Run(ctx)
|
|
}
|