diff --git a/docs/package-lock.json b/docs/package-lock.json index b5e50e7..673d152 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -734,6 +734,13 @@ "ajv": "4.11.8 - 6" } }, + "node_modules/better-ajv-errors/node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true + }, "node_modules/binary-extensions": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.0.tgz", @@ -1316,10 +1323,14 @@ } }, "node_modules/core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js." + "version": "3.36.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.36.0.tgz", + "integrity": "sha512-mt7+TUBbTFg5+GngsAxeKBTl5/VS0guFeJacYge9OmHb+m058UwwIm41SE9T4Den7ClatV57B6TYTuJ0CX1MAw==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } }, "node_modules/core-util-is": { "version": "1.0.2", @@ -6242,17 +6253,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/swagger2openapi/node_modules/core-js": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.9.0.tgz", - "integrity": "sha512-PyFBJaLq93FlyYdsndE5VaueA9K5cNB7CGzeCj191YYLhkQM0gdZR2SKihM70oF0wdqKSKClv/tEBOpoRmdOVQ==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/swagger2openapi/node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -6955,6 +6955,13 @@ "optionalDependencies": { "commander": "^2.7.1" } + }, + "node_modules/z-schema/node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true } }, "dependencies": { @@ -7571,6 +7578,13 @@ "json-to-ast": "^2.0.3", "jsonpointer": "^4.0.1", "leven": "^2.1.0" + }, + "dependencies": { + "core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" + } } }, "binary-extensions": { @@ -8048,9 +8062,9 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" }, "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "3.36.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.36.0.tgz", + "integrity": "sha512-mt7+TUBbTFg5+GngsAxeKBTl5/VS0guFeJacYge9OmHb+m058UwwIm41SE9T4Den7ClatV57B6TYTuJ0CX1MAw==" }, "core-util-is": { "version": "1.0.2", @@ -11959,11 +11973,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "core-js": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.9.0.tgz", - "integrity": "sha512-PyFBJaLq93FlyYdsndE5VaueA9K5cNB7CGzeCj191YYLhkQM0gdZR2SKihM70oF0wdqKSKClv/tEBOpoRmdOVQ==" - }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -12534,6 +12543,13 @@ "lodash.get": "^4.0.0", "lodash.isequal": "^4.0.0", "validator": "^10.0.0" + }, + "dependencies": { + "core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" + } } } } diff --git a/docs/spec/components/schemas/Balance.yaml b/docs/spec/components/schemas/Balance.yaml index bc62bb9..4cd5ff8 100644 --- a/docs/spec/components/schemas/Balance.yaml +++ b/docs/spec/components/schemas/Balance.yaml @@ -14,6 +14,7 @@ allOf: properties: amount: type: integer + format: int64 description: Amount of points example: 580 is_verified: diff --git a/docs/spec/components/schemas/Event.yaml b/docs/spec/components/schemas/Event.yaml index 9dffbf1..8128a08 100644 --- a/docs/spec/components/schemas/Event.yaml +++ b/docs/spec/components/schemas/Event.yaml @@ -28,6 +28,7 @@ allOf: $ref: '#/components/schemas/EventMeta' points_amount: type: integer + format: int64 description: | How many points were accrued. Required only for `claimed` events. This is necessary, as the reward might change over time, while diff --git a/docs/spec/components/schemas/EventStaticMeta.yaml b/docs/spec/components/schemas/EventStaticMeta.yaml index d5249c5..ba49404 100644 --- a/docs/spec/components/schemas/EventStaticMeta.yaml +++ b/docs/spec/components/schemas/EventStaticMeta.yaml @@ -16,6 +16,7 @@ properties: example: get_poh reward: type: integer + format: int64 description: Reward amount in points example: 50 title: diff --git a/docs/spec/components/schemas/PointPrice.yaml b/docs/spec/components/schemas/PointPrice.yaml index e6a5a33..bf93753 100644 --- a/docs/spec/components/schemas/PointPrice.yaml +++ b/docs/spec/components/schemas/PointPrice.yaml @@ -14,5 +14,6 @@ allOf: properties: urmo: type: integer + format: int64 description: Amount of `urmo` tokens for one point example: 1000 diff --git a/docs/spec/components/schemas/Withdraw.yaml b/docs/spec/components/schemas/Withdraw.yaml index 2322b25..e720b7a 100644 --- a/docs/spec/components/schemas/Withdraw.yaml +++ b/docs/spec/components/schemas/Withdraw.yaml @@ -13,6 +13,7 @@ allOf: properties: amount: type: integer + format: int64 description: Amount of points to withdraw example: 580 address: diff --git a/docs/spec/components/schemas/Withdrawal.yaml b/docs/spec/components/schemas/Withdrawal.yaml index ef4951f..4d6145c 100644 --- a/docs/spec/components/schemas/Withdrawal.yaml +++ b/docs/spec/components/schemas/Withdrawal.yaml @@ -13,6 +13,7 @@ allOf: properties: amount: type: integer + format: int64 description: Amount of points withdrawn example: 580 address: diff --git a/docs/spec/openapi.yaml b/docs/spec/openapi.yaml index ab3353f..c37d081 100644 --- a/docs/spec/openapi.yaml +++ b/docs/spec/openapi.yaml @@ -4,5 +4,5 @@ info: title: rarime-points-svc description: '' servers: - - url: 'https://api.demo.tokend.io' + - url: 'https://api.stage.rarime.com' description: TokenD Developer Environment diff --git a/go.mod b/go.mod index 265b4fa..846fc0c 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/rarimo/rarime-points-svc -go 1.21.3 +go 1.22 require ( github.com/Masterminds/squirrel v1.4.0 @@ -11,7 +11,7 @@ require ( github.com/go-co-op/gocron/v2 v2.2.2 github.com/go-ozzo/ozzo-validation/v4 v4.3.0 github.com/iden3/go-iden3-core/v2 v2.0.4 - github.com/rarimo/rarime-auth-svc v0.0.1-rc9 + github.com/rarimo/auth-svc v1.0.0-rc2 github.com/rarimo/saver-grpc-lib v1.0.0 github.com/rubenv/sql-migrate v1.6.1 gitlab.com/distributed_lab/ape v1.7.1 diff --git a/go.sum b/go.sum index 4089299..eb9a50a 100644 --- a/go.sum +++ b/go.sum @@ -1216,12 +1216,12 @@ github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJf github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= +github.com/rarimo/auth-svc v1.0.0-rc2 h1:6w+T8xFZwi5gc2x8FlObnfMEij2zlNQZDNSdaBLY2iw= +github.com/rarimo/auth-svc v1.0.0-rc2/go.mod h1:XtPIuJABJ3QNmhcpmJRlQrxuagPfpIWwFbY0j5SakFg= github.com/rarimo/broadcaster-svc v1.0.2 h1:ExQcjjWCRP5+POLDlZHrTD1ffUsBH+Dgv5FAgcP3BXc= github.com/rarimo/broadcaster-svc v1.0.2/go.mod h1:lYIHy+X4IqQt4eBdtMN/V352H3EV0/gO8G+32SFwUWI= github.com/rarimo/cosmos-sdk v0.46.7 h1:jU2PiWzc+19SF02cXM0O0puKPeH1C6Q6t2lzJ9s1ejc= github.com/rarimo/cosmos-sdk v0.46.7/go.mod h1:fqKqz39U5IlEFb4nbQ72951myztsDzFKKDtffYJ63nk= -github.com/rarimo/rarime-auth-svc v0.0.1-rc9 h1:JZfq1q3WaaLwa8ICk8RHEx4+ojl/JBE8N5zJ/uHZUwM= -github.com/rarimo/rarime-auth-svc v0.0.1-rc9/go.mod h1:48c8FsJixnLeWx8F8MDy79FvhU1r0oTU11mwP3JPngQ= github.com/rarimo/saver-grpc-lib v1.0.0 h1:MGUVjYg7unmodYczVsLqlqZNkT4CIgKqdo6aQtL1qdE= github.com/rarimo/saver-grpc-lib v1.0.0/go.mod h1:DpugWK5B7Hi0bdC3MPe/9FD2zCxaRwsyykdwxtF1Zgg= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= diff --git a/internal/assets/migrations/001_initial.sql b/internal/assets/migrations/001_initial.sql index bddf0bd..84f3107 100644 --- a/internal/assets/migrations/001_initial.sql +++ b/internal/assets/migrations/001_initial.sql @@ -6,7 +6,7 @@ AS $$ BEGIN NEW.updated_at = EXTRACT('EPOCH' FROM NOW()); RETURN NEW; END; $$; CREATE TABLE IF NOT EXISTS balances ( did text PRIMARY KEY, - amount integer not null default 0, + amount bigint not null default 0, created_at integer not null default EXTRACT('EPOCH' FROM NOW()), updated_at integer not null default EXTRACT('EPOCH' FROM NOW()), passport_hash text UNIQUE, diff --git a/internal/config/main.go b/internal/config/main.go index f2384af..eafd5b5 100644 --- a/internal/config/main.go +++ b/internal/config/main.go @@ -1,7 +1,7 @@ package config import ( - "github.com/rarimo/rarime-auth-svc/pkg/auth" + "github.com/rarimo/auth-svc/pkg/auth" "github.com/rarimo/rarime-points-svc/internal/data/evtypes" "github.com/rarimo/rarime-points-svc/internal/service/workers/sbtcheck" "github.com/rarimo/saver-grpc-lib/broadcaster" @@ -22,7 +22,7 @@ type Config interface { evtypes.EventTypeser sbtcheck.SbtChecker - PointPrice() int32 + PointPrice() int64 } type config struct { diff --git a/internal/config/point_price.go b/internal/config/point_price.go index 27c211b..8c35f22 100644 --- a/internal/config/point_price.go +++ b/internal/config/point_price.go @@ -7,10 +7,10 @@ import ( "gitlab.com/distributed_lab/kit/kv" ) -func (c *config) PointPrice() int32 { +func (c *config) PointPrice() int64 { return c.pointPrice.Do(func() interface{} { var cfg struct { - PointPriceURMO int32 `fig:"point_price_urmo,required"` + PointPriceURMO int64 `fig:"point_price_urmo,required"` } err := figure.Out(&cfg). @@ -21,5 +21,5 @@ func (c *config) PointPrice() int32 { } return cfg.PointPriceURMO - }).(int32) + }).(int64) } diff --git a/internal/data/balances.go b/internal/data/balances.go index d1f0f90..e337610 100644 --- a/internal/data/balances.go +++ b/internal/data/balances.go @@ -9,7 +9,7 @@ import ( type Balance struct { DID string `db:"did"` - Amount int32 `db:"amount"` + Amount int64 `db:"amount"` CreatedAt int32 `db:"created_at"` UpdatedAt int32 `db:"updated_at"` PassportHash sql.NullString `db:"passport_hash"` @@ -20,7 +20,7 @@ type Balance struct { type BalancesQ interface { New() BalancesQ Insert(did string) error - UpdateAmountBy(points int32) error + UpdateAmountBy(points int64) error SetPassport(hash string, exp time.Time) error Page(*pgdb.OffsetPageParams) BalancesQ diff --git a/internal/data/events.go b/internal/data/events.go index d499251..091c903 100644 --- a/internal/data/events.go +++ b/internal/data/events.go @@ -1,7 +1,6 @@ package data import ( - "database/sql" "encoding/json" "gitlab.com/distributed_lab/kit/pgdb" @@ -20,14 +19,14 @@ func (s EventStatus) String() string { } type Event struct { - ID string `db:"id"` - UserDID string `db:"user_did"` - Type string `db:"type"` - Status EventStatus `db:"status"` - CreatedAt int32 `db:"created_at"` - UpdatedAt int32 `db:"updated_at"` - Meta Jsonb `db:"meta"` - PointsAmount sql.NullInt32 `db:"points_amount"` + ID string `db:"id"` + UserDID string `db:"user_did"` + Type string `db:"type"` + Status EventStatus `db:"status"` + CreatedAt int32 `db:"created_at"` + UpdatedAt int32 `db:"updated_at"` + Meta Jsonb `db:"meta"` + PointsAmount *int64 `db:"points_amount"` } // ReopenableEvent is a pair that is sufficient to build a new open event with a specific type for a user @@ -39,7 +38,7 @@ type ReopenableEvent struct { type EventsQ interface { New() EventsQ Insert(...Event) error - Update(status EventStatus, meta json.RawMessage, points *int32) (*Event, error) + Update(status EventStatus, meta json.RawMessage, points *int64) (*Event, error) Transaction(func() error) error Page(*pgdb.CursorPageParams) EventsQ diff --git a/internal/data/evtypes/config.go b/internal/data/evtypes/config.go index 468053b..6da8f82 100644 --- a/internal/data/evtypes/config.go +++ b/internal/data/evtypes/config.go @@ -29,7 +29,7 @@ func (c *config) EventTypes() Types { Types []struct { Name string `fig:"name,required"` Description string `fig:"description,required"` - Reward int32 `fig:"reward,required"` + Reward int64 `fig:"reward,required"` Title string `fig:"title,required"` Frequency Frequency `fig:"frequency,required"` ExpiresAt *time.Time `fig:"expires_at"` diff --git a/internal/data/pg/balances.go b/internal/data/pg/balances.go index 759a10c..9390756 100644 --- a/internal/data/pg/balances.go +++ b/internal/data/pg/balances.go @@ -41,7 +41,7 @@ func (q *balances) Insert(did string) error { return nil } -func (q *balances) UpdateAmountBy(points int32) error { +func (q *balances) UpdateAmountBy(points int64) error { stmt := q.updater.Set("amount", squirrel.Expr("amount + ?", points)) if err := q.db.Exec(stmt); err != nil { diff --git a/internal/data/pg/events.go b/internal/data/pg/events.go index bfd6688..15dc3e4 100644 --- a/internal/data/pg/events.go +++ b/internal/data/pg/events.go @@ -58,7 +58,7 @@ func (q *events) Insert(events ...data.Event) error { return nil } -func (q *events) Update(status data.EventStatus, meta json.RawMessage, points *int32) (*data.Event, error) { +func (q *events) Update(status data.EventStatus, meta json.RawMessage, points *int64) (*data.Event, error) { umap := map[string]any{ "status": status, } @@ -66,7 +66,7 @@ func (q *events) Update(status data.EventStatus, meta json.RawMessage, points *i umap["meta"] = meta } if points != nil { - umap["points_amount"] = sql.NullInt32{Int32: *points, Valid: true} + umap["points_amount"] = points } var res data.Event diff --git a/internal/data/withdrawals.go b/internal/data/withdrawals.go index 722237e..d07a85a 100644 --- a/internal/data/withdrawals.go +++ b/internal/data/withdrawals.go @@ -7,7 +7,7 @@ import ( type Withdrawal struct { ID string `db:"id"` UserDID string `db:"user_did"` - Amount int32 `db:"amount"` + Amount int64 `db:"amount"` Address string `db:"address"` CreatedAt int32 `db:"created_at"` } diff --git a/internal/service/handlers/claim_event.go b/internal/service/handlers/claim_event.go index d1487dc..95d3448 100644 --- a/internal/service/handlers/claim_event.go +++ b/internal/service/handlers/claim_event.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" + "github.com/rarimo/auth-svc/pkg/auth" "github.com/rarimo/rarime-points-svc/internal/data" "github.com/rarimo/rarime-points-svc/internal/service/requests" "github.com/rarimo/rarime-points-svc/resources" @@ -18,54 +19,56 @@ func ClaimEvent(w http.ResponseWriter, r *http.Request) { return } - event := getEventToClaim(req.Data.ID, w, r) - if event == nil { - return - } + event, err := EventsQ(r). + FilterByID(req.Data.ID). + FilterByStatus(data.EventFulfilled). + Get() - evType := EventTypes(r).Get(event.Type) - if evType == nil { - Log(r).Error("Wrong event type is stored in DB: might be bad event config") + if err != nil { + Log(r).WithError(err).Error("Failed to get event by balance ID") ape.RenderErr(w, problems.InternalError()) return } - event = claimEventWithPoints(*event, evType.Reward, w, r) if event == nil { + Log(r).Debugf("Event not found for id=%s status=%s", req.Data.ID, data.EventFulfilled) + ape.RenderErr(w, problems.NotFound()) return } - // can't return balance with rank on update, see create_balance.go - balance := getBalanceByDID(event.UserDID, true, w, r) - if balance == nil { + + if !auth.Authenticates(UserClaims(r), auth.UserGrant(event.UserDID)) { + ape.RenderErr(w, problems.Unauthorized()) return } - ape.Render(w, newClaimEventResponse(*event, *evType, *balance)) -} - -func getEventToClaim(id string, w http.ResponseWriter, r *http.Request) *data.Event { - event, err := EventsQ(r). - FilterByID(id). - FilterByStatus(data.EventFulfilled). - Get() + evType := EventTypes(r).Get(event.Type) + if evType == nil { + Log(r).Error("Wrong event type is stored in DB: might be bad event config") + ape.RenderErr(w, problems.InternalError()) + return + } + event, err = claimEventWithPoints(*event, evType.Reward, r) if err != nil { - Log(r).WithError(err).Error("Failed to get event by balance ID") + Log(r).WithError(err).Error("Failed to claim event and accrue points to the balance") ape.RenderErr(w, problems.InternalError()) - return nil + return } - if event == nil { - Log(r).Debugf("Event not found for id=%s status=%s", id, data.EventFulfilled) - ape.RenderErr(w, problems.NotFound()) - return nil + // balance should exist cause of previous logic + balance, err := getBalanceByDID(event.UserDID, true, r) + if err != nil { + Log(r).WithError(err).Error("Failed to get balance by DID") + ape.RenderErr(w, problems.InternalError()) + return } - return event + ape.Render(w, newClaimEventResponse(*event, *evType, *balance)) } -func claimEventWithPoints(event data.Event, reward int32, w http.ResponseWriter, r *http.Request) (claimed *data.Event) { - err := EventsQ(r).Transaction(func() error { +// requires: event exist +func claimEventWithPoints(event data.Event, reward int64, r *http.Request) (claimed *data.Event, err error) { + err = EventsQ(r).Transaction(func() error { updated, err := EventsQ(r).FilterByID(event.ID).Update(data.EventClaimed, nil, &reward) if err != nil { return fmt.Errorf("update event status: %w", err) @@ -79,12 +82,6 @@ func claimEventWithPoints(event data.Event, reward int32, w http.ResponseWriter, claimed = updated return nil }) - - if err != nil { - Log(r).WithError(err).Error("Failed to claim event and accrue points to the balance") - ape.RenderErr(w, problems.InternalError()) - } - return } diff --git a/internal/service/handlers/create_balance.go b/internal/service/handlers/create_balance.go index 7149b5a..323fdda 100644 --- a/internal/service/handlers/create_balance.go +++ b/internal/service/handlers/create_balance.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" + "github.com/rarimo/auth-svc/pkg/auth" "github.com/rarimo/rarime-points-svc/internal/service/requests" "gitlab.com/distributed_lab/ape" "gitlab.com/distributed_lab/ape/problems" @@ -17,7 +18,20 @@ func CreateBalance(w http.ResponseWriter, r *http.Request) { } did := req.Data.ID - balance := getBalanceByDID(did, false, w, r) + + if !auth.Authenticates(UserClaims(r), auth.UserGrant(did)) { + ape.RenderErr(w, problems.Unauthorized()) + return + } + + balance, err := getBalanceByDID(did, false, r) + if err != nil { + Log(r).WithError(err).Error("Failed to get balance by DID") + ape.RenderErr(w, problems.InternalError()) + return + } + + // Balance should not exist if balance != nil { ape.RenderErr(w, problems.Conflict()) return @@ -42,9 +56,14 @@ func CreateBalance(w http.ResponseWriter, r *http.Request) { // We can't return inserted balance in a single query, because we can't calculate // rank in transaction: RANK() is a window function allowed on a set of rows, // while with RETURNING we operate a single one. - balance = getBalanceByDID(did, true, w, r) - if balance == nil { + + // Balance will exist cause of previous logic + balance, err = getBalanceByDID(did, true, r) + if err != nil { + Log(r).WithError(err).Error("Failed to get balance by DID") + ape.RenderErr(w, problems.InternalError()) return } + ape.Render(w, newBalanceModel(*balance)) } diff --git a/internal/service/handlers/ctx.go b/internal/service/handlers/ctx.go index 2604717..d95e97b 100644 --- a/internal/service/handlers/ctx.go +++ b/internal/service/handlers/ctx.go @@ -4,7 +4,7 @@ import ( "context" "net/http" - "github.com/rarimo/rarime-auth-svc/resources" + "github.com/rarimo/auth-svc/resources" "github.com/rarimo/rarime-points-svc/internal/data" "github.com/rarimo/rarime-points-svc/internal/data/evtypes" "github.com/rarimo/saver-grpc-lib/broadcaster" @@ -94,12 +94,12 @@ func Broadcaster(r *http.Request) broadcaster.Broadcaster { return r.Context().Value(broadcasterCtxKey).(broadcaster.Broadcaster) } -func CtxPointPrice(price int32) func(context.Context) context.Context { +func CtxPointPrice(price int64) func(context.Context) context.Context { return func(ctx context.Context) context.Context { return context.WithValue(ctx, pointPriceCtxKey, price) } } -func PointPrice(r *http.Request) int32 { - return r.Context().Value(pointPriceCtxKey).(int32) +func PointPrice(r *http.Request) int64 { + return r.Context().Value(pointPriceCtxKey).(int64) } diff --git a/internal/service/handlers/get_balance.go b/internal/service/handlers/get_balance.go index 489bad0..34b8322 100644 --- a/internal/service/handlers/get_balance.go +++ b/internal/service/handlers/get_balance.go @@ -3,7 +3,7 @@ package handlers import ( "net/http" - "github.com/rarimo/rarime-auth-svc/pkg/auth" + "github.com/rarimo/auth-svc/pkg/auth" "github.com/rarimo/rarime-points-svc/internal/data" "github.com/rarimo/rarime-points-svc/internal/service/requests" "github.com/rarimo/rarime-points-svc/resources" @@ -18,8 +18,20 @@ func GetBalance(w http.ResponseWriter, r *http.Request) { return } - balance := getBalanceByDID(req.DID, true, w, r) + if !auth.Authenticates(UserClaims(r), auth.UserGrant(req.DID)) { + ape.RenderErr(w, problems.Unauthorized()) + return + } + + balance, err := getBalanceByDID(req.DID, true, r) + 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 } @@ -42,29 +54,11 @@ func newBalanceModel(balance data.Balance) resources.Balance { } } -func getBalanceByDID(did string, withRank bool, w http.ResponseWriter, r *http.Request) *data.Balance { - if !auth.Authenticates(UserClaims(r), auth.UserGrant(did)) { - ape.RenderErr(w, problems.Unauthorized()) - return nil - } - +func getBalanceByDID(did string, withRank bool, r *http.Request) (*data.Balance, error) { q := BalancesQ(r).FilterByDID(did) if withRank { q.WithRank() } - balance, err := q.Get() - if err != nil { - Log(r).WithError(err).Error("Failed to get balance by DID") - ape.RenderErr(w, problems.InternalError()) - return nil - } - - if balance == nil { - Log(r).Debugf("Balance not found for DID %s", did) - ape.RenderErr(w, problems.NotFound()) - return nil - } - - return balance + return q.Get() } diff --git a/internal/service/handlers/list_events.go b/internal/service/handlers/list_events.go index a8712b0..bf7bce2 100644 --- a/internal/service/handlers/list_events.go +++ b/internal/service/handlers/list_events.go @@ -4,12 +4,13 @@ import ( "encoding/json" "net/http" - "github.com/rarimo/rarime-auth-svc/pkg/auth" + "github.com/rarimo/auth-svc/pkg/auth" "github.com/rarimo/rarime-points-svc/internal/data" "github.com/rarimo/rarime-points-svc/internal/service/requests" "github.com/rarimo/rarime-points-svc/resources" "gitlab.com/distributed_lab/ape" "gitlab.com/distributed_lab/ape/problems" + "gitlab.com/distributed_lab/logan/v3/errors" ) func ListEvents(w http.ResponseWriter, r *http.Request) { @@ -50,8 +51,10 @@ func ListEvents(w http.ResponseWriter, r *http.Request) { } } - meta, ok := getOrderedEventsMeta(events, w, r) - if !ok { + meta, err := getOrderedEventsMeta(events, r) + if err != nil { + Log(r).WithError(err).Error("Failed to get ordered events metadata") + ape.RenderErr(w, problems.InternalError()) return } @@ -70,28 +73,21 @@ func ListEvents(w http.ResponseWriter, r *http.Request) { ape.Render(w, resp) } -func getOrderedEventsMeta(events []data.Event, w http.ResponseWriter, r *http.Request) ([]resources.EventStaticMeta, bool) { +func getOrderedEventsMeta(events []data.Event, r *http.Request) ([]resources.EventStaticMeta, error) { res := make([]resources.EventStaticMeta, len(events)) for i, event := range events { evType := EventTypes(r).Get(event.Type) if evType == nil { - Log(r).Error("Wrong event type is stored in DB: might be bad event config") - ape.RenderErr(w, problems.InternalError()) - return nil, false + return nil, errors.New("wrong event type is stored in DB: might be bad event config") } res[i] = *evType } - return res, true + return res, nil } func newEventModel(event data.Event, meta resources.EventStaticMeta) resources.Event { - var points *int32 - if event.PointsAmount.Valid { - points = &event.PointsAmount.Int32 - } - return resources.Event{ Key: resources.Key{ ID: event.ID, @@ -105,7 +101,7 @@ func newEventModel(event data.Event, meta resources.EventStaticMeta) resources.E Dynamic: (*json.RawMessage)(&event.Meta), }, Status: event.Status.String(), - PointsAmount: points, + PointsAmount: event.PointsAmount, }, } } diff --git a/internal/service/handlers/list_withdrawals.go b/internal/service/handlers/list_withdrawals.go index 37d2693..746a77f 100644 --- a/internal/service/handlers/list_withdrawals.go +++ b/internal/service/handlers/list_withdrawals.go @@ -3,7 +3,7 @@ package handlers import ( "net/http" - "github.com/rarimo/rarime-auth-svc/pkg/auth" + "github.com/rarimo/auth-svc/pkg/auth" "github.com/rarimo/rarime-points-svc/internal/data" "github.com/rarimo/rarime-points-svc/internal/service/requests" "github.com/rarimo/rarime-points-svc/resources" @@ -37,6 +37,7 @@ func ListWithdrawals(w http.ResponseWriter, r *http.Request) { resp := newWithdrawalsResponse(withdrawals) resp.Links = req.CursorParams.GetLinks(r, last) + ape.Render(w, resp) } func newWithdrawalsResponse(withdrawals []data.Withdrawal) resources.WithdrawalListResponse { diff --git a/internal/service/handlers/middleware.go b/internal/service/handlers/middleware.go index 376f438..f87b5aa 100644 --- a/internal/service/handlers/middleware.go +++ b/internal/service/handlers/middleware.go @@ -3,7 +3,7 @@ package handlers import ( "net/http" - "github.com/rarimo/rarime-auth-svc/pkg/auth" + "github.com/rarimo/auth-svc/pkg/auth" "gitlab.com/distributed_lab/ape" "gitlab.com/distributed_lab/ape/problems" "gitlab.com/distributed_lab/logan/v3" @@ -12,7 +12,7 @@ import ( func AuthMiddleware(auth *auth.Client, log *logan.Entry) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - claims, err := auth.ValidateJWT(r.Header) + claims, err := auth.ValidateJWT(r) if err != nil { log.WithError(err).Error("failed to execute auth validate request") ape.Render(w, problems.InternalError()) diff --git a/internal/service/handlers/withdraw.go b/internal/service/handlers/withdraw.go index 963887c..bed307e 100644 --- a/internal/service/handlers/withdraw.go +++ b/internal/service/handlers/withdraw.go @@ -8,6 +8,7 @@ import ( cosmos "github.com/cosmos/cosmos-sdk/types" bank "github.com/cosmos/cosmos-sdk/x/bank/types" validation "github.com/go-ozzo/ozzo-validation/v4" + "github.com/rarimo/auth-svc/pkg/auth" "github.com/rarimo/rarime-points-svc/internal/data" "github.com/rarimo/rarime-points-svc/internal/service/requests" "github.com/rarimo/rarime-points-svc/resources" @@ -22,7 +23,25 @@ func Withdraw(w http.ResponseWriter, r *http.Request) { return } - if !isEligibleToWithdraw(req, w, r) { + if !auth.Authenticates(UserClaims(r), auth.UserGrant(req.Data.ID)) { + ape.RenderErr(w, problems.Unauthorized()) + return + } + + balance, err := getBalanceByDID(req.Data.ID, true, r) + 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 + } + + if err := isEligibleToWithdraw(balance, req.Data.Attributes.Amount); err != nil { + ape.RenderErr(w, problems.BadRequest(err)...) return } @@ -54,9 +73,11 @@ func Withdraw(w http.ResponseWriter, r *http.Request) { return } - // see create_balance.go for explanation - balance := getBalanceByDID(req.Data.ID, true, w, r) - if balance == nil { + // balance should exist cause of previous logic + balance, err = getBalanceByDID(req.Data.ID, true, r) + if err != nil { + Log(r).WithError(err).Error("Failed to get balance by DID") + ape.RenderErr(w, problems.InternalError()) return } @@ -81,30 +102,24 @@ func newWithdrawResponse(w data.Withdrawal, balance data.Balance) *resources.Wit return &resp } -func isEligibleToWithdraw(req resources.WithdrawRequest, w http.ResponseWriter, r *http.Request) bool { - balance := getBalanceByDID(req.Data.ID, false, w, r) - if balance == nil { - return false - } - - render := func(field, format string, a ...any) bool { - ape.RenderErr(w, problems.BadRequest(validation.Errors{ +func isEligibleToWithdraw(balance *data.Balance, amount int64) validation.Errors { + mapValidationErr := func(field, format string, a ...any) validation.Errors { + return validation.Errors{ field: fmt.Errorf(format, a...), - })...) - return false + } } if !balance.PassportHash.Valid { - return render("is_verified", "user must have verified passport for withdrawals") + return mapValidationErr("is_verified", "user must have verified passport for withdrawals") } if balance.PassportExpires.Time.Before(time.Now().UTC()) { - return render("is_verified", "user passport is expired") + return mapValidationErr("is_verified", "user passport is expired") } - if balance.Amount < req.Data.Attributes.Amount { - return render("data/attributes/amount", "insufficient balance: %d", balance.Amount) + if balance.Amount < amount { + return mapValidationErr("data/attributes/amount", "insufficient balance: %d", balance.Amount) } - return true + return nil } func broadcastWithdrawalTx(req resources.WithdrawRequest, r *http.Request) error { diff --git a/resources/model_balance_attributes.go b/resources/model_balance_attributes.go index 229263c..03421da 100644 --- a/resources/model_balance_attributes.go +++ b/resources/model_balance_attributes.go @@ -6,7 +6,7 @@ package resources type BalanceAttributes struct { // Amount of points - Amount int32 `json:"amount"` + Amount int64 `json:"amount"` // Unix timestamp of balance creation CreatedAt int32 `json:"created_at"` // Whether the user has scanned passport diff --git a/resources/model_event_attributes.go b/resources/model_event_attributes.go index 26f3f6a..14d6800 100644 --- a/resources/model_event_attributes.go +++ b/resources/model_event_attributes.go @@ -9,7 +9,7 @@ type EventAttributes struct { CreatedAt int32 `json:"created_at"` Meta EventMeta `json:"meta"` // How many points were accrued. Required only for `claimed` events. This is necessary, as the reward might change over time, while the certain balance should be left intact. - PointsAmount *int32 `json:"points_amount,omitempty"` + PointsAmount *int64 `json:"points_amount,omitempty"` // See `filter[status]` parameter for explanation Status string `json:"status"` // Unix timestamp of the event status change diff --git a/resources/model_event_static_meta.go b/resources/model_event_static_meta.go index 659e1be..ac16602 100644 --- a/resources/model_event_static_meta.go +++ b/resources/model_event_static_meta.go @@ -18,6 +18,6 @@ type EventStaticMeta struct { // If true, the event will not be created with `open` status automatically when user creates the balance. NoAutoOpen bool `json:"no_auto_open"` // Reward amount in points - Reward int32 `json:"reward"` + Reward int64 `json:"reward"` Title string `json:"title"` } diff --git a/resources/model_point_price_attributes.go b/resources/model_point_price_attributes.go index e653436..c571955 100644 --- a/resources/model_point_price_attributes.go +++ b/resources/model_point_price_attributes.go @@ -6,5 +6,5 @@ package resources type PointPriceAttributes struct { // Amount of `urmo` tokens for one point - Urmo int32 `json:"urmo"` + Urmo int64 `json:"urmo"` } diff --git a/resources/model_withdraw_attributes.go b/resources/model_withdraw_attributes.go index 9bbea67..2f6bdb6 100644 --- a/resources/model_withdraw_attributes.go +++ b/resources/model_withdraw_attributes.go @@ -8,5 +8,5 @@ type WithdrawAttributes struct { // Rarimo address to withdraw to. Can be any valid address. Address string `json:"address"` // Amount of points to withdraw - Amount int32 `json:"amount"` + Amount int64 `json:"amount"` } diff --git a/resources/model_withdrawal_attributes.go b/resources/model_withdrawal_attributes.go index 60af863..703d2d3 100644 --- a/resources/model_withdrawal_attributes.go +++ b/resources/model_withdrawal_attributes.go @@ -8,7 +8,7 @@ type WithdrawalAttributes struct { // Rarimo address which points were withdrawn to. Can be any valid address. Address string `json:"address"` // Amount of points withdrawn - Amount int32 `json:"amount"` + Amount int64 `json:"amount"` // Unix timestamp of withdrawal creation CreatedAt int32 `json:"created_at"` }