From cc062da67fedf9d5779caf8458425d5efea48306 Mon Sep 17 00:00:00 2001 From: Peter Sanchez Date: Wed, 27 Nov 2024 13:57:55 -0600 Subject: [PATCH] Fixing issue with IP not triggering analytics --- analytics/helpers.go | 3 ++- analytics/routes_test.go | 16 +++++++++++----- cmd/api/main.go | 1 + cmd/links/main.go | 1 + cmd/list/main.go | 1 + cmd/short/main.go | 1 + cmd/test/helpers.go | 2 ++ core/middleware.go | 11 +++++++++++ helpers.go | 25 +++++++++++++++++++++---- 9 files changed, 51 insertions(+), 10 deletions(-) diff --git a/analytics/helpers.go b/analytics/helpers.go index 9aff073..dcc346a 100644 --- a/analytics/helpers.go +++ b/analytics/helpers.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "fmt" + "links" "links/models" "net" "net/http" @@ -116,7 +117,7 @@ func AddMetaAnalytics(ctx context.Context, req *http.Request, dailyTotalID int, } } - ip := req.Header.Get("X-FORWARDED-FOR") + ip := links.IPForContext(req.Context()) if ip != "" { db, err := geoip2.Open(geoPath) if err != nil { diff --git a/analytics/routes_test.go b/analytics/routes_test.go index 5996115..c6cd46f 100644 --- a/analytics/routes_test.go +++ b/analytics/routes_test.go @@ -148,16 +148,22 @@ func TestAPI(t *testing.T) { c.NoError(err) req := httptest.NewRequest(http.MethodGet, "/", nil) + path, ok := srv.Config.File.Get("geo", "path") + if ok { + // test config has geodb path set. Let's set an IP so we can test the db integration + req = req.WithContext(links.IPContext(request.Context(), "142.250.217.196")) // www.google.com + } + // Add fake analytics entries - err = analytics.AddAnalytics(dbCtx, req, short.ID, analytics.LinkShortAnalyticsFilter, "") + err = analytics.AddAnalytics(dbCtx, req, short.ID, analytics.LinkShortAnalyticsFilter, path) c.NoError(err) - err = analytics.AddAnalytics(dbCtx, req, short.ID, analytics.LinkShortAnalyticsFilter, "") + err = analytics.AddAnalytics(dbCtx, req, short.ID, analytics.LinkShortAnalyticsFilter, path) c.NoError(err) - err = analytics.AddAnalytics(dbCtx, req, short.ID, analytics.LinkShortAnalyticsFilter, "") + err = analytics.AddAnalytics(dbCtx, req, short.ID, analytics.LinkShortAnalyticsFilter, path) c.NoError(err) - err = analytics.AddAnalytics(dbCtx, req, short.ID, analytics.LinkShortAnalyticsFilter, "") + err = analytics.AddAnalytics(dbCtx, req, short.ID, analytics.LinkShortAnalyticsFilter, path) c.NoError(err) - err = analytics.AddAnalytics(dbCtx, req, short.ID, analytics.LinkShortAnalyticsFilter, "") + err = analytics.AddAnalytics(dbCtx, req, short.ID, analytics.LinkShortAnalyticsFilter, path) c.NoError(err) today := time.Now().UTC() diff --git a/cmd/api/main.go b/cmd/api/main.go index c1b1d6f..53b0365 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -122,6 +122,7 @@ func run() error { WithQueues(eq, wq). WithMiddleware( database.Middleware(db), + core.RemoteIPMiddleware, loaders.Middleware(), core.TimezoneContext(), crypto.Middleware(entropy), diff --git a/cmd/links/main.go b/cmd/links/main.go index 39689f9..658af8e 100644 --- a/cmd/links/main.go +++ b/cmd/links/main.go @@ -213,6 +213,7 @@ func run() error { WithQueues(eq, wq, wqi). WithMiddleware( database.Middleware(db), + core.RemoteIPMiddleware, core.TimezoneContext(), crypto.Middleware(entropy), domain.DomainContext(models.DomainServiceLinks), diff --git a/cmd/list/main.go b/cmd/list/main.go index 24ab79f..1915cdd 100644 --- a/cmd/list/main.go +++ b/cmd/list/main.go @@ -84,6 +84,7 @@ func run() error { WithQueues(eq, wq). WithMiddleware( database.Middleware(db), + core.RemoteIPMiddleware, core.TimezoneContext(), crypto.Middleware(entropy), domain.DomainContext(models.DomainServiceList), diff --git a/cmd/short/main.go b/cmd/short/main.go index f975d04..ff3aa19 100644 --- a/cmd/short/main.go +++ b/cmd/short/main.go @@ -82,6 +82,7 @@ func run() error { WithQueues(eq, wq). WithMiddleware( database.Middleware(db), + core.RemoteIPMiddleware, core.TimezoneContext(), crypto.Middleware(entropy), domain.DomainContext(models.DomainServiceShort), diff --git a/cmd/test/helpers.go b/cmd/test/helpers.go index 5e03b55..f002b5e 100644 --- a/cmd/test/helpers.go +++ b/cmd/test/helpers.go @@ -170,6 +170,7 @@ func NewAPITestServer(t *testing.T) (*server.Server, *echo.Echo, string) { DefaultMiddlewareWithConfig(mwConf). WithMiddleware( database.Middleware(db), + core.RemoteIPMiddleware, core.TimezoneContext(), crypto.Middleware(entropy), core.InternalAuthMiddleware(accounts.NewUserFetch()), @@ -245,6 +246,7 @@ func getMWChain(s *server.Server, f echo.HandlerFunc, user *models.User) echo.Ha entropy, _ := s.Config.File.Get("access", "entropy") cryptoMiddleware := crypto.Middleware(entropy) handlerFunc := cryptoMiddleware(f) + handlerFunc = core.RemoteIPMiddleware(handlerFunc) timezoneMiddleware := core.TimezoneContext() handlerFunc = timezoneMiddleware(handlerFunc) serverMiddleware := server.Middleware(s) diff --git a/core/middleware.go b/core/middleware.go index e8a72f4..304bd0c 100644 --- a/core/middleware.go +++ b/core/middleware.go @@ -91,3 +91,14 @@ func CORSReadOnlyMiddleware(next echo.HandlerFunc) echo.HandlerFunc { return next(c) } } + +func RemoteIPMiddleware(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + c.SetRequest( + c.Request().WithContext( + links.IPContext(c.Request().Context(), c.RealIP()), + ), + ) + return next(c) + } +} diff --git a/helpers.go b/helpers.go index 1585d95..adc879a 100644 --- a/helpers.go +++ b/helpers.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "encoding/xml" + "errors" "fmt" "html/template" "io" @@ -43,6 +44,10 @@ import ( "netlandish.com/x/gobwebs/validate" ) +type contextKey struct { + name string +} + type errorExtension struct { Code int Field string @@ -686,10 +691,6 @@ func ArchiveURLSnapshot(ctx context.Context, orgLink *models.OrgLink) error { return nil } -type contextKey struct { - name string -} - var langCtxKey = &contextKey{"userLang"} func LangContext(c echo.Context) context.Context { @@ -1135,3 +1136,19 @@ func NewRateLimiterConfig(conf *config.Config) (middleware.RateLimiterConfig, er } return rlConfig, nil } + +var IPCtxKey = &contextKey{"remote_ip"} + +// IPContext adds a domain model to context for immediate use +func IPContext(ctx context.Context, ip string) context.Context { + return context.WithValue(ctx, IPCtxKey, ip) +} + +// IPForContext fetches current domain from the request context +func IPForContext(ctx context.Context) string { + ip, ok := ctx.Value(IPCtxKey).(string) + if !ok { + panic(errors.New("Invalid IP context")) + } + return ip +} -- 2.45.2