From 797faa14a398e1ae9e38b23c756462e994996d72 Mon Sep 17 00:00:00 2001 From: violog <51th.apprent1ce.f0rce@gmail.com> Date: Mon, 19 Feb 2024 16:05:50 +0200 Subject: [PATCH 1/6] Connector implementation to add events and verify passport --- go.mod | 4 +++- go.sum | 6 ++++- pkg/connector/client.go | 28 ++++++++++++++++++++++ pkg/connector/config.go | 52 +++++++++++++++++++++++++++++++++++++++++ pkg/connector/main.go | 30 ++++++++++++++++++++++++ pkg/connector/models.go | 15 ++++++++++++ 6 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 pkg/connector/client.go create mode 100644 pkg/connector/config.go create mode 100644 pkg/connector/main.go create mode 100644 pkg/connector/models.go diff --git a/go.mod b/go.mod index 846fc0c..9c81b84 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/rubenv/sql-migrate v1.6.1 gitlab.com/distributed_lab/ape v1.7.1 gitlab.com/distributed_lab/figure/v3 v3.1.3 + gitlab.com/distributed_lab/json-api-connector v0.2.7 gitlab.com/distributed_lab/kit v1.11.2 gitlab.com/distributed_lab/logan v3.8.1+incompatible gitlab.com/distributed_lab/running v0.0.0-20200706131153-4af0e83eb96c @@ -62,6 +63,7 @@ require ( github.com/dustin/go-humanize v1.0.1-0.20200219035652-afde56e7acac // indirect github.com/dvsekhvalnov/jose2go v1.5.0 // indirect github.com/ethereum/c-kzg-4844 v0.4.0 // indirect + github.com/fatih/structs v1.1.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/getsentry/raven-go v0.2.0 // indirect github.com/getsentry/sentry-go v0.26.0 // indirect @@ -144,7 +146,7 @@ require ( github.com/tklauser/numcpus v0.6.1 // indirect github.com/zondax/hid v0.9.1 // indirect github.com/zondax/ledger-go v0.14.1 // indirect - gitlab.com/distributed_lab/figure v2.1.0+incompatible // indirect + gitlab.com/distributed_lab/figure v2.1.2+incompatible // indirect gitlab.com/distributed_lab/lorem v0.2.1 // indirect go.etcd.io/bbolt v1.3.6 // indirect go.uber.org/multierr v1.11.0 // indirect diff --git a/go.sum b/go.sum index eb9a50a..468345b 100644 --- a/go.sum +++ b/go.sum @@ -735,6 +735,7 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqL github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= @@ -1373,10 +1374,13 @@ github.com/zondax/ledger-go v0.14.1/go.mod h1:fZ3Dqg6qcdXWSOJFKMG8GCTnD7slO/RL2f gitlab.com/distributed_lab/ape v1.6.1/go.mod h1:Qy9Y2arL0hmZIpVpctGEFhdrVsjWtyVJ5G+bZWcFT4s= gitlab.com/distributed_lab/ape v1.7.1 h1:LpTmZgG7Lvx6ulopQbH2aWI3s8ey9FsKVjbic3ZQIy4= gitlab.com/distributed_lab/ape v1.7.1/go.mod h1:Qy9Y2arL0hmZIpVpctGEFhdrVsjWtyVJ5G+bZWcFT4s= -gitlab.com/distributed_lab/figure v2.1.0+incompatible h1:8kNtvWO91BSQ4OsqL2P3qNWSBnh/Q/TdWB8vHy8xvNI= gitlab.com/distributed_lab/figure v2.1.0+incompatible/go.mod h1:tk+aPBohT49MGPLy5+eVbE1HpD/CaC5drBHfVpRI8eE= +gitlab.com/distributed_lab/figure v2.1.2+incompatible h1:xO1KCYPK9KFx6OUBOaJ62d8vYd1R3aNgidHlC/ZtVBA= +gitlab.com/distributed_lab/figure v2.1.2+incompatible/go.mod h1:tk+aPBohT49MGPLy5+eVbE1HpD/CaC5drBHfVpRI8eE= gitlab.com/distributed_lab/figure/v3 v3.1.3 h1:gCHplT1Ih8B1s4eYTeAhRZyto3gIWoUCUj3yYfNM4r8= gitlab.com/distributed_lab/figure/v3 v3.1.3/go.mod h1:gYbCEdQBQCVEg+ap0zrpjY56BU95k9H8ELebL1ChONo= +gitlab.com/distributed_lab/json-api-connector v0.2.7 h1:cwKDOxY/WLNFUJqpj90gGwnrdOZctQPD6RiTEJ7rNw4= +gitlab.com/distributed_lab/json-api-connector v0.2.7/go.mod h1:/jNqcDl22LxF06EOYsU8DvLpYwB5okFvesDotsj4ClA= gitlab.com/distributed_lab/kit v1.11.2 h1:3GYAVe/ih5fvFuM/44zIorv9mUyD3JBQe/5v+GL7x+k= gitlab.com/distributed_lab/kit v1.11.2/go.mod h1:MZj5Vb71YBWJ2wLAb9fDvlCYKewmNDNVWjAiERwgbdA= gitlab.com/distributed_lab/logan v3.7.2+incompatible/go.mod h1:25oL/FPFXmyYzWeA6vahMvnFJV8P7mOx0jZhRP7nhlc= diff --git a/pkg/connector/client.go b/pkg/connector/client.go new file mode 100644 index 0000000..da68d11 --- /dev/null +++ b/pkg/connector/client.go @@ -0,0 +1,28 @@ +package connector + +import ( + "net/http" + "net/url" + "path" +) + +type client struct { + base *url.URL + http *http.Client +} + +func (c *client) Do(r *http.Request) (*http.Response, error) { + return c.http.Do(r) +} + +func (c *client) Resolve(endpoint *url.URL) (string, error) { + base := *c.base + + if base.Path != "" { + endpoint.Path = path.Join(base.Path, endpoint.Path) + base.Path = "" + } + + resolved := base.ResolveReference(endpoint) + return resolved.String(), nil +} diff --git a/pkg/connector/config.go b/pkg/connector/config.go new file mode 100644 index 0000000..9052ef1 --- /dev/null +++ b/pkg/connector/config.go @@ -0,0 +1,52 @@ +package connector + +import ( + "fmt" + "net/http" + "net/url" + "time" + + "gitlab.com/distributed_lab/figure/v3" + "gitlab.com/distributed_lab/kit/comfig" + "gitlab.com/distributed_lab/kit/kv" +) + +const defaultTimeout = 10 * time.Second + +type Pointer interface { + Points() *Client +} + +type points struct { + once comfig.Once + getter kv.Getter +} + +func NewPointer(getter kv.Getter) Pointer { + return &points{getter: getter} +} + +func (p *points) Points() *Client { + return p.once.Do(func() any { + var cfg struct { + Addr *url.URL `fig:"addr,required"` + RequestTimeout time.Duration `fig:"request_timeout"` + } + + err := figure.Out(&cfg). + From(kv.MustGetStringMap(p.getter, "points")). + Please() + if err != nil { + panic(fmt.Errorf("failed to figure out points: %s", err)) + } + + if cfg.RequestTimeout == 0 { + cfg.RequestTimeout = defaultTimeout + } + + return NewClient(&client{ + base: cfg.Addr, + http: &http.Client{Timeout: cfg.RequestTimeout}, + }) + }).(*Client) +} diff --git a/pkg/connector/main.go b/pkg/connector/main.go new file mode 100644 index 0000000..55c3ecd --- /dev/null +++ b/pkg/connector/main.go @@ -0,0 +1,30 @@ +package connector + +import ( + "context" + "net/url" + + conn "gitlab.com/distributed_lab/json-api-connector" + iface "gitlab.com/distributed_lab/json-api-connector/client" +) + +const FulfillEventEndpoint = "/integrations/rarime-points-svc/v1/private/events" +const VerifyPassportEndpoint = "/integrations/rarime-points-svc/v1/private/balances" + +type Client struct { + conn *conn.Connector +} + +func NewClient(cli iface.Client) *Client { + return &Client{conn: conn.NewConnector(cli)} +} + +func (c *Client) FulfillEvent(ctx context.Context, req FulfillEventRequest) error { + u, _ := url.Parse(FulfillEventEndpoint) + return c.conn.PostJSON(u, req, ctx, nil) +} + +func (c *Client) VerifyPassport(ctx context.Context, req VerifyPassportRequest) error { + u, _ := url.Parse(VerifyPassportEndpoint) + return c.conn.PostJSON(u, req, ctx, nil) +} diff --git a/pkg/connector/models.go b/pkg/connector/models.go new file mode 100644 index 0000000..3cea950 --- /dev/null +++ b/pkg/connector/models.go @@ -0,0 +1,15 @@ +package connector + +import "time" + +type FulfillEventRequest struct { + UserDID string `json:"user_did"` + EventType string `json:"event_type"` + ExternalID *string `json:"external_id,omitempty"` +} + +type VerifyPassportRequest struct { + UserDID string `json:"user_did"` + Hash string `json:"hash"` + Expiry time.Time `json:"expiry"` +} From f237e38ef3daeb43b838dc1c09ad0720d9fd9654 Mon Sep 17 00:00:00 2001 From: violog <51th.apprent1ce.f0rce@gmail.com> Date: Mon, 19 Feb 2024 19:08:00 +0200 Subject: [PATCH 2/6] Endpoint to verify passport --- internal/service/handlers/verify_passport.go | 91 ++++++++++++++++++++ internal/service/requests/verify_passport.go | 36 ++++++++ internal/service/router.go | 4 + pkg/connector/main.go | 4 +- 4 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 internal/service/handlers/verify_passport.go create mode 100644 internal/service/requests/verify_passport.go diff --git a/internal/service/handlers/verify_passport.go b/internal/service/handlers/verify_passport.go new file mode 100644 index 0000000..e6a3c84 --- /dev/null +++ b/internal/service/handlers/verify_passport.go @@ -0,0 +1,91 @@ +package handlers + +import ( + "fmt" + "net/http" + + "github.com/rarimo/rarime-points-svc/internal/data" + "github.com/rarimo/rarime-points-svc/internal/data/evtypes" + "github.com/rarimo/rarime-points-svc/internal/service/requests" + "gitlab.com/distributed_lab/ape" + "gitlab.com/distributed_lab/ape/problems" +) + +func VerifyPassport(w http.ResponseWriter, r *http.Request) { + req, err := requests.NewVerifyPassport(r) + if err != nil { + ape.RenderErr(w, problems.BadRequest(err)...) + return + } + + balance, err := BalancesQ(r).FilterByDID(req.UserDID).Get() + if err != nil { + Log(r).WithError(err).Error("Failed to get balance by DID") + ape.RenderErr(w, problems.InternalError()) + return + } + if balance == nil { + ape.RenderErr(w, problems.NotFound()) + return + } + + err = EventsQ(r).Transaction(func() error { + // If you make this endpoint public, you should check the passport hash for + // uniqueness and provide a better validation. Think about other changes too. + err = BalancesQ(r).FilterByDID(req.UserDID).SetPassport(req.Hash, req.Expiry) + if err != nil { + return fmt.Errorf("set passport for balance by DID: %w", err) + } + + evType := EventTypes(r).Get(evtypes.TypeReferralSpecific, evtypes.FilterInactive) + if evType == nil { + Log(r).Debug("Referral event type is disabled or expired, not accruing points to referrer") + return nil + } + + refDID, err := getReferrerDID(*balance, r) + if err != nil { + return fmt.Errorf("get referrer DID by referred_by: %w", err) + } + if refDID == "" { + return nil + } + + err = EventsQ(r).Insert(data.Event{ + UserDID: refDID, + Type: evType.Name, + Status: data.EventFulfilled, + }) + if err != nil { + return fmt.Errorf("add event for referrer: %w", err) + } + + return nil + }) + + if err != nil { + Log(r).WithError(err).Error("Failed to set passport and add event for referrer") + ape.RenderErr(w, problems.InternalError()) + return + } + + w.WriteHeader(http.StatusNoContent) +} + +func getReferrerDID(balance data.Balance, r *http.Request) (string, error) { + if !balance.ReferredBy.Valid { + return "", nil + } + + refBy := balance.ReferredBy.String + referrer, err := BalancesQ(r).FilterByReferralID(refBy).Get() + if err != nil { + return "", fmt.Errorf("failed to get balance by referral ID: %w", err) + } + if referrer == nil { + return "", fmt.Errorf("referrer not found: %s", refBy) + } + + Log(r).Debugf("Found referrer: DID=%s", referrer.DID) + return referrer.DID, nil +} diff --git a/internal/service/requests/verify_passport.go b/internal/service/requests/verify_passport.go new file mode 100644 index 0000000..e1d4ec9 --- /dev/null +++ b/internal/service/requests/verify_passport.go @@ -0,0 +1,36 @@ +package requests + +import ( + "encoding/json" + "errors" + "net/http" + "time" + + validation "github.com/go-ozzo/ozzo-validation/v4" + "github.com/rarimo/rarime-points-svc/pkg/connector" +) + +func NewVerifyPassport(r *http.Request) (req connector.VerifyPassportRequest, err error) { + if err = json.NewDecoder(r.Body).Decode(&req); err != nil { + return + } + + return req, validation.Errors{ + "user_did": validation.Validate(req.UserDID, validation.Required), + "hash": validation.Validate(req.Hash, validation.Required), + "expiry": validation.Validate(req.Expiry, validation.Required, validation.By(isNotExpiredRule)), + }.Filter() +} + +func isNotExpiredRule(value interface{}) error { + v, ok := value.(time.Time) + if !ok { + panic("value is not a time.Time") // invalid function usage + } + + if v.Before(time.Now().UTC()) { + return errors.New("expiry is in the past") + } + + return nil +} diff --git a/internal/service/router.go b/internal/service/router.go index 1f02539..0da4a0a 100644 --- a/internal/service/router.go +++ b/internal/service/router.go @@ -40,6 +40,10 @@ func (s *service) router() chi.Router { }) r.Get("/balances", handlers.Leaderboard) r.Get("/point_price", handlers.GetPointPrice) + // must be accessible only within the cluster + r.Route("/private", func(r chi.Router) { + r.Patch("/balances", handlers.VerifyPassport) + }) }) return r diff --git a/pkg/connector/main.go b/pkg/connector/main.go index 55c3ecd..8f5846f 100644 --- a/pkg/connector/main.go +++ b/pkg/connector/main.go @@ -21,10 +21,10 @@ func NewClient(cli iface.Client) *Client { func (c *Client) FulfillEvent(ctx context.Context, req FulfillEventRequest) error { u, _ := url.Parse(FulfillEventEndpoint) - return c.conn.PostJSON(u, req, ctx, nil) + return c.conn.PatchJSON(u, req, ctx, nil) } func (c *Client) VerifyPassport(ctx context.Context, req VerifyPassportRequest) error { u, _ := url.Parse(VerifyPassportEndpoint) - return c.conn.PostJSON(u, req, ctx, nil) + return c.conn.PatchJSON(u, req, ctx, nil) } From 4f762852b239378d2b526a1ff91420b2eec990f7 Mon Sep 17 00:00:00 2001 From: violog <51th.apprent1ce.f0rce@gmail.com> Date: Tue, 20 Feb 2024 11:26:57 +0200 Subject: [PATCH 3/6] Remove cop --- internal/config/main.go | 5 ----- internal/service/main.go | 43 -------------------------------------- internal/service/router.go | 30 ++++++++++++++------------ pkg/connector/main.go | 7 +++---- 4 files changed, 20 insertions(+), 65 deletions(-) delete mode 100644 internal/service/main.go diff --git a/internal/config/main.go b/internal/config/main.go index eafd5b5..41d793e 100644 --- a/internal/config/main.go +++ b/internal/config/main.go @@ -6,8 +6,6 @@ import ( "github.com/rarimo/rarime-points-svc/internal/service/workers/sbtcheck" "github.com/rarimo/saver-grpc-lib/broadcaster" "gitlab.com/distributed_lab/kit/comfig" - "gitlab.com/distributed_lab/kit/copus" - "gitlab.com/distributed_lab/kit/copus/types" "gitlab.com/distributed_lab/kit/kv" "gitlab.com/distributed_lab/kit/pgdb" ) @@ -15,7 +13,6 @@ import ( type Config interface { comfig.Logger pgdb.Databaser - types.Copuser comfig.Listenerer auth.Auther broadcaster.Broadcasterer @@ -28,7 +25,6 @@ type Config interface { type config struct { comfig.Logger pgdb.Databaser - types.Copuser comfig.Listenerer auth.Auther broadcaster.Broadcasterer @@ -43,7 +39,6 @@ func New(getter kv.Getter) Config { return &config{ getter: getter, Databaser: pgdb.NewDatabaser(getter), - Copuser: copus.NewCopuser(getter), Listenerer: comfig.NewListenerer(getter), Logger: comfig.NewLogger(getter, comfig.LoggerOpts{}), Auther: auth.NewAuther(getter), //nolint:misspell diff --git a/internal/service/main.go b/internal/service/main.go deleted file mode 100644 index d3fc6fa..0000000 --- a/internal/service/main.go +++ /dev/null @@ -1,43 +0,0 @@ -package service - -import ( - "context" - "fmt" - - "github.com/rarimo/rarime-points-svc/internal/config" - "gitlab.com/distributed_lab/ape" - "gitlab.com/distributed_lab/kit/copus/types" - "gitlab.com/distributed_lab/logan/v3" -) - -type service struct { - log *logan.Entry - copus types.Copus - cfg config.Config -} - -func (s *service) run(ctx context.Context) error { - s.log.Info("Service started") - r := s.router() - - if err := s.copus.RegisterChi(r); err != nil { - return fmt.Errorf("cop failed: %w", err) - } - - ape.Serve(ctx, r, s.cfg, ape.ServeOpts{}) - return nil -} - -func newService(cfg config.Config) *service { - return &service{ - log: cfg.Log(), - copus: cfg.Copus(), - cfg: cfg, - } -} - -func Run(ctx context.Context, cfg config.Config) { - if err := newService(cfg).run(ctx); err != nil { - panic(err) - } -} diff --git a/internal/service/router.go b/internal/service/router.go index 0da4a0a..ced93c4 100644 --- a/internal/service/router.go +++ b/internal/service/router.go @@ -1,31 +1,34 @@ package service import ( + "context" + "github.com/go-chi/chi" + "github.com/rarimo/rarime-points-svc/internal/config" "github.com/rarimo/rarime-points-svc/internal/data/pg" "github.com/rarimo/rarime-points-svc/internal/service/handlers" "gitlab.com/distributed_lab/ape" ) -func (s *service) router() chi.Router { +func Run(ctx context.Context, cfg config.Config) { r := chi.NewRouter() r.Use( - ape.RecoverMiddleware(s.log), - ape.LoganMiddleware(s.log), + ape.RecoverMiddleware(cfg.Log()), + ape.LoganMiddleware(cfg.Log()), ape.CtxMiddleware( - handlers.CtxLog(s.log), - handlers.CtxEventsQ(pg.NewEvents(s.cfg.DB())), - handlers.CtxBalancesQ(pg.NewBalances(s.cfg.DB())), - handlers.CtxWithdrawalsQ(pg.NewWithdrawals(s.cfg.DB())), - handlers.CtxEventTypes(s.cfg.EventTypes()), - handlers.CtxBroadcaster(s.cfg.Broadcaster()), - handlers.CtxPointPrice(s.cfg.PointPrice()), + handlers.CtxLog(cfg.Log()), + handlers.CtxEventsQ(pg.NewEvents(cfg.DB())), + handlers.CtxBalancesQ(pg.NewBalances(cfg.DB())), + handlers.CtxWithdrawalsQ(pg.NewWithdrawals(cfg.DB())), + handlers.CtxEventTypes(cfg.EventTypes()), + handlers.CtxBroadcaster(cfg.Broadcaster()), + handlers.CtxPointPrice(cfg.PointPrice()), ), ) r.Route("/integrations/rarime-points-svc/v1", func(r chi.Router) { r.Route("/balances", func(r chi.Router) { - r.Use(handlers.AuthMiddleware(s.cfg.Auth(), s.log)) + r.Use(handlers.AuthMiddleware(cfg.Auth(), cfg.Log())) r.Post("/", handlers.CreateBalance) r.Route("/{did}", func(r chi.Router) { r.Get("/", handlers.GetBalance) @@ -34,7 +37,7 @@ func (s *service) router() chi.Router { }) }) r.Route("/events", func(r chi.Router) { - r.Use(handlers.AuthMiddleware(s.cfg.Auth(), s.log)) + r.Use(handlers.AuthMiddleware(cfg.Auth(), cfg.Log())) r.Get("/", handlers.ListEvents) r.Patch("/{id}", handlers.ClaimEvent) }) @@ -46,5 +49,6 @@ func (s *service) router() chi.Router { }) }) - return r + cfg.Log().Info("Service started") + ape.Serve(ctx, r, cfg, ape.ServeOpts{}) } diff --git a/pkg/connector/main.go b/pkg/connector/main.go index 8f5846f..52056eb 100644 --- a/pkg/connector/main.go +++ b/pkg/connector/main.go @@ -8,8 +8,7 @@ import ( iface "gitlab.com/distributed_lab/json-api-connector/client" ) -const FulfillEventEndpoint = "/integrations/rarime-points-svc/v1/private/events" -const VerifyPassportEndpoint = "/integrations/rarime-points-svc/v1/private/balances" +const privatePrefix = "/integrations/rarime-points-svc/v1/private" type Client struct { conn *conn.Connector @@ -20,11 +19,11 @@ func NewClient(cli iface.Client) *Client { } func (c *Client) FulfillEvent(ctx context.Context, req FulfillEventRequest) error { - u, _ := url.Parse(FulfillEventEndpoint) + u, _ := url.Parse(privatePrefix + "/events") return c.conn.PatchJSON(u, req, ctx, nil) } func (c *Client) VerifyPassport(ctx context.Context, req VerifyPassportRequest) error { - u, _ := url.Parse(VerifyPassportEndpoint) + u, _ := url.Parse(privatePrefix + "/balances") return c.conn.PatchJSON(u, req, ctx, nil) } From 138d7e3b9771a2b00973557677aca3771bdc907d Mon Sep 17 00:00:00 2001 From: violog <51th.apprent1ce.f0rce@gmail.com> Date: Tue, 20 Feb 2024 11:44:33 +0200 Subject: [PATCH 4/6] Increase golangci-lint timeout to 2m --- .github/workflows/code-review.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/code-review.yaml b/.github/workflows/code-review.yaml index abceffb..b3afaf4 100644 --- a/.github/workflows/code-review.yaml +++ b/.github/workflows/code-review.yaml @@ -20,3 +20,4 @@ jobs: with: reporter: github-pr-review cache: false + golangci_lint_flags: --timeout=2m From d987f1fa64a7ca0c4ce5ce4d790bcbaf46a6b800 Mon Sep 17 00:00:00 2001 From: violog <51th.apprent1ce.f0rce@gmail.com> Date: Thu, 22 Feb 2024 11:48:47 +0200 Subject: [PATCH 5/6] Do db.Clone() when necessary, remove when it causes bugs --- internal/data/pg/balances.go | 2 +- internal/data/pg/events.go | 2 +- internal/data/pg/withdrawals.go | 2 +- internal/service/router.go | 7 ++++--- internal/service/workers/reopener/init.go | 2 +- internal/service/workers/sbtcheck/main.go | 2 +- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/internal/data/pg/balances.go b/internal/data/pg/balances.go index d183152..6388712 100644 --- a/internal/data/pg/balances.go +++ b/internal/data/pg/balances.go @@ -28,7 +28,7 @@ func NewBalances(db *pgdb.DB) data.BalancesQ { } func (q *balances) New() data.BalancesQ { - return NewBalances(q.db.Clone()) + return NewBalances(q.db) } func (q *balances) Insert(bal data.Balance) error { diff --git a/internal/data/pg/events.go b/internal/data/pg/events.go index 0c2d58c..6fe4d7d 100644 --- a/internal/data/pg/events.go +++ b/internal/data/pg/events.go @@ -35,7 +35,7 @@ func NewEvents(db *pgdb.DB) data.EventsQ { } func (q *events) New() data.EventsQ { - return NewEvents(q.db.Clone()) + return NewEvents(q.db) } func (q *events) Insert(events ...data.Event) error { diff --git a/internal/data/pg/withdrawals.go b/internal/data/pg/withdrawals.go index 461ca49..638823d 100644 --- a/internal/data/pg/withdrawals.go +++ b/internal/data/pg/withdrawals.go @@ -23,7 +23,7 @@ func NewWithdrawals(db *pgdb.DB) data.WithdrawalsQ { } func (q *withdrawals) New() data.WithdrawalsQ { - return NewWithdrawals(q.db.Clone()) + return NewWithdrawals(q.db) } func (q *withdrawals) Insert(w data.Withdrawal) (*data.Withdrawal, error) { diff --git a/internal/service/router.go b/internal/service/router.go index ced93c4..8c3a8bc 100644 --- a/internal/service/router.go +++ b/internal/service/router.go @@ -12,15 +12,16 @@ import ( func Run(ctx context.Context, cfg config.Config) { r := chi.NewRouter() + db := cfg.DB().Clone() r.Use( ape.RecoverMiddleware(cfg.Log()), ape.LoganMiddleware(cfg.Log()), ape.CtxMiddleware( handlers.CtxLog(cfg.Log()), - handlers.CtxEventsQ(pg.NewEvents(cfg.DB())), - handlers.CtxBalancesQ(pg.NewBalances(cfg.DB())), - handlers.CtxWithdrawalsQ(pg.NewWithdrawals(cfg.DB())), + handlers.CtxEventsQ(pg.NewEvents(db)), + handlers.CtxBalancesQ(pg.NewBalances(db)), + handlers.CtxWithdrawalsQ(pg.NewWithdrawals(db)), handlers.CtxEventTypes(cfg.EventTypes()), handlers.CtxBroadcaster(cfg.Broadcaster()), handlers.CtxPointPrice(cfg.PointPrice()), diff --git a/internal/service/workers/reopener/init.go b/internal/service/workers/reopener/init.go index 763d2c7..7085f5c 100644 --- a/internal/service/workers/reopener/init.go +++ b/internal/service/workers/reopener/init.go @@ -13,7 +13,7 @@ import ( func initialRun(cfg config.Config) error { var ( - q = pg.NewEvents(cfg.DB()) + q = pg.NewEvents(cfg.DB().Clone()) log = cfg.Log().WithField("who", "reopener[initializer]") col = &initCollector{ q: q, diff --git a/internal/service/workers/sbtcheck/main.go b/internal/service/workers/sbtcheck/main.go index 5d39cb9..d3b189a 100644 --- a/internal/service/workers/sbtcheck/main.go +++ b/internal/service/workers/sbtcheck/main.go @@ -76,7 +76,7 @@ func Run(ctx context.Context, cfg extConfig) { r := &runner{ network: net, - db: cfg.DB(), + db: cfg.DB().Clone(), types: cfg.EventTypes(), log: log.WithField("network", name), cancel: cancel, From 4c85d77d0eecb14cbf5e571161ec2566df97146e Mon Sep 17 00:00:00 2001 From: violog <51th.apprent1ce.f0rce@gmail.com> Date: Thu, 22 Feb 2024 15:35:06 +0200 Subject: [PATCH 6/6] Fix DB session cloning on each new request --- internal/service/handlers/middleware.go | 28 +++++++++++++++++++++++++ internal/service/router.go | 6 +----- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/internal/service/handlers/middleware.go b/internal/service/handlers/middleware.go index f87b5aa..b13c721 100644 --- a/internal/service/handlers/middleware.go +++ b/internal/service/handlers/middleware.go @@ -1,11 +1,14 @@ package handlers import ( + "context" "net/http" "github.com/rarimo/auth-svc/pkg/auth" + "github.com/rarimo/rarime-points-svc/internal/data/pg" "gitlab.com/distributed_lab/ape" "gitlab.com/distributed_lab/ape/problems" + "gitlab.com/distributed_lab/kit/pgdb" "gitlab.com/distributed_lab/logan/v3" ) @@ -29,3 +32,28 @@ func AuthMiddleware(auth *auth.Client, log *logan.Entry) func(http.Handler) http }) } } + +type ctxExtender func(context.Context) context.Context + +// DBCloneMiddleware is designed to clone DB session on each request. You must +// put all new DB handlers here instead of ape.CtxMiddleware, unless you have a +// reason to do otherwise. +func DBCloneMiddleware(db *pgdb.DB) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + clone := db.Clone() + ctx := r.Context() + + extenders := []ctxExtender{ + CtxEventsQ(pg.NewEvents(clone)), + CtxBalancesQ(pg.NewBalances(clone)), + CtxWithdrawalsQ(pg.NewWithdrawals(clone)), + } + + for _, extender := range extenders { + ctx = extender(ctx) + } + next.ServeHTTP(w, r.WithContext(ctx)) + }) + } +} diff --git a/internal/service/router.go b/internal/service/router.go index 8c3a8bc..2e47bfc 100644 --- a/internal/service/router.go +++ b/internal/service/router.go @@ -5,27 +5,23 @@ import ( "github.com/go-chi/chi" "github.com/rarimo/rarime-points-svc/internal/config" - "github.com/rarimo/rarime-points-svc/internal/data/pg" "github.com/rarimo/rarime-points-svc/internal/service/handlers" "gitlab.com/distributed_lab/ape" ) func Run(ctx context.Context, cfg config.Config) { r := chi.NewRouter() - db := cfg.DB().Clone() r.Use( ape.RecoverMiddleware(cfg.Log()), ape.LoganMiddleware(cfg.Log()), ape.CtxMiddleware( handlers.CtxLog(cfg.Log()), - handlers.CtxEventsQ(pg.NewEvents(db)), - handlers.CtxBalancesQ(pg.NewBalances(db)), - handlers.CtxWithdrawalsQ(pg.NewWithdrawals(db)), handlers.CtxEventTypes(cfg.EventTypes()), handlers.CtxBroadcaster(cfg.Broadcaster()), handlers.CtxPointPrice(cfg.PointPrice()), ), + handlers.DBCloneMiddleware(cfg.DB()), ) r.Route("/integrations/rarime-points-svc/v1", func(r chi.Router) { r.Route("/balances", func(r chi.Router) {