New code verification service
Some checks failed
ci/woodpecker/push/bff Pipeline was successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/fx_ingestor Pipeline was successful
ci/woodpecker/push/billing_fees Pipeline was successful
ci/woodpecker/push/chain_gateway Pipeline was successful
ci/woodpecker/push/fx_oracle Pipeline was successful
ci/woodpecker/push/frontend Pipeline was successful
ci/woodpecker/push/nats Pipeline was successful
ci/woodpecker/push/ledger Pipeline was successful
ci/woodpecker/push/notification Pipeline was successful
ci/woodpecker/push/payments_orchestrator Pipeline was successful
ci/woodpecker/push/bump_version Pipeline failed
Some checks failed
ci/woodpecker/push/bff Pipeline was successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/fx_ingestor Pipeline was successful
ci/woodpecker/push/billing_fees Pipeline was successful
ci/woodpecker/push/chain_gateway Pipeline was successful
ci/woodpecker/push/fx_oracle Pipeline was successful
ci/woodpecker/push/frontend Pipeline was successful
ci/woodpecker/push/nats Pipeline was successful
ci/woodpecker/push/ledger Pipeline was successful
ci/woodpecker/push/notification Pipeline was successful
ci/woodpecker/push/payments_orchestrator Pipeline was successful
ci/woodpecker/push/bump_version Pipeline failed
This commit is contained in:
16
api/pkg/db/confirmation/confirmation.go
Normal file
16
api/pkg/db/confirmation/confirmation.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package confirmation
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tech/sendico/pkg/db/template"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type DB interface {
|
||||
template.DB[*model.ConfirmationCode]
|
||||
|
||||
FindActive(ctx context.Context, accountRef primitive.ObjectID, destination string, target model.ConfirmationTarget, now int64) (*model.ConfirmationCode, error)
|
||||
DeleteTuple(ctx context.Context, accountRef primitive.ObjectID, destination string, target model.ConfirmationTarget) error
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package db
|
||||
import (
|
||||
"github.com/tech/sendico/pkg/auth"
|
||||
"github.com/tech/sendico/pkg/db/account"
|
||||
"github.com/tech/sendico/pkg/db/confirmation"
|
||||
mongoimpl "github.com/tech/sendico/pkg/db/internal/mongo"
|
||||
"github.com/tech/sendico/pkg/db/invitation"
|
||||
"github.com/tech/sendico/pkg/db/organization"
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
// Factory exposes high-level repositories used by application services.
|
||||
type Factory interface {
|
||||
NewRefreshTokensDB() (refreshtokens.DB, error)
|
||||
NewConfirmationsDB() (confirmation.DB, error)
|
||||
|
||||
NewAccountDB() (account.DB, error)
|
||||
NewOrganizationDB() (organization.DB, error)
|
||||
|
||||
67
api/pkg/db/internal/mongo/confirmationdb/db.go
Normal file
67
api/pkg/db/internal/mongo/confirmationdb/db.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package confirmationdb
|
||||
|
||||
import (
|
||||
"github.com/tech/sendico/pkg/db/confirmation"
|
||||
ri "github.com/tech/sendico/pkg/db/repository/index"
|
||||
"github.com/tech/sendico/pkg/db/template"
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const (
|
||||
fieldAccountRef = "accountRef"
|
||||
fieldDestination = "destination"
|
||||
fieldTarget = "target"
|
||||
fieldExpiresAt = "expiresAt"
|
||||
fieldUsed = "used"
|
||||
)
|
||||
|
||||
type ConfirmationDB struct {
|
||||
template.DBImp[*model.ConfirmationCode]
|
||||
}
|
||||
|
||||
func Create(logger mlogger.Logger, db *mongo.Database) (confirmation.DB, error) {
|
||||
p := &ConfirmationDB{
|
||||
DBImp: *template.Create[*model.ConfirmationCode](logger, mservice.Confirmations, db),
|
||||
}
|
||||
|
||||
// Ensure one active code per account/destination/target.
|
||||
if err := p.Repository.CreateIndex(&ri.Definition{
|
||||
Keys: []ri.Key{
|
||||
{Field: fieldAccountRef, Sort: ri.Asc},
|
||||
{Field: fieldDestination, Sort: ri.Asc},
|
||||
{Field: fieldTarget, Sort: ri.Asc},
|
||||
},
|
||||
Unique: true,
|
||||
}); err != nil {
|
||||
p.Logger.Error("Failed to create confirmation unique index", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TTL on expiry.
|
||||
ttl := int32(0)
|
||||
if err := p.Repository.CreateIndex(&ri.Definition{
|
||||
Keys: []ri.Key{
|
||||
{Field: fieldExpiresAt, Sort: ri.Asc},
|
||||
},
|
||||
TTL: &ttl,
|
||||
}); err != nil {
|
||||
p.Logger.Error("Failed to create confirmation TTL index", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Query helper indexes.
|
||||
if err := p.Repository.CreateIndex(&ri.Definition{
|
||||
Keys: []ri.Key{
|
||||
{Field: fieldUsed, Sort: ri.Asc},
|
||||
},
|
||||
}); err != nil {
|
||||
p.Logger.Error("Failed to create confirmation used index", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
17
api/pkg/db/internal/mongo/confirmationdb/delete.go
Normal file
17
api/pkg/db/internal/mongo/confirmationdb/delete.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package confirmationdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tech/sendico/pkg/db/repository"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func (db *ConfirmationDB) DeleteTuple(ctx context.Context, accountRef primitive.ObjectID, destination string, target model.ConfirmationTarget) error {
|
||||
query := repository.Query().
|
||||
Filter(repository.Field(fieldAccountRef), accountRef).
|
||||
Filter(repository.Field(fieldDestination), destination).
|
||||
Filter(repository.Field(fieldTarget), target)
|
||||
return db.DeleteMany(ctx, query)
|
||||
}
|
||||
26
api/pkg/db/internal/mongo/confirmationdb/find.go
Normal file
26
api/pkg/db/internal/mongo/confirmationdb/find.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package confirmationdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/tech/sendico/pkg/db/repository"
|
||||
"github.com/tech/sendico/pkg/db/repository/builder"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func (db *ConfirmationDB) FindActive(ctx context.Context, accountRef primitive.ObjectID, destination string, target model.ConfirmationTarget, now int64) (*model.ConfirmationCode, error) {
|
||||
var res model.ConfirmationCode
|
||||
query := repository.Query().
|
||||
Filter(repository.Field(fieldAccountRef), accountRef).
|
||||
Filter(repository.Field(fieldDestination), destination).
|
||||
Filter(repository.Field(fieldTarget), target).
|
||||
Filter(repository.Field(fieldUsed), false).
|
||||
Comparison(repository.Field(fieldExpiresAt), builder.Gt, time.Unix(now, 0))
|
||||
|
||||
if err := db.FindOne(ctx, query, &res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/tech/sendico/pkg/auth"
|
||||
"github.com/tech/sendico/pkg/db/account"
|
||||
"github.com/tech/sendico/pkg/db/confirmation"
|
||||
"github.com/tech/sendico/pkg/db/internal/mongo/accountdb"
|
||||
"github.com/tech/sendico/pkg/db/internal/mongo/confirmationdb"
|
||||
"github.com/tech/sendico/pkg/db/internal/mongo/invitationdb"
|
||||
"github.com/tech/sendico/pkg/db/internal/mongo/organizationdb"
|
||||
"github.com/tech/sendico/pkg/db/internal/mongo/policiesdb"
|
||||
@@ -156,6 +158,10 @@ func (db *DB) NewAccountDB() (account.DB, error) {
|
||||
return accountdb.Create(db.logger, db.db())
|
||||
}
|
||||
|
||||
func (db *DB) NewConfirmationsDB() (confirmation.DB, error) {
|
||||
return confirmationdb.Create(db.logger, db.db())
|
||||
}
|
||||
|
||||
func (db *DB) NewOrganizationDB() (organization.DB, error) {
|
||||
pdb, err := db.NewPoliciesDB()
|
||||
if err != nil {
|
||||
|
||||
@@ -3,9 +3,9 @@ package builderimp
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tech/sendico/pkg/db/repository/builder"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
@@ -45,7 +45,7 @@ func TestPipelineImp_Lookup(t *testing.T) {
|
||||
mockForeignField := &MockField{build: "foreignField"}
|
||||
mockAsField := &MockField{build: "asField"}
|
||||
|
||||
result := pipeline.Lookup(mservice.Projects, mockLocalField, mockForeignField, mockAsField)
|
||||
result := pipeline.Lookup(mservice.Site, mockLocalField, mockForeignField, mockAsField)
|
||||
|
||||
// Should return self for chaining
|
||||
assert.Same(t, pipeline, result)
|
||||
@@ -54,7 +54,7 @@ func TestPipelineImp_Lookup(t *testing.T) {
|
||||
assert.Len(t, built, 1)
|
||||
|
||||
expected := bson.D{{Key: string(builder.Lookup), Value: bson.D{
|
||||
{Key: string(builder.MKFrom), Value: mservice.Projects},
|
||||
{Key: string(builder.MKFrom), Value: mservice.Site},
|
||||
{Key: string(builder.MKLocalField), Value: "localField"},
|
||||
{Key: string(builder.MKForeignField), Value: "foreignField"},
|
||||
{Key: string(builder.MKAs), Value: "asField"},
|
||||
@@ -70,7 +70,7 @@ func TestPipelineImp_LookupWithPipeline_WithoutLet(t *testing.T) {
|
||||
}
|
||||
mockAsField := &MockField{build: "asField"}
|
||||
|
||||
result := pipeline.LookupWithPipeline(mservice.Tasks, mockNestedPipeline, mockAsField, nil)
|
||||
result := pipeline.LookupWithPipeline(mservice.Site, mockNestedPipeline, mockAsField, nil)
|
||||
|
||||
// Should return self for chaining
|
||||
assert.Same(t, pipeline, result)
|
||||
@@ -79,7 +79,7 @@ func TestPipelineImp_LookupWithPipeline_WithoutLet(t *testing.T) {
|
||||
assert.Len(t, built, 1)
|
||||
|
||||
expected := bson.D{{Key: string(builder.Lookup), Value: bson.D{
|
||||
{Key: string(builder.MKFrom), Value: mservice.Tasks},
|
||||
{Key: string(builder.MKFrom), Value: mservice.Site},
|
||||
{Key: string(builder.MKPipeline), Value: mockNestedPipeline.build},
|
||||
{Key: string(builder.MKAs), Value: "asField"},
|
||||
}}}
|
||||
@@ -99,7 +99,7 @@ func TestPipelineImp_LookupWithPipeline_WithLet(t *testing.T) {
|
||||
"projRef": mockLetField,
|
||||
}
|
||||
|
||||
result := pipeline.LookupWithPipeline(mservice.Tasks, mockNestedPipeline, mockAsField, &letVars)
|
||||
result := pipeline.LookupWithPipeline(mservice.Site, mockNestedPipeline, mockAsField, &letVars)
|
||||
|
||||
// Should return self for chaining
|
||||
assert.Same(t, pipeline, result)
|
||||
@@ -108,7 +108,7 @@ func TestPipelineImp_LookupWithPipeline_WithLet(t *testing.T) {
|
||||
assert.Len(t, built, 1)
|
||||
|
||||
expected := bson.D{{Key: string(builder.Lookup), Value: bson.D{
|
||||
{Key: string(builder.MKFrom), Value: mservice.Tasks},
|
||||
{Key: string(builder.MKFrom), Value: mservice.Site},
|
||||
{Key: string(builder.MKPipeline), Value: mockNestedPipeline.build},
|
||||
{Key: string(builder.MKAs), Value: "asField"},
|
||||
{Key: string(builder.MKLet), Value: bson.D{{Key: "projRef", Value: "$_id"}}},
|
||||
@@ -126,14 +126,14 @@ func TestPipelineImp_LookupWithPipeline_WithEmptyLet(t *testing.T) {
|
||||
|
||||
emptyLetVars := map[string]builder.Field{}
|
||||
|
||||
pipeline.LookupWithPipeline(mservice.Tasks, mockNestedPipeline, mockAsField, &emptyLetVars)
|
||||
pipeline.LookupWithPipeline(mservice.Site, mockNestedPipeline, mockAsField, &emptyLetVars)
|
||||
|
||||
built := pipeline.Build()
|
||||
assert.Len(t, built, 1)
|
||||
|
||||
// Should not include let field when empty
|
||||
expected := bson.D{{Key: string(builder.Lookup), Value: bson.D{
|
||||
{Key: string(builder.MKFrom), Value: mservice.Tasks},
|
||||
{Key: string(builder.MKFrom), Value: mservice.Site},
|
||||
{Key: string(builder.MKPipeline), Value: mockNestedPipeline.build},
|
||||
{Key: string(builder.MKAs), Value: "asField"},
|
||||
}}}
|
||||
|
||||
@@ -15,8 +15,8 @@ require (
|
||||
github.com/testcontainers/testcontainers-go v0.33.0
|
||||
github.com/testcontainers/testcontainers-go/modules/mongodb v0.33.0
|
||||
go.mongodb.org/mongo-driver v1.17.6
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/crypto v0.44.0
|
||||
go.uber.org/zap v1.27.1
|
||||
golang.org/x/crypto v0.45.0
|
||||
google.golang.org/grpc v1.77.0
|
||||
google.golang.org/protobuf v1.36.10
|
||||
)
|
||||
@@ -67,7 +67,7 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.67.3 // indirect
|
||||
github.com/prometheus/common v0.67.4 // indirect
|
||||
github.com/prometheus/procfs v0.19.2 // indirect
|
||||
github.com/shirou/gopsutil/v3 v3.24.5 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
|
||||
@@ -11,8 +11,6 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE=
|
||||
github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/casbin/casbin/v2 v2.132.0 h1:73hGmOszGSL3hTVquwkAi98XLl3gPJ+BxB6D7G9Fxtk=
|
||||
github.com/casbin/casbin/v2 v2.132.0/go.mod h1:FmcfntdXLTcYXv/hxgNntcRPqAbwOG9xsism0yXT+18=
|
||||
github.com/casbin/casbin/v2 v2.134.0 h1:wyO3hZb487GzlGVAI2hUoHQT0ehFD+9B5P+HVG9BVTM=
|
||||
github.com/casbin/casbin/v2 v2.134.0/go.mod h1:FmcfntdXLTcYXv/hxgNntcRPqAbwOG9xsism0yXT+18=
|
||||
github.com/casbin/govaluate v1.3.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A=
|
||||
@@ -128,14 +126,12 @@ github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.67.2 h1:PcBAckGFTIHt2+L3I33uNRTlKTplNzFctXcWhPyAEN8=
|
||||
github.com/prometheus/common v0.67.2/go.mod h1:63W3KZb1JOKgcjlIr64WW/LvFGAqKPj0atm+knVGEko=
|
||||
github.com/prometheus/common v0.67.3 h1:shd26MlnwTw5jksTDhC7rTQIteBxy+ZZDr3t7F2xN2Q=
|
||||
github.com/prometheus/common v0.67.3/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI=
|
||||
github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc=
|
||||
github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI=
|
||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
@@ -174,27 +170,23 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss=
|
||||
go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
|
||||
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
||||
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
|
||||
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
|
||||
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
|
||||
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
|
||||
@@ -202,16 +194,16 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
|
||||
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
|
||||
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@@ -275,12 +267,10 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b h1:ULiyYQ0FdsJhwwZUwbaXpZF5yUE3h+RA+gxvBu37ucc=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:oDOGiMSXHL4sDTJvFvIB9nRQCGdLP1o/iVaqQK8zB+M=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba h1:UKgtfRM7Yh93Sya0Fo8ZzhDP4qBckrrxEr2oF5UIVb8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
|
||||
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
|
||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
||||
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package notifications
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
messaging "github.com/tech/sendico/pkg/messaging/envelope"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
nm "github.com/tech/sendico/pkg/model/notification"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type confirmationCodePayload struct {
|
||||
AccountRef string `json:"accountRef"`
|
||||
Destination string `json:"destination"`
|
||||
Target string `json:"target"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
type ConfirmationCodeNotification struct {
|
||||
messaging.Envelope
|
||||
payload confirmationCodePayload
|
||||
}
|
||||
|
||||
func (ccn *ConfirmationCodeNotification) Serialize() ([]byte, error) {
|
||||
data, err := json.Marshal(ccn.payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ccn.Envelope.Wrap(data)
|
||||
}
|
||||
|
||||
func newConfirmationEvent(action nm.NotificationAction) model.NotificationEvent {
|
||||
return model.NewNotification(mservice.Confirmations, action)
|
||||
}
|
||||
|
||||
func NewConfirmationCodeEnvelope(sender string, accountRef primitive.ObjectID, destination string, target model.ConfirmationTarget, code string) messaging.Envelope {
|
||||
return &ConfirmationCodeNotification{
|
||||
Envelope: messaging.CreateEnvelope(sender, newConfirmationEvent(nm.NAPending)),
|
||||
payload: confirmationCodePayload{
|
||||
AccountRef: accountRef.Hex(),
|
||||
Destination: destination,
|
||||
Target: string(target),
|
||||
Code: code,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package notifications
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/tech/sendico/pkg/db/account"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
me "github.com/tech/sendico/pkg/messaging/envelope"
|
||||
ch "github.com/tech/sendico/pkg/messaging/notifications/confirmation/handler"
|
||||
np "github.com/tech/sendico/pkg/messaging/notifications/processor"
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
nm "github.com/tech/sendico/pkg/model/notification"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type ConfirmationCodeProcessor struct {
|
||||
logger mlogger.Logger
|
||||
db account.DB
|
||||
handler ch.ConfirmationCodeHandler
|
||||
event model.NotificationEvent
|
||||
}
|
||||
|
||||
func (ccp *ConfirmationCodeProcessor) Process(ctx context.Context, envelope me.Envelope) error {
|
||||
var msg confirmationCodePayload
|
||||
if err := json.Unmarshal(envelope.GetData(), &msg); err != nil {
|
||||
ccp.logger.Warn("Failed to unmarshal confirmation code envelope", zap.Error(err), zap.String("topic", ccp.event.ToString()))
|
||||
return err
|
||||
}
|
||||
|
||||
accountRef, err := primitive.ObjectIDFromHex(msg.AccountRef)
|
||||
if err != nil {
|
||||
ccp.logger.Warn("Failed to restore account id from envelope", zap.Error(err), zap.String("topic", ccp.event.ToString()), zap.String("account_ref", msg.AccountRef))
|
||||
return err
|
||||
}
|
||||
|
||||
var account model.Account
|
||||
if err := ccp.db.Get(ctx, accountRef, &account); err != nil {
|
||||
ccp.logger.Warn("Failed to fetch account for confirmation code", zap.Error(err), zap.String("topic", ccp.event.ToString()), zap.String("account_ref", msg.AccountRef))
|
||||
return err
|
||||
}
|
||||
|
||||
target := model.ConfirmationTarget(msg.Target)
|
||||
if target != model.ConfirmationTargetLogin && target != model.ConfirmationTargetPayout {
|
||||
return merrors.InvalidArgument("invalid confirmation target", "target")
|
||||
}
|
||||
if msg.Code == "" {
|
||||
return merrors.InvalidArgument("empty confirmation code", "code")
|
||||
}
|
||||
if msg.Destination == "" {
|
||||
return merrors.InvalidArgument("empty destination", "destination")
|
||||
}
|
||||
|
||||
return ccp.handler(ctx, &account, msg.Destination, target, msg.Code)
|
||||
}
|
||||
|
||||
func (ccp *ConfirmationCodeProcessor) GetSubject() model.NotificationEvent {
|
||||
return ccp.event
|
||||
}
|
||||
|
||||
func NewConfirmationCodeProcessor(logger mlogger.Logger, db account.DB, handler ch.ConfirmationCodeHandler) np.EnvelopeProcessor {
|
||||
return &ConfirmationCodeProcessor{
|
||||
logger: logger.Named("confirmation_code_processor"),
|
||||
db: db,
|
||||
handler: handler,
|
||||
event: newConfirmationEvent(nm.NAPending),
|
||||
}
|
||||
}
|
||||
20
api/pkg/messaging/notifications/confirmation/confirmation.go
Normal file
20
api/pkg/messaging/notifications/confirmation/confirmation.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package notifications
|
||||
|
||||
import (
|
||||
"github.com/tech/sendico/pkg/db/account"
|
||||
messaging "github.com/tech/sendico/pkg/messaging/envelope"
|
||||
cinternal "github.com/tech/sendico/pkg/messaging/internal/notifications/confirmation"
|
||||
ch "github.com/tech/sendico/pkg/messaging/notifications/confirmation/handler"
|
||||
np "github.com/tech/sendico/pkg/messaging/notifications/processor"
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func Code(sender string, accountRef primitive.ObjectID, destination string, target model.ConfirmationTarget, code string) messaging.Envelope {
|
||||
return cinternal.NewConfirmationCodeEnvelope(sender, accountRef, destination, target, code)
|
||||
}
|
||||
|
||||
func NewConfirmationCodeProcessor(logger mlogger.Logger, db account.DB, handler ch.ConfirmationCodeHandler) np.EnvelopeProcessor {
|
||||
return cinternal.NewConfirmationCodeProcessor(logger, db, handler)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
)
|
||||
|
||||
type ConfirmationCodeHandler = func(context.Context, *model.Account, string, model.ConfirmationTarget, string) error
|
||||
43
api/pkg/model/confirmation.go
Normal file
43
api/pkg/model/confirmation.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type ConfirmationTarget string
|
||||
|
||||
const (
|
||||
ConfirmationTargetLogin ConfirmationTarget = "login"
|
||||
ConfirmationTargetPayout ConfirmationTarget = "payout"
|
||||
)
|
||||
|
||||
// ConfirmationCode stores verification codes for operations like login or payouts.
|
||||
type ConfirmationCode struct {
|
||||
AccountBoundBase `bson:",inline" json:",inline"`
|
||||
|
||||
Destination string `bson:"destination" json:"destination"`
|
||||
Target ConfirmationTarget `bson:"target" json:"target"`
|
||||
CodeHash []byte `bson:"codeHash" json:"-"`
|
||||
Salt []byte `bson:"salt" json:"-"`
|
||||
ExpiresAt time.Time `bson:"expiresAt" json:"expiresAt"`
|
||||
Attempts int `bson:"attempts" json:"attempts"`
|
||||
MaxAttempts int `bson:"maxAttempts" json:"maxAttempts"`
|
||||
ResendCount int `bson:"resendCount" json:"resendCount"`
|
||||
ResendLimit int `bson:"resendLimit" json:"resendLimit"`
|
||||
CooldownUntil time.Time `bson:"cooldownUntil" json:"cooldownUntil"`
|
||||
Used bool `bson:"used" json:"used"`
|
||||
}
|
||||
|
||||
func (c *ConfirmationCode) Collection() string {
|
||||
return mservice.Confirmations
|
||||
}
|
||||
|
||||
func NewConfirmationCode(accountRef primitive.ObjectID) *ConfirmationCode {
|
||||
cc := &ConfirmationCode{}
|
||||
cc.SetID(primitive.NewObjectID())
|
||||
cc.AccountRef = &accountRef
|
||||
return cc
|
||||
}
|
||||
@@ -6,12 +6,11 @@ type Type = string
|
||||
|
||||
const (
|
||||
Accounts Type = "accounts" // Represents user accounts in the system
|
||||
Confirmations Type = "confirmations" // Represents confirmation code flows
|
||||
Amplitude Type = "amplitude" // Represents analytics integration with Amplitude
|
||||
Site Type = "site" // Represents public site endpoints
|
||||
Automations Type = "automation" // Represents automation workflows
|
||||
Changes Type = "changes" // Tracks changes made to resources
|
||||
Clients Type = "clients" // Represents client information
|
||||
Comments Type = "comments" // Represents comments on tasks or other resources
|
||||
ChainGateway Type = "chain_gateway" // Represents chain gateway microservice
|
||||
FXOracle Type = "fx_oracle" // Represents FX oracle microservice
|
||||
FeePlans Type = "fee_plans" // Represents fee plans microservice
|
||||
@@ -37,37 +36,22 @@ const (
|
||||
Permissions Type = "permissions" // Represents permissiosns service
|
||||
Policies Type = "policies" // Represents access control policies
|
||||
PolicyAssignements Type = "policy_assignments" // Represents policy assignments database
|
||||
Priorities Type = "priorities" // Represents object properties
|
||||
PriorityGroups Type = "priority_groups" // Represents task or project priorities
|
||||
Projects Type = "projects" // Represents projects managed in the system
|
||||
PropertyBindings Type = "property_bindings" // Represents properties bindings of resources
|
||||
PropertySchemas Type = "property_schemas" // Represents properties or attributes of resources
|
||||
Properties Type = "properties" // Represents property values of the propertites of specific objects
|
||||
Reactions Type = "reactions" // Represents comment reactions
|
||||
RefreshTokens Type = "refresh_tokens" // Represents refresh tokens for authentication
|
||||
Roles Type = "roles" // Represents roles in access control
|
||||
Statuses Type = "statuses" // Represents statuses of tasks or projects
|
||||
StatusGroups Type = "status_groups" // Represents status groups
|
||||
Steps Type = "steps" // Represents steps in workflows or processes
|
||||
Storage Type = "storage" // Represents statuses of tasks or projects
|
||||
Tags Type = "tags" // Represents tags managed in the system
|
||||
Tasks Type = "tasks" // Represents tasks managed in the system
|
||||
Teams Type = "teams" // Represents teams managed in the system
|
||||
Tenants Type = "tenants" // Represents tenants managed in the system
|
||||
Workflows Type = "workflows" // Represents workflows for tasks or projects
|
||||
Workspaces Type = "workspaces" // Represents workspaces containing projects and teams
|
||||
)
|
||||
|
||||
func StringToSType(s string) (Type, error) {
|
||||
switch Type(s) {
|
||||
case Accounts, Amplitude, Site, Automations, Changes, Clients, Comments, ChainGateway, ChainWallets, ChainWalletBalances,
|
||||
case Accounts, Confirmations, Amplitude, Site, Changes, Clients, ChainGateway, ChainWallets, ChainWalletBalances,
|
||||
ChainTransfers, ChainDeposits, FXOracle, FeePlans, FilterProjects, Invitations, Invoices, Logo, Ledger,
|
||||
LedgerAccounts, LedgerBalances, LedgerEntries, LedgerOutbox, LedgerParties, LedgerPlines, Notifications,
|
||||
Organizations, Payments, PaymentOrchestrator, Permissions, Policies, PolicyAssignements, Priorities,
|
||||
PriorityGroups, Projects, PropertyBindings, PropertySchemas, Properties, Reactions, RefreshTokens, Roles,
|
||||
Statuses, StatusGroups, Steps, Storage, Tags, Tasks, Teams, Tenants, Workflows, Workspaces:
|
||||
Organizations, Payments, PaymentOrchestrator, Permissions, Policies, PolicyAssignements,
|
||||
RefreshTokens, Roles, Storage, Tenants, Workflows:
|
||||
return Type(s), nil
|
||||
default:
|
||||
return "", merrors.DataConflict("invalid service type: " + s)
|
||||
return "", merrors.InvalidArgument("invalid service type", s)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user