diff --git a/api/billing/fees/go.mod b/api/billing/fees/go.mod index 10bae4f..91255a8 100644 --- a/api/billing/fees/go.mod +++ b/api/billing/fees/go.mod @@ -10,7 +10,7 @@ require ( github.com/tech/sendico/fx/oracle v0.0.0 github.com/tech/sendico/pkg v0.1.0 go.mongodb.org/mongo-driver v1.17.6 - go.uber.org/zap v1.27.0 + go.uber.org/zap v1.27.1 google.golang.org/grpc v1.77.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -36,7 +36,7 @@ require ( github.com/nats-io/nuid v1.0.1 // indirect github.com/prometheus/client_golang v1.23.2 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/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect @@ -44,7 +44,7 @@ require ( github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect - golang.org/x/crypto v0.44.0 // indirect + golang.org/x/crypto v0.45.0 // indirect golang.org/x/net v0.47.0 // indirect golang.org/x/sync v0.18.0 // indirect golang.org/x/sys v0.38.0 // indirect diff --git a/api/billing/fees/go.sum b/api/billing/fees/go.sum index 13a4a5d..91ffdb4 100644 --- a/api/billing/fees/go.sum +++ b/api/billing/fees/go.sum @@ -9,8 +9,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= @@ -117,10 +115,8 @@ 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.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= @@ -156,32 +152,32 @@ 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/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/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 v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +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.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.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-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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -218,8 +214,6 @@ 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/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= diff --git a/api/chain/gateway/go.mod b/api/chain/gateway/go.mod index 3f79cec..be00771 100644 --- a/api/chain/gateway/go.mod +++ b/api/chain/gateway/go.mod @@ -14,7 +14,7 @@ require ( github.com/stretchr/testify v1.11.1 github.com/tech/sendico/pkg v0.1.0 go.mongodb.org/mongo-driver v1.17.6 - go.uber.org/zap v1.27.0 + go.uber.org/zap v1.27.1 google.golang.org/grpc v1.77.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 @@ -65,7 +65,7 @@ require ( github.com/nats-io/nuid v1.0.1 // indirect github.com/pmezard/go-difflib v1.0.0 // 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/ryanuber/go-glob v1.0.0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect @@ -79,7 +79,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect - golang.org/x/crypto v0.44.0 // indirect + golang.org/x/crypto v0.45.0 // indirect golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect golang.org/x/net v0.47.0 // indirect golang.org/x/sync v0.18.0 // indirect diff --git a/api/chain/gateway/go.sum b/api/chain/gateway/go.sum index 704ef47..d7bb493 100644 --- a/api/chain/gateway/go.sum +++ b/api/chain/gateway/go.sum @@ -6,25 +6,17 @@ github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251110112254-48a6e677648f h1:B/TfTw73mVqWKDzJZhU9Qi9wQyYfmiCz9FnmpQsyv5M= -github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251110112254-48a6e677648f/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= -github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251117160429-c598d23eddcf h1:aZI2VRIP0LAI6Rw934WEAxxL0SNYSVt9vR9h/cP5Pbo= -github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251117160429-c598d23eddcf/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251119083800-2aa1d4cc79d7 h1:uups37roJCTtR/BrJa0WoMrxt3rzgV+Qrj+TxYyJoAo= github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251119083800-2aa1d4cc79d7/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0= github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bits-and-blooms/bitset v1.24.3 h1:Bte86SlO3lwPQqww+7BE9ZuUCKIjfqnG5jtEyqA9y9Y= -github.com/bits-and-blooms/bitset v1.24.3/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE= github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= 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= @@ -247,10 +239,8 @@ 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/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= @@ -285,12 +275,8 @@ github.com/testcontainers/testcontainers-go v0.33.0 h1:zJS9PfXYT5O0ZFXM2xxXfk4J5 github.com/testcontainers/testcontainers-go v0.33.0/go.mod h1:W80YpTa8D5C3Yy16icheD01UTDu+LmXIA2Keo+jWtT8= github.com/testcontainers/testcontainers-go/modules/mongodb v0.33.0 h1:iXVA84s5hKMS5gn01GWOYHE3ymy/2b+0YkpFeTxB2XY= github.com/testcontainers/testcontainers-go/modules/mongodb v0.33.0/go.mod h1:R6tMjTojRiaoo89fh/hf7tOmfzohdqSU17R9DwSVSog= -github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= -github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= -github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= -github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= @@ -310,32 +296,32 @@ 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/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/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 v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +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.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.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-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/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY= golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -378,8 +364,6 @@ 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/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= diff --git a/api/fx/ingestor/go.mod b/api/fx/ingestor/go.mod index c856265..50ced22 100644 --- a/api/fx/ingestor/go.mod +++ b/api/fx/ingestor/go.mod @@ -12,7 +12,7 @@ require ( github.com/prometheus/client_golang v1.23.2 github.com/tech/sendico/fx/storage v0.0.0 github.com/tech/sendico/pkg v0.1.0 - go.uber.org/zap v1.27.0 + go.uber.org/zap v1.27.1 gopkg.in/yaml.v3 v3.0.1 ) @@ -35,7 +35,7 @@ require ( github.com/nats-io/nkeys v0.4.11 // indirect github.com/nats-io/nuid v1.0.1 // 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/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect @@ -44,7 +44,7 @@ require ( go.mongodb.org/mongo-driver v1.17.6 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect - golang.org/x/crypto v0.44.0 // indirect + golang.org/x/crypto v0.45.0 // indirect golang.org/x/net v0.47.0 // indirect golang.org/x/sync v0.18.0 // indirect golang.org/x/sys v0.38.0 // indirect diff --git a/api/fx/ingestor/go.sum b/api/fx/ingestor/go.sum index 53148e5..91ffdb4 100644 --- a/api/fx/ingestor/go.sum +++ b/api/fx/ingestor/go.sum @@ -9,8 +9,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= @@ -117,10 +115,8 @@ 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.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= @@ -156,47 +152,41 @@ 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/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/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 v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +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.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.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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= -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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -205,8 +195,6 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -215,8 +203,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -226,12 +212,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/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/rpc v0.0.0-20251103181224-f26f9409b101 h1:tRPGkdGHuewF4UisLzzHHr1spKw92qLM98nIzxbC0wY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= 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= diff --git a/api/fx/oracle/go.mod b/api/fx/oracle/go.mod index 1659166..b1b2718 100644 --- a/api/fx/oracle/go.mod +++ b/api/fx/oracle/go.mod @@ -12,7 +12,7 @@ require ( github.com/tech/sendico/fx/storage v0.0.0 github.com/tech/sendico/pkg v0.1.0 go.mongodb.org/mongo-driver v1.17.6 - go.uber.org/zap v1.27.0 + go.uber.org/zap v1.27.1 google.golang.org/grpc v1.77.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 @@ -37,7 +37,7 @@ require ( github.com/nats-io/nkeys v0.4.11 // indirect github.com/nats-io/nuid v1.0.1 // 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/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect @@ -45,7 +45,7 @@ require ( github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect - golang.org/x/crypto v0.44.0 // indirect + golang.org/x/crypto v0.45.0 // indirect golang.org/x/net v0.47.0 // indirect golang.org/x/sync v0.18.0 // indirect golang.org/x/sys v0.38.0 // indirect diff --git a/api/fx/oracle/go.sum b/api/fx/oracle/go.sum index 13a4a5d..91ffdb4 100644 --- a/api/fx/oracle/go.sum +++ b/api/fx/oracle/go.sum @@ -9,8 +9,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= @@ -117,10 +115,8 @@ 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.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= @@ -156,32 +152,32 @@ 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/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/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 v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +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.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.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-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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -218,8 +214,6 @@ 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/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= diff --git a/api/fx/storage/go.mod b/api/fx/storage/go.mod index 8dd247c..7b1bf9b 100644 --- a/api/fx/storage/go.mod +++ b/api/fx/storage/go.mod @@ -7,7 +7,7 @@ replace github.com/tech/sendico/pkg => ../../pkg require ( github.com/tech/sendico/pkg v0.1.0 go.mongodb.org/mongo-driver v1.17.6 - go.uber.org/zap v1.27.0 + go.uber.org/zap v1.27.1 ) require ( @@ -25,7 +25,7 @@ require ( github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.44.0 // indirect + golang.org/x/crypto v0.45.0 // indirect golang.org/x/sync v0.18.0 // indirect golang.org/x/text v0.31.0 // indirect google.golang.org/protobuf v1.36.10 // indirect diff --git a/api/fx/storage/go.sum b/api/fx/storage/go.sum index bab4975..170b262 100644 --- a/api/fx/storage/go.sum +++ b/api/fx/storage/go.sum @@ -7,8 +7,6 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA 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= @@ -122,28 +120,26 @@ 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/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/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 v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +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/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= 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= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= -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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -151,8 +147,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -160,16 +154,14 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/api/ledger/go.mod b/api/ledger/go.mod index 12767dd..9bf4afe 100644 --- a/api/ledger/go.mod +++ b/api/ledger/go.mod @@ -10,7 +10,7 @@ require ( github.com/stretchr/testify v1.11.1 github.com/tech/sendico/pkg v0.1.0 go.mongodb.org/mongo-driver v1.17.6 - go.uber.org/zap v1.27.0 + go.uber.org/zap v1.27.1 google.golang.org/grpc v1.77.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 @@ -38,7 +38,7 @@ require ( github.com/nats-io/nuid v1.0.1 // indirect github.com/pmezard/go-difflib v1.0.0 // 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/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect @@ -46,7 +46,7 @@ require ( github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect - golang.org/x/crypto v0.44.0 // indirect + golang.org/x/crypto v0.45.0 // indirect golang.org/x/net v0.47.0 // indirect golang.org/x/sync v0.18.0 // indirect golang.org/x/sys v0.38.0 // indirect diff --git a/api/ledger/go.sum b/api/ledger/go.sum index 4ae6767..63f1b50 100644 --- a/api/ledger/go.sum +++ b/api/ledger/go.sum @@ -9,8 +9,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= @@ -117,10 +115,8 @@ 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.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= @@ -158,32 +154,32 @@ 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/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/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 v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +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.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.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-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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -220,8 +216,6 @@ 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/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= diff --git a/api/notification/go.mod b/api/notification/go.mod index 4f18fef..7d313cb 100644 --- a/api/notification/go.mod +++ b/api/notification/go.mod @@ -13,7 +13,7 @@ require ( github.com/tech/sendico/pkg v0.1.0 github.com/xhit/go-simple-mail/v2 v2.16.0 go.mongodb.org/mongo-driver v1.17.6 - go.uber.org/zap v1.27.0 + go.uber.org/zap v1.27.1 golang.org/x/text v0.31.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -38,7 +38,7 @@ require ( github.com/nats-io/nuid v1.0.1 // indirect github.com/prometheus/client_golang v1.23.2 // 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/sendgrid/rest v2.6.9+incompatible // indirect github.com/toorop/go-dkim v0.0.0-20250226130143-9025cce95817 // indirect @@ -48,7 +48,7 @@ require ( github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect - golang.org/x/crypto v0.44.0 // indirect + golang.org/x/crypto v0.45.0 // indirect golang.org/x/net v0.47.0 // indirect golang.org/x/sync v0.18.0 // indirect golang.org/x/sys v0.38.0 // indirect diff --git a/api/notification/go.sum b/api/notification/go.sum index acaab3e..5ae62e8 100644 --- a/api/notification/go.sum +++ b/api/notification/go.sum @@ -13,8 +13,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= @@ -123,10 +121,8 @@ 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.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= @@ -171,32 +167,32 @@ 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/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/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 v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +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.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.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-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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -233,8 +229,6 @@ 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/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= diff --git a/api/notification/i18n/en.json b/api/notification/i18n/en.json index 0cc34ad..24afb54 100644 --- a/api/notification/i18n/en.json +++ b/api/notification/i18n/en.json @@ -20,6 +20,10 @@ "mail.reset-password.greeting": "Hi, {{.Name}}", "mail.reset-password.body": "It looks like you requested a new password.

If that sounds right, you can enter a new password by clicking the followinglink<\/a>.

If you have not requested a passowrd change please contact us under
{{.SupportMail}}<\/a>.", + "mail.confirmation-code.subj": "{{.ServiceName}}: your confirmation code", + "mail.confirmation-code.greeting": "Hi, {{.Name}}", + "mail.confirmation-code.body": "Use this code to complete your {{.Target}} request: {{.Code}}<\/b>. This code expires soon.", + "mail.email-verification.subj": "{{.ServiceName}}: verify your email address", "mail.email-verification.greeting": "Hi, {{.Name}}", "mail.email-verification.body": "It looks like you have changed your email address.

If that sounds right, verify your email address by clicking the following
link<\/a>.

If you have not changed your email address please contact us under
{{.SupportMail}}<\/a>.", diff --git a/api/notification/i18n/ru.json b/api/notification/i18n/ru.json index b7f28a6..0d42683 100644 --- a/api/notification/i18n/ru.json +++ b/api/notification/i18n/ru.json @@ -21,6 +21,10 @@ "mail.reset-password.greeting": "День добрый, {{.Name}}", "mail.reset-password.body": "От вашего имени пришел запрос на сброс пароля.

Если этот запрос отправили вы, то подтвердите сброс переходом по
ссылке<\/a>.

Если этот запрос отправили не вы, то дайте нам знать по адресу электронной почты
{{.SupportMail}}<\/a>", + "mail.confirmation-code.subj": "{{.ServiceName}}: ваш код подтверждения", + "mail.confirmation-code.greeting": "День добрый, {{.Name}}", + "mail.confirmation-code.body": "Используйте этот код, чтобы завершить запрос {{.Target}}: {{.Code}}<\/b>. Код скоро истекает.", + "mail.email-verification.subj": "{{.ServiceName}}: подтвердите ваш адрес электронной почты", "mail.email-verification.greeting": "День добрый, {{.Name}}", "mail.email-verification.body": "От вашего имени пришел запрос на смену адреса электронной почты.

Если этот запрос отправили вы, то подтвердите смену переходом по
ссылке <\/a>.

Если этот запрос отправили не вы, то дайте нам знать по адресу электронной почты
{{.SupportMail}}<\/a>", @@ -58,4 +62,4 @@ "___file_trailer": "" -} \ No newline at end of file +} diff --git a/api/notification/i18n/uk.json b/api/notification/i18n/uk.json index 8771068..e19651c 100644 --- a/api/notification/i18n/uk.json +++ b/api/notification/i18n/uk.json @@ -20,6 +20,10 @@ "mail.reset-password.greeting": "День добрий, {{.Name}}", "mail.reset-password.body": "Від вашого імені прийшов запит на скидання пароля.

Якщо цей запит відправили ви, то підтвердіть скидання переходом по
посилання<\/a>.
>
Якщо цей запит надіслали не ви, дайте нам знати за адресою електронної пошти
{{.SupportMail}}<\/a>", + "mail.confirmation-code.subj": "{{.ServiceName}}: ваш код підтвердження", + "mail.confirmation-code.greeting": "День добрий, {{.Name}}", + "mail.confirmation-code.body": "Використайте цей код, щоб завершити запит {{.Target}}: {{.Code}}<\/b>. Код скоро спливає.", + "mail.email-verification.subj": "{{.ServiceName}}: Перевірте свою адресу електронної пошти", "mail.email-verification.greeting": "День добрий, {{.Name}}", "mail.email-verification.body": "Від вашого імені надійшов запит на адресу електронної пошти.

Якщо цей запит надіслали, то підтвердьте зміну переходом за
адресою<\/a>.

Якщо цей запит відправили не ви, то дайте нам знати на адресу електронної пошти
{{.SupportMail}}<\/a>", diff --git a/api/notification/internal/server/notificationimp/confcode.go b/api/notification/internal/server/notificationimp/confcode.go new file mode 100644 index 0000000..bccf5ee --- /dev/null +++ b/api/notification/internal/server/notificationimp/confcode.go @@ -0,0 +1,27 @@ +package notificationimp + +import ( + "context" + "strings" + + "github.com/tech/sendico/pkg/model" + "go.uber.org/zap" +) + +func (a *NotificationAPI) onConfirmationCode(ctx context.Context, account *model.Account, destination string, target model.ConfirmationTarget, code string) error { + builder := a.client.MailBuilder(). + AddRecipient(account.Name, strings.TrimSpace(destination)). + SetAccountID(account.ID.Hex()). + SetLocale(account.Locale). + SetTemplateID("confirmation-code"). + AddData("Name", account.Name). + AddData("Code", code). + AddData("Target", string(target)) + + if err := a.client.Send(builder); err != nil { + a.logger.Warn("Failed to send confirmation code email", zap.Error(err), zap.String("login", account.Login)) + return err + } + a.logger.Info("Confirmation code email sent", zap.String("login", account.Login), zap.String("destination", destination), zap.String("target", string(target))) + return nil +} diff --git a/api/notification/internal/server/notificationimp/notification.go b/api/notification/internal/server/notificationimp/notification.go index 2805652..4f81929 100644 --- a/api/notification/internal/server/notificationimp/notification.go +++ b/api/notification/internal/server/notificationimp/notification.go @@ -9,6 +9,7 @@ import ( "github.com/tech/sendico/pkg/domainprovider" "github.com/tech/sendico/pkg/merrors" na "github.com/tech/sendico/pkg/messaging/notifications/account" + cnotifications "github.com/tech/sendico/pkg/messaging/notifications/confirmation" ni "github.com/tech/sendico/pkg/messaging/notifications/invitation" snotifications "github.com/tech/sendico/pkg/messaging/notifications/site" "github.com/tech/sendico/pkg/mlogger" @@ -70,6 +71,11 @@ func CreateAPI(a api.API) (*NotificationAPI, error) { return nil, err } + if err := a.Register().Consumer(cnotifications.NewConfirmationCodeProcessor(p.logger, db, p.onConfirmationCode)); err != nil { + p.logger.Error("Failed to create confirmation code handler", zap.Error(err)) + return nil, err + } + idb, err := a.DBFactory().NewInvitationsDB() if err != nil { p.logger.Error("Failed to create invitation db connection", zap.Error(err)) diff --git a/api/payments/orchestrator/go.mod b/api/payments/orchestrator/go.mod index 5973b59..f87cb3f 100644 --- a/api/payments/orchestrator/go.mod +++ b/api/payments/orchestrator/go.mod @@ -20,7 +20,7 @@ require ( github.com/tech/sendico/ledger v0.0.0-00010101000000-000000000000 github.com/tech/sendico/pkg v0.1.0 go.mongodb.org/mongo-driver v1.17.6 - go.uber.org/zap v1.27.0 + go.uber.org/zap v1.27.1 google.golang.org/grpc v1.77.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 @@ -46,7 +46,7 @@ require ( github.com/nats-io/nkeys v0.4.11 // indirect github.com/nats-io/nuid v1.0.1 // 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/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect @@ -54,7 +54,7 @@ require ( github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect - golang.org/x/crypto v0.44.0 // indirect + golang.org/x/crypto v0.45.0 // indirect golang.org/x/net v0.47.0 // indirect golang.org/x/sync v0.18.0 // indirect golang.org/x/sys v0.38.0 // indirect diff --git a/api/payments/orchestrator/go.sum b/api/payments/orchestrator/go.sum index 4babef9..44a7f0b 100644 --- a/api/payments/orchestrator/go.sum +++ b/api/payments/orchestrator/go.sum @@ -9,8 +9,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= @@ -117,10 +115,8 @@ 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.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= @@ -142,10 +138,10 @@ github.com/testcontainers/testcontainers-go v0.33.0 h1:zJS9PfXYT5O0ZFXM2xxXfk4J5 github.com/testcontainers/testcontainers-go v0.33.0/go.mod h1:W80YpTa8D5C3Yy16icheD01UTDu+LmXIA2Keo+jWtT8= github.com/testcontainers/testcontainers-go/modules/mongodb v0.33.0 h1:iXVA84s5hKMS5gn01GWOYHE3ymy/2b+0YkpFeTxB2XY= github.com/testcontainers/testcontainers-go/modules/mongodb v0.33.0/go.mod h1:R6tMjTojRiaoo89fh/hf7tOmfzohdqSU17R9DwSVSog= -github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= -github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= -github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= -github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= +github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= +github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= +github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= +github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= @@ -159,32 +155,32 @@ 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/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/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 v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +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.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.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-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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -221,8 +217,6 @@ 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/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= diff --git a/api/pkg/db/confirmation/confirmation.go b/api/pkg/db/confirmation/confirmation.go new file mode 100644 index 0000000..7f5d510 --- /dev/null +++ b/api/pkg/db/confirmation/confirmation.go @@ -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 +} diff --git a/api/pkg/db/factory.go b/api/pkg/db/factory.go index dc8c7e8..3c37aac 100644 --- a/api/pkg/db/factory.go +++ b/api/pkg/db/factory.go @@ -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) diff --git a/api/pkg/db/internal/mongo/confirmationdb/db.go b/api/pkg/db/internal/mongo/confirmationdb/db.go new file mode 100644 index 0000000..27cf571 --- /dev/null +++ b/api/pkg/db/internal/mongo/confirmationdb/db.go @@ -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 +} diff --git a/api/pkg/db/internal/mongo/confirmationdb/delete.go b/api/pkg/db/internal/mongo/confirmationdb/delete.go new file mode 100644 index 0000000..c174435 --- /dev/null +++ b/api/pkg/db/internal/mongo/confirmationdb/delete.go @@ -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) +} diff --git a/api/pkg/db/internal/mongo/confirmationdb/find.go b/api/pkg/db/internal/mongo/confirmationdb/find.go new file mode 100644 index 0000000..3be2f63 --- /dev/null +++ b/api/pkg/db/internal/mongo/confirmationdb/find.go @@ -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 +} diff --git a/api/pkg/db/internal/mongo/db.go b/api/pkg/db/internal/mongo/db.go index 6083f15..02cc9ff 100755 --- a/api/pkg/db/internal/mongo/db.go +++ b/api/pkg/db/internal/mongo/db.go @@ -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 { diff --git a/api/pkg/db/internal/mongo/repositoryimp/builderimp/pipeline_test.go b/api/pkg/db/internal/mongo/repositoryimp/builderimp/pipeline_test.go index 750b0e7..129f34a 100644 --- a/api/pkg/db/internal/mongo/repositoryimp/builderimp/pipeline_test.go +++ b/api/pkg/db/internal/mongo/repositoryimp/builderimp/pipeline_test.go @@ -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"}, }}} diff --git a/api/pkg/go.mod b/api/pkg/go.mod index e96d7fa..be42262 100644 --- a/api/pkg/go.mod +++ b/api/pkg/go.mod @@ -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 diff --git a/api/pkg/go.sum b/api/pkg/go.sum index d3150a6..6c0ebad 100644 --- a/api/pkg/go.sum +++ b/api/pkg/go.sum @@ -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= diff --git a/api/pkg/messaging/internal/notifications/confirmation/notification.go b/api/pkg/messaging/internal/notifications/confirmation/notification.go new file mode 100644 index 0000000..c45f5ae --- /dev/null +++ b/api/pkg/messaging/internal/notifications/confirmation/notification.go @@ -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, + }, + } +} diff --git a/api/pkg/messaging/internal/notifications/confirmation/processor.go b/api/pkg/messaging/internal/notifications/confirmation/processor.go new file mode 100644 index 0000000..c085712 --- /dev/null +++ b/api/pkg/messaging/internal/notifications/confirmation/processor.go @@ -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), + } +} diff --git a/api/pkg/messaging/notifications/confirmation/confirmation.go b/api/pkg/messaging/notifications/confirmation/confirmation.go new file mode 100644 index 0000000..23ec6db --- /dev/null +++ b/api/pkg/messaging/notifications/confirmation/confirmation.go @@ -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) +} diff --git a/api/pkg/messaging/notifications/confirmation/handler/interface.go b/api/pkg/messaging/notifications/confirmation/handler/interface.go new file mode 100644 index 0000000..a7c2477 --- /dev/null +++ b/api/pkg/messaging/notifications/confirmation/handler/interface.go @@ -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 diff --git a/api/pkg/model/confirmation.go b/api/pkg/model/confirmation.go new file mode 100644 index 0000000..1fdcfc6 --- /dev/null +++ b/api/pkg/model/confirmation.go @@ -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 +} diff --git a/api/pkg/mservice/services.go b/api/pkg/mservice/services.go index be4e30f..51b3b47 100644 --- a/api/pkg/mservice/services.go +++ b/api/pkg/mservice/services.go @@ -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) } } diff --git a/api/server/go.mod b/api/server/go.mod index 4618b51..27cea8c 100644 --- a/api/server/go.mod +++ b/api/server/go.mod @@ -7,10 +7,10 @@ replace github.com/tech/sendico/pkg => ../pkg replace github.com/tech/sendico/chain/gateway => ../chain/gateway require ( - github.com/aws/aws-sdk-go-v2 v1.39.6 - github.com/aws/aws-sdk-go-v2/config v1.31.20 - github.com/aws/aws-sdk-go-v2/credentials v1.18.24 - github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2 + github.com/aws/aws-sdk-go-v2 v1.40.0 + github.com/aws/aws-sdk-go-v2/config v1.32.0 + github.com/aws/aws-sdk-go-v2/credentials v1.19.0 + github.com/aws/aws-sdk-go-v2/service/s3 v1.92.0 github.com/go-chi/chi/v5 v5.2.3 github.com/go-chi/cors v1.2.2 github.com/go-chi/jwtauth/v5 v5.3.3 @@ -23,7 +23,7 @@ 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 + go.uber.org/zap v1.27.1 golang.org/x/net v0.47.0 gopkg.in/yaml.v3 v3.0.1 moul.io/chizap v1.0.3 @@ -40,18 +40,19 @@ require ( github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.14 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.3 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.40.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.5 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.14 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.4 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.8 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.1 // indirect github.com/aws/smithy-go v1.23.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/casbin/mongodb-adapter/v3 v3.7.0 // indirect @@ -104,7 +105,7 @@ require ( github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // 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/segmentio/asm v1.2.1 // indirect github.com/shirou/gopsutil/v3 v3.24.5 // indirect @@ -126,7 +127,7 @@ require ( go.opentelemetry.io/proto/otlp v1.7.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect - golang.org/x/crypto v0.44.0 // indirect + golang.org/x/crypto v0.45.0 // indirect golang.org/x/sync v0.18.0 // indirect golang.org/x/sys v0.38.0 // indirect golang.org/x/text v0.31.0 // indirect diff --git a/api/server/go.sum b/api/server/go.sum index 6c9922f..e2d491e 100644 --- a/api/server/go.sum +++ b/api/server/go.sum @@ -6,40 +6,42 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25 github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+Xsqk= -github.com/aws/aws-sdk-go-v2 v1.39.6/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE= +github.com/aws/aws-sdk-go-v2 v1.40.0 h1:/WMUA0kjhZExjOQN2z3oLALDREea1A7TobfuiBrKlwc= +github.com/aws/aws-sdk-go-v2 v1.40.0/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 h1:DHctwEM8P8iTXFxC/QK0MRjwEpWQeM9yzidCRjldUz0= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3/go.mod h1:xdCzcZEtnSTKVDOmUZs4l/j3pSV6rpo1WXl5ugNsL8Y= -github.com/aws/aws-sdk-go-v2/config v1.31.20 h1:/jWF4Wu90EhKCgjTdy1DGxcbcbNrjfBHvksEL79tfQc= -github.com/aws/aws-sdk-go-v2/config v1.31.20/go.mod h1:95Hh1Tc5VYKL9NJ7tAkDcqeKt+MCXQB1hQZaRdJIZE0= -github.com/aws/aws-sdk-go-v2/credentials v1.18.24 h1:iJ2FmPT35EaIB0+kMa6TnQ+PwG5A1prEdAw+PsMzfHg= -github.com/aws/aws-sdk-go-v2/credentials v1.18.24/go.mod h1:U91+DrfjAiXPDEGYhh/x29o4p0qHX5HDqG7y5VViv64= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 h1:T1brd5dR3/fzNFAQch/iBKeX07/ffu/cLu+q+RuzEWk= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13/go.mod h1:Peg/GBAQ6JDt+RoBf4meB1wylmAipb7Kg2ZFakZTlwk= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 h1:a+8/MLcWlIxo1lF9xaGt3J/u3yOZx+CdSveSNwjhD40= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13/go.mod h1:oGnKwIYZ4XttyU2JWxFrwvhF6YKiK/9/wmE3v3Iu9K8= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 h1:HBSI2kDkMdWz4ZM7FjwE7e/pWDEZ+nR95x8Ztet1ooY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13/go.mod h1:YE94ZoDArI7awZqJzBAZ3PDD2zSfuP7w6P2knOzIn8M= +github.com/aws/aws-sdk-go-v2/config v1.32.0 h1:T5WWJYnam9SzBLbsVYDu2HscLDe+GU1AUJtfcDAc/vA= +github.com/aws/aws-sdk-go-v2/config v1.32.0/go.mod h1:pSRm/+D3TxBixGMXlgtX4+MPO9VNtEEtiFmNpxksoxw= +github.com/aws/aws-sdk-go-v2/credentials v1.19.0 h1:7zm+ez+qEqLaNsCSRaistkvJRJv8sByDOVuCnyHbP7M= +github.com/aws/aws-sdk-go-v2/credentials v1.19.0/go.mod h1:pHKPblrT7hqFGkNLxqoS3FlGoPrQg4hMIa+4asZzBfs= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 h1:WZVR5DbDgxzA0BJeudId89Kmgy6DIU4ORpxwsVHz0qA= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14/go.mod h1:Dadl9QO0kHgbrH1GRqGiZdYtW5w+IXXaBNCHTIaheM4= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14 h1:PZHqQACxYb8mYgms4RZbhZG0a7dPW06xOjmaH0EJC/I= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14/go.mod h1:VymhrMJUWs69D8u0/lZ7jSB6WgaG/NqHi3gX0aYf6U0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14 h1:bOS19y6zlJwagBfHxs0ESzr1XCOU2KXJCWcq3E2vfjY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14/go.mod h1:1ipeGBMAxZ0xcTm6y6paC2C/J6f6OO7LBODV9afuAyM= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13 h1:eg/WYAa12vqTphzIdWMzqYRVKKnCboVPRlvaybNCqPA= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13/go.mod h1:/FDdxWhz1486obGrKKC1HONd7krpk38LBt+dutLcN9k= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.14 h1:ITi7qiDSv/mSGDSWNpZ4k4Ve0DQR6Ug2SJQ8zEHoDXg= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.14/go.mod h1:k1xtME53H1b6YpZt74YmwlONMWf4ecM+lut1WQLAF/U= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 h1:x2Ibm/Af8Fi+BH+Hsn9TXGdT+hKbDd5XOTZxTMxDk7o= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3/go.mod h1:IW1jwyrQgMdhisceG8fQLmQIydcT/jWY21rFhzgaKwo= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4 h1:NvMjwvv8hpGUILarKw7Z4Q0w1H9anXKsesMxtw++MA4= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4/go.mod h1:455WPHSwaGj2waRSpQp7TsnpOnBfw8iDfPfbwl7KPJE= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 h1:kDqdFvMY4AtKoACfzIGD8A0+hbT41KTKF//gq7jITfM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13/go.mod h1:lmKuogqSU3HzQCwZ9ZtcqOc5XGMqtDK7OIc2+DxiUEg= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 h1:zhBJXdhWIFZ1acfDYIhu4+LCzdUS2Vbcum7D01dXlHQ= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13/go.mod h1:JaaOeCE368qn2Hzi3sEzY6FgAZVCIYcC2nwbro2QCh8= -github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2 h1:DhdbtDl4FdNlj31+xiRXANxEE+eC7n8JQz+/ilwQ8Uc= -github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2/go.mod h1:+wArOOrcHUevqdto9k1tKOF5++YTe9JEcPSc9Tx2ZSw= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.3 h1:NjShtS1t8r5LUfFVtFeI8xLAHQNTa7UI0VawXlrBMFQ= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.3/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7 h1:gTsnx0xXNQ6SBbymoDvcoRHL+q4l/dAFsQuKfDWSaGc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo= -github.com/aws/aws-sdk-go-v2/service/sts v1.40.2 h1:HK5ON3KmQV2HcAunnx4sKLB9aPf3gKGwVAf7xnx0QT0= -github.com/aws/aws-sdk-go-v2/service/sts v1.40.2/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.5 h1:Hjkh7kE6D81PgrHlE/m9gx+4TyyeLHuY8xJs7yXN5C4= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.5/go.mod h1:nPRXgyCfAurhyaTMoBMwRBYBhaHI4lNPAnJmjM0Tslc= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14 h1:FIouAnCE46kyYqyhs0XEBDFFSREtdnr8HQuLPQPLCrY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14/go.mod h1:UTwDc5COa5+guonQU8qBikJo1ZJ4ln2r1MkF7Dqag1E= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.14 h1:FzQE21lNtUor0Fb7QNgnEyiRCBlolLTX/Z1j65S7teM= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.14/go.mod h1:s1ydyWG9pm3ZwmmYN21HKyG9WzAZhYVW85wMHs5FV6w= +github.com/aws/aws-sdk-go-v2/service/s3 v1.92.0 h1:8FshVvnV2sr9kOSAbOnc/vwVmmAwMjOedKH6JW2ddPM= +github.com/aws/aws-sdk-go-v2/service/s3 v1.92.0/go.mod h1:wYNqY3L02Z3IgRYxOBPH9I1zD9Cjh9hI5QOy/eOjQvw= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.1 h1:BDgIUYGEo5TkayOWv/oBLPphWwNm/A91AebUjAu5L5g= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.1/go.mod h1:iS6EPmNeqCsGo+xQmXv0jIMjyYtQfnwg36zl2FwEouk= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.4 h1:U//SlnkE1wOQiIImxzdY5PXat4Wq+8rlfVEw4Y7J8as= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.4/go.mod h1:av+ArJpoYf3pgyrj6tcehSFW+y9/QvAY8kMooR9bZCw= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.8 h1:MvlNs/f+9eM0mOjD9JzBUbf5jghyTk3p+O9yHMXX94Y= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.8/go.mod h1:/j67Z5XBVDx8nZVp9EuFM9/BS5dvBznbqILGuu73hug= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.1 h1:GdGmKtG+/Krag7VfyOXV17xjTCz0i9NT+JnqLTOI5nA= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.1/go.mod h1:6TxbXoDSgBQ225Qd8Q+MbxUxUh6TtNKwbRt/EPS9xso= github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM= github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -196,8 +198,8 @@ 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.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.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= @@ -278,16 +280,16 @@ go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95a 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.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= -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/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= diff --git a/api/server/interface/api/register.go b/api/server/interface/api/register.go index 4a46514..63683bd 100644 --- a/api/server/interface/api/register.go +++ b/api/server/interface/api/register.go @@ -11,6 +11,7 @@ import ( type Register interface { Handler(service mservice.Type, endpoint string, method api.HTTPMethod, handler sresponse.HandlerFunc) AccountHandler(service mservice.Type, endpoint string, method api.HTTPMethod, handler sresponse.AccountHandlerFunc) + PendingAccountHandler(service mservice.Type, endpoint string, method api.HTTPMethod, handler sresponse.PendingAccountHandlerFunc) WSHandler(messageType string, handler ws.HandlerFunc) Messaging() messaging.Register diff --git a/api/server/interface/api/sresponse/login_pending.go b/api/server/interface/api/sresponse/login_pending.go new file mode 100644 index 0000000..5ef2d4a --- /dev/null +++ b/api/server/interface/api/sresponse/login_pending.go @@ -0,0 +1,31 @@ +package sresponse + +import ( + "net/http" + + "github.com/tech/sendico/pkg/api/http/response" + "github.com/tech/sendico/pkg/mlogger" + "github.com/tech/sendico/pkg/model" +) + +type pendingLoginResponse struct { + Account accountResponse `json:"account"` + PendingToken TokenData `json:"pendingToken"` + Destination string `json:"destination"` + TTLSeconds int `json:"ttlSeconds"` +} + +func LoginPending(logger mlogger.Logger, account *model.Account, pendingToken *TokenData, destination string, ttlSeconds int) http.HandlerFunc { + return response.Accepted( + logger, + &pendingLoginResponse{ + Account: accountResponse{ + Account: *_createAccount(account, false), + authResponse: authResponse{}, + }, + PendingToken: *pendingToken, + Destination: destination, + TTLSeconds: ttlSeconds, + }, + ) +} diff --git a/api/server/interface/api/sresponse/response.go b/api/server/interface/api/sresponse/response.go index 04a6cf8..8bd7211 100644 --- a/api/server/interface/api/sresponse/response.go +++ b/api/server/interface/api/sresponse/response.go @@ -4,9 +4,11 @@ import ( "net/http" "github.com/tech/sendico/pkg/model" + emodel "github.com/tech/sendico/server/interface/model" ) type ( - HandlerFunc = func(r *http.Request) http.HandlerFunc - AccountHandlerFunc = func(r *http.Request, account *model.Account, accessToken *TokenData) http.HandlerFunc + HandlerFunc = func(r *http.Request) http.HandlerFunc + AccountHandlerFunc = func(r *http.Request, account *model.Account, accessToken *TokenData) http.HandlerFunc + PendingAccountHandlerFunc = func(r *http.Request, account *model.Account, token *emodel.AccountToken) http.HandlerFunc ) diff --git a/api/server/interface/model/token.go b/api/server/interface/model/token.go index 7f51ba6..9390ab6 100644 --- a/api/server/interface/model/token.go +++ b/api/server/interface/model/token.go @@ -17,6 +17,7 @@ type AccountToken struct { Name string Locale string Expiration time.Time + Pending bool } func createAccountToken(a *model.Account, expiration int) AccountToken { @@ -26,6 +27,7 @@ func createAccountToken(a *model.Account, expiration int) AccountToken { Name: a.Name, Locale: a.Locale, Expiration: time.Now().Add(mduration.Param2Duration(expiration, time.Hour)), + Pending: false, } } @@ -44,6 +46,7 @@ const ( paramNameLocale = "locale" paramNameLogin = "login" paramNameExpiration = "exp" + paramNamePending = "pending" ) func Claims2Token(claims middleware.MapClaims) (*AccountToken, error) { @@ -65,6 +68,11 @@ func Claims2Token(claims middleware.MapClaims) (*AccountToken, error) { if at.Locale, err = getTokenParam(claims, paramNameLocale); err != nil { return nil, err } + if pending, ok := claims[paramNamePending]; ok { + if pbool, ok := pending.(bool); ok { + at.Pending = pbool + } + } if expValue, ok := claims[paramNameExpiration]; ok { switch exp := expValue.(type) { case time.Time: @@ -90,5 +98,20 @@ func Account2Claims(a *model.Account, expiration int) middleware.MapClaims { paramNameName: t.Name, paramNameLocale: t.Locale, paramNameExpiration: int64(t.Expiration.Unix()), + paramNamePending: t.Pending, + } +} + +func PendingAccount2Claims(a *model.Account, expirationMinutes int) middleware.MapClaims { + t := createAccountToken(a, expirationMinutes/60) + t.Expiration = time.Now().Add(time.Duration(expirationMinutes) * time.Minute) + t.Pending = true + return middleware.MapClaims{ + paramNameID: t.AccountRef.Hex(), + paramNameLogin: t.Login, + paramNameName: t.Name, + paramNameLocale: t.Locale, + paramNameExpiration: t.Expiration.Unix(), + paramNamePending: t.Pending, } } diff --git a/api/server/interface/services/confirmation/confirmation.go b/api/server/interface/services/confirmation/confirmation.go new file mode 100644 index 0000000..39cc975 --- /dev/null +++ b/api/server/interface/services/confirmation/confirmation.go @@ -0,0 +1,11 @@ +package confirmation + +import ( + "github.com/tech/sendico/pkg/mservice" + "github.com/tech/sendico/server/interface/api" + "github.com/tech/sendico/server/internal/server/confirmationimp" +) + +func Create(a api.API) (mservice.MicroService, error) { + return confirmationimp.CreateAPI(a) +} diff --git a/api/server/internal/api/api.go b/api/server/internal/api/api.go index 822e514..d37506f 100644 --- a/api/server/internal/api/api.go +++ b/api/server/internal/api/api.go @@ -12,6 +12,7 @@ import ( "github.com/tech/sendico/pkg/mservice" "github.com/tech/sendico/server/interface/api" "github.com/tech/sendico/server/interface/services/account" + "github.com/tech/sendico/server/interface/services/confirmation" "github.com/tech/sendico/server/interface/services/invitation" "github.com/tech/sendico/server/interface/services/logo" "github.com/tech/sendico/server/interface/services/organization" @@ -76,6 +77,7 @@ func (a *APIImp) installServices() error { srvf := make([]api.MicroServiceFactoryT, 0) srvf = append(srvf, account.Create) + srvf = append(srvf, confirmation.Create) srvf = append(srvf, organization.Create) srvf = append(srvf, invitation.Create) srvf = append(srvf, logo.Create) diff --git a/api/server/internal/api/middleware.go b/api/server/internal/api/middleware.go index 800c800..c024582 100644 --- a/api/server/internal/api/middleware.go +++ b/api/server/internal/api/middleware.go @@ -45,6 +45,10 @@ func (mw *Middleware) AccountHandler(service mservice.Type, endpoint string, met mw.epdispatcher.AccountHandler(service, endpoint, method, handler) } +func (mw *Middleware) PendingAccountHandler(service mservice.Type, endpoint string, method api.HTTPMethod, handler sresponse.PendingAccountHandlerFunc) { + mw.epdispatcher.PendingAccountHandler(service, endpoint, method, handler) +} + func (mw *Middleware) WSHandler(messageType string, handler wsh.HandlerFunc) { mw.wshandler.InstallHandler(messageType, handler) } @@ -133,7 +137,13 @@ func CreateMiddleware(logger mlogger.Logger, db db.Factory, enforcer auth.Enforc return nil, err } - p.epdispatcher = routers.NewDispatcher(p.logger, p.router, adb, rtdb, enforcer, config) + cdb, err := db.NewConfirmationsDB() + if err != nil { + p.logger.Error("Failed to create confirmations database", zap.Error(err)) + return nil, err + } + + p.epdispatcher = routers.NewDispatcher(p.logger, p.router, adb, cdb, rtdb, enforcer, config) p.wshandler = ws.NewRouter(p.logger, p.router, &config.WebSocket, p.apiEndpoint) return p, nil } diff --git a/api/server/internal/api/routers/authorized/handler.go b/api/server/internal/api/routers/authorized/handler.go index b0a3580..29118ce 100644 --- a/api/server/internal/api/routers/authorized/handler.go +++ b/api/server/internal/api/routers/authorized/handler.go @@ -37,6 +37,9 @@ func (ar *AuthorizedRouter) tokenHandler(service mservice.Type, endpoint string, func (ar *AuthorizedRouter) AccountHandler(service mservice.Type, endpoint string, method api.HTTPMethod, handler sresponse.AccountHandlerFunc) { hndlr := func(r *http.Request, t *emodel.AccountToken) http.HandlerFunc { + if t.Pending { + return response.Forbidden(ar.logger, ar.service, "confirmation_required", "pending token requires confirmation") + } var a model.Account if err := ar.db.Get(r.Context(), t.AccountRef, &a); err != nil { if errors.Is(err, merrors.ErrNoData) { @@ -54,3 +57,18 @@ func (ar *AuthorizedRouter) AccountHandler(service mservice.Type, endpoint strin } ar.tokenHandler(service, endpoint, method, hndlr) } + +func (ar *AuthorizedRouter) PendingAccountHandler(service mservice.Type, endpoint string, method api.HTTPMethod, handler sresponse.PendingAccountHandlerFunc) { + hndlr := func(r *http.Request, t *emodel.AccountToken) http.HandlerFunc { + var a model.Account + if err := ar.db.Get(r.Context(), t.AccountRef, &a); err != nil { + if errors.Is(err, merrors.ErrNoData) { + ar.logger.Debug("Failed to find related user", zap.Error(err), mzap.ObjRef("account_ref", t.AccountRef)) + return response.NotFound(ar.logger, ar.service, err.Error()) + } + return response.Internal(ar.logger, ar.service, err) + } + return handler(r, &a, t) + } + ar.tokenHandler(service, endpoint, method, hndlr) +} diff --git a/api/server/internal/api/routers/dispatcher.go b/api/server/internal/api/routers/dispatcher.go index 6f6ebb0..3ec2929 100644 --- a/api/server/internal/api/routers/dispatcher.go +++ b/api/server/internal/api/routers/dispatcher.go @@ -8,6 +8,7 @@ import ( api "github.com/tech/sendico/pkg/api/http" "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/refreshtokens" "github.com/tech/sendico/pkg/mlogger" "github.com/tech/sendico/pkg/mservice" @@ -31,7 +32,11 @@ func (d *Dispatcher) AccountHandler(service mservice.Type, endpoint string, meth d.protected.AccountHandler(service, endpoint, method, handler) } -func NewDispatcher(logger mlogger.Logger, router chi.Router, db account.DB, rtdb refreshtokens.DB, enforcer auth.Enforcer, config *middleware.Config) *Dispatcher { +func (d *Dispatcher) PendingAccountHandler(service mservice.Type, endpoint string, method api.HTTPMethod, handler sresponse.PendingAccountHandlerFunc) { + d.protected.PendingAccountHandler(service, endpoint, method, handler) +} + +func NewDispatcher(logger mlogger.Logger, router chi.Router, db account.DB, cdb confirmation.DB, rtdb refreshtokens.DB, enforcer auth.Enforcer, config *middleware.Config) *Dispatcher { d := &Dispatcher{ logger: logger.Named("api_dispatcher"), } @@ -40,7 +45,7 @@ func NewDispatcher(logger mlogger.Logger, router chi.Router, db account.DB, rtdb endpoint := os.Getenv(config.EndPointEnv) signature := middleware.SignatureConf(config) router.Group(func(r chi.Router) { - d.public = rpublic.NewRouter(d.logger, endpoint, db, rtdb, r, &config.Token, &signature) + d.public = rpublic.NewRouter(d.logger, endpoint, db, cdb, rtdb, r, &config.Token, &signature) }) router.Group(func(r chi.Router) { d.protected = rauthorized.NewRouter(d.logger, endpoint, r, db, enforcer, &config.Token, &signature) diff --git a/api/server/internal/api/routers/endpoint/token.go b/api/server/internal/api/routers/endpoint/token.go index bb01eb2..be85a2a 100644 --- a/api/server/internal/api/routers/endpoint/token.go +++ b/api/server/internal/api/routers/endpoint/token.go @@ -18,3 +18,13 @@ func (er *HttpEndpointRouter) CreateAccessToken(user *model.Account) (sresponse. } return token, err } + +func (er *HttpEndpointRouter) CreatePendingToken(user *model.Account, ttlMinutes int) (sresponse.TokenData, error) { + ja := jwtauth.New(er.signature.Algorithm, er.signature.PrivateKey, er.signature.PublicKey) + _, res, err := ja.Encode(emodel.PendingAccount2Claims(user, ttlMinutes)) + token := sresponse.TokenData{ + Token: res, + Expiration: time.Now().Add(time.Duration(ttlMinutes) * time.Minute), + } + return token, err +} diff --git a/api/server/internal/api/routers/public/login.go b/api/server/internal/api/routers/public/login.go index b668b9e..c8c90c1 100644 --- a/api/server/internal/api/routers/public/login.go +++ b/api/server/internal/api/routers/public/login.go @@ -6,14 +6,20 @@ import ( "errors" "net/http" "strings" + "time" "github.com/tech/sendico/pkg/api/http/response" "github.com/tech/sendico/pkg/merrors" + "github.com/tech/sendico/pkg/model" "github.com/tech/sendico/pkg/mservice" "github.com/tech/sendico/server/interface/api/srequest" + "github.com/tech/sendico/server/interface/api/sresponse" + "github.com/tech/sendico/server/internal/server/confirmationimp" "go.uber.org/zap" ) +const pendingLoginTTLMinutes = 10 + func (pr *PublicRouter) logUserIn(ctx context.Context, r *http.Request, req *srequest.Login) http.HandlerFunc { // Get the account database entry trimmedLogin := strings.TrimSpace(req.Login) @@ -35,13 +41,23 @@ func (pr *PublicRouter) logUserIn(ctx context.Context, r *http.Request, req *sre return response.Unauthorized(pr.logger, pr.service, "password does not match") } - accessToken, err := pr.imp.CreateAccessToken(account) + pendingToken, err := pr.imp.CreatePendingToken(account, pendingLoginTTLMinutes) if err != nil { - pr.logger.Warn("Failed to generate access token", zap.Error(err)) + pr.logger.Warn("Failed to generate pending token", zap.Error(err)) return response.Internal(pr.logger, pr.service, err) } - return pr.refreshAndRespondLogin(ctx, r, &req.SessionIdentifier, account, &accessToken) + cfg := confirmationimp.DefaultConfig() + _, rec, err := pr.cstore.Create(ctx, account.ID, account.Login, model.ConfirmationTargetLogin, cfg, pr.generateCode) + if err != nil { + pr.logger.Warn("Failed to create login confirmation code", zap.Error(err)) + return response.Internal(pr.logger, pr.service, err) + } + pr.logger.Info("Login confirmation code issued", + zap.String("destination", pr.maskEmail(account.Login)), + zap.String("account", account.Login)) + + return sresponse.LoginPending(pr.logger, account, &pendingToken, pr.maskEmail(account.Login), int(time.Until(rec.ExpiresAt).Seconds())) } func (a *PublicRouter) login(r *http.Request) http.HandlerFunc { diff --git a/api/server/internal/api/routers/public/respond.go b/api/server/internal/api/routers/public/respond.go index 002c51f..71dff09 100644 --- a/api/server/internal/api/routers/public/respond.go +++ b/api/server/internal/api/routers/public/respond.go @@ -2,59 +2,16 @@ package routers import ( "context" - "crypto/rand" - "encoding/base64" - "io" "net/http" - "time" "github.com/tech/sendico/pkg/api/http/response" - "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/model" "github.com/tech/sendico/pkg/mutil/mzap" "github.com/tech/sendico/server/interface/api/sresponse" + rtokens "github.com/tech/sendico/server/internal/api/routers/tokens" "go.uber.org/zap" ) -func generateRefreshTokenData(length int) (string, error) { - randomBytes := make([]byte, length) - if _, err := io.ReadFull(rand.Reader, randomBytes); err != nil { - return "", merrors.Internal("failed to generate secure random bytes: " + err.Error()) - } - - return base64.URLEncoding.EncodeToString(randomBytes), nil -} - -func (er *PublicRouter) prepareRefreshToken(ctx context.Context, r *http.Request, session *model.SessionIdentifier, account *model.Account) (*model.RefreshToken, error) { - refreshToken, err := generateRefreshTokenData(er.config.Length) - if err != nil { - er.logger.Warn("Failed to generate refresh token", zap.Error(err), mzap.StorableRef(account)) - return nil, err - } - - token := &model.RefreshToken{ - AccountBoundBase: model.AccountBoundBase{ - AccountRef: account.GetID(), - }, - ClientRefreshToken: model.ClientRefreshToken{ - SessionIdentifier: *session, - RefreshToken: refreshToken, - }, - ExpiresAt: time.Now().Add(time.Duration(er.config.Expiration.Refresh) * time.Hour), - IsRevoked: false, - UserAgent: r.UserAgent(), - IPAddress: r.RemoteAddr, - } - - if err = er.rtdb.Create(ctx, token); err != nil { - er.logger.Warn("Failed to store a refresh token", zap.Error(err), mzap.StorableRef(account), - zap.String("client_id", token.ClientID), zap.String("device_id", token.DeviceID)) - return nil, err - } - - return token, nil -} - func (pr *PublicRouter) refreshAndRespondLogin( ctx context.Context, r *http.Request, @@ -62,7 +19,16 @@ func (pr *PublicRouter) refreshAndRespondLogin( account *model.Account, accessToken *sresponse.TokenData, ) http.HandlerFunc { - refreshToken, err := pr.prepareRefreshToken(ctx, r, session, account) + refreshToken, err := rtokens.PrepareRefreshToken( + ctx, + r, + pr.rtdb, + pr.config.Length, + pr.config.Expiration.Refresh, + session, + account, + pr.logger, + ) if err != nil { pr.logger.Warn("Failed to create refresh token", zap.Error(err), mzap.StorableRef(account), zap.String("client_id", session.ClientID), zap.String("device_id", session.DeviceID)) diff --git a/api/server/internal/api/routers/public/router.go b/api/server/internal/api/routers/public/router.go index a2fcae3..2d5ab0f 100644 --- a/api/server/internal/api/routers/public/router.go +++ b/api/server/internal/api/routers/public/router.go @@ -1,20 +1,27 @@ package routers import ( + "crypto/rand" + "strings" + "github.com/go-chi/chi/v5" api "github.com/tech/sendico/pkg/api/http" "github.com/tech/sendico/pkg/db/account" + "github.com/tech/sendico/pkg/db/confirmation" "github.com/tech/sendico/pkg/db/refreshtokens" "github.com/tech/sendico/pkg/mlogger" "github.com/tech/sendico/pkg/mservice" "github.com/tech/sendico/server/interface/api/sresponse" "github.com/tech/sendico/server/interface/middleware" re "github.com/tech/sendico/server/internal/api/routers/endpoint" + "github.com/tech/sendico/server/internal/server/confirmationimp" ) type PublicRouter struct { logger mlogger.Logger db account.DB + cdb confirmation.DB + cstore *confirmationimp.ConfirmationStore imp *re.HttpEndpointRouter rtdb refreshtokens.DB config middleware.TokenConfig @@ -26,11 +33,39 @@ func (pr *PublicRouter) InstallHandler(service mservice.Type, endpoint string, m pr.imp.InstallHandler(service, endpoint, method, handler) } -func NewRouter(logger mlogger.Logger, apiEndpoint string, db account.DB, rtdb refreshtokens.DB, router chi.Router, config *middleware.TokenConfig, signature *middleware.Signature) *PublicRouter { +func (pr *PublicRouter) generateCode() (string, error) { + const digits = "0123456789" + b := make([]byte, confirmationimp.DefaultConfig().CodeLength) + if _, err := rand.Read(b); err != nil { + return "", err + } + for i := range b { + b[i] = digits[int(b[i])%len(digits)] + } + return string(b), nil +} + +func (pr *PublicRouter) maskEmail(email string) string { + parts := strings.Split(email, "@") + if len(parts) != 2 { + return email + } + local := parts[0] + if len(local) > 2 { + local = local[:1] + "***" + local[len(local)-1:] + } else { + local = local[:1] + "***" + } + return local + "@" + parts[1] +} + +func NewRouter(logger mlogger.Logger, apiEndpoint string, db account.DB, cdb confirmation.DB, rtdb refreshtokens.DB, router chi.Router, config *middleware.TokenConfig, signature *middleware.Signature) *PublicRouter { l := logger.Named("public") hr := PublicRouter{ logger: l, db: db, + cdb: cdb, + cstore: confirmationimp.NewStore(cdb), rtdb: rtdb, config: *config, signature: *signature, diff --git a/api/server/internal/api/routers/router.go b/api/server/internal/api/routers/router.go index ef95937..90ea96f 100644 --- a/api/server/internal/api/routers/router.go +++ b/api/server/internal/api/routers/router.go @@ -12,4 +12,5 @@ type APIRouter interface { type ProtectedAPIRouter interface { AccountHandler(service mservice.Type, endpoint string, method api.HTTPMethod, handler sresponse.AccountHandlerFunc) + PendingAccountHandler(service mservice.Type, endpoint string, method api.HTTPMethod, handler sresponse.PendingAccountHandlerFunc) } diff --git a/api/server/internal/api/routers/tokens/tokens.go b/api/server/internal/api/routers/tokens/tokens.go new file mode 100644 index 0000000..e768532 --- /dev/null +++ b/api/server/internal/api/routers/tokens/tokens.go @@ -0,0 +1,65 @@ +package tokens + +import ( + "context" + "crypto/rand" + "encoding/base64" + "io" + "net/http" + "time" + + "github.com/tech/sendico/pkg/db/refreshtokens" + "github.com/tech/sendico/pkg/merrors" + "github.com/tech/sendico/pkg/mlogger" + "github.com/tech/sendico/pkg/model" + "github.com/tech/sendico/pkg/mutil/mzap" + "go.uber.org/zap" +) + +func generateRefreshTokenData(length int) (string, error) { + randomBytes := make([]byte, length) + if _, err := io.ReadFull(rand.Reader, randomBytes); err != nil { + return "", merrors.Internal("failed to generate secure random bytes: " + err.Error()) + } + + return base64.URLEncoding.EncodeToString(randomBytes), nil +} + +func PrepareRefreshToken( + ctx context.Context, + r *http.Request, + rtdb refreshtokens.DB, + length int, + refreshExpiration int, + session *model.SessionIdentifier, + account *model.Account, + logger mlogger.Logger, +) (*model.RefreshToken, error) { + refreshToken, err := generateRefreshTokenData(length) + if err != nil { + logger.Warn("Failed to generate refresh token", zap.Error(err), mzap.StorableRef(account)) + return nil, err + } + + token := &model.RefreshToken{ + AccountBoundBase: model.AccountBoundBase{ + AccountRef: account.GetID(), + }, + ClientRefreshToken: model.ClientRefreshToken{ + SessionIdentifier: *session, + RefreshToken: refreshToken, + }, + ExpiresAt: time.Now().Add(time.Duration(refreshExpiration) * time.Hour), + IsRevoked: false, + UserAgent: r.UserAgent(), + IPAddress: r.RemoteAddr, + } + + if err = rtdb.Create(ctx, token); err != nil { + logger.Warn("Failed to store a refresh token", zap.Error(err), mzap.StorableRef(account), + zap.String("client_id", token.ClientID), zap.String("device_id", token.DeviceID)) + return nil, err + } + + return token, nil +} diff --git a/api/server/internal/server/confirmationimp/request.go b/api/server/internal/server/confirmationimp/request.go new file mode 100644 index 0000000..68e61f3 --- /dev/null +++ b/api/server/internal/server/confirmationimp/request.go @@ -0,0 +1,48 @@ +package confirmationimp + +import ( + "encoding/json" + "net/http" + "time" + + "github.com/tech/sendico/pkg/api/http/response" + "github.com/tech/sendico/pkg/model" + "github.com/tech/sendico/pkg/mutil/mzap" + emodel "github.com/tech/sendico/server/interface/model" + "go.uber.org/zap" +) + +func (a *ConfirmationAPI) requestCode(r *http.Request, account *model.Account, token *emodel.AccountToken) http.HandlerFunc { + var req confirmationRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + a.logger.Warn("Failed to decode confirmation request", zap.Error(err)) + return response.BadPayload(a.logger, a.Name(), err) + } + + target, err := a.parseTarget(req.Target) + if err != nil { + return response.BadRequest(a.logger, a.Name(), "invalid_target", err.Error()) + } + + if target == model.ConfirmationTargetLogin && (token == nil || !token.Pending) { + return response.Forbidden(a.logger, a.Name(), "pending_token_required", "login confirmation requires pending token") + } + + destination := a.resolveDestination(req.Destination, account) + if destination == "" { + return response.BadRequest(a.logger, a.Name(), "missing_destination", "email destination is required") + } + code, rec, err := a.store.Create(r.Context(), account.ID, destination, target, a.config, a.generateCode) + if err != nil { + a.logger.Warn("Failed to create confirmation code", zap.Error(err), mzap.ObjRef("account_ref", account.ID)) + return response.Internal(a.logger, a.Name(), err) + } + + a.sendCode(account, target, destination, code) + + return response.Accepted(a.logger, confirmationResponse{ + TTLSeconds: int(time.Until(rec.ExpiresAt).Seconds()), + CooldownSeconds: int(a.config.Cooldown.Seconds()), + Destination: maskEmail(destination), + }) +} diff --git a/api/server/internal/server/confirmationimp/resend.go b/api/server/internal/server/confirmationimp/resend.go new file mode 100644 index 0000000..4e7e1d2 --- /dev/null +++ b/api/server/internal/server/confirmationimp/resend.go @@ -0,0 +1,56 @@ +package confirmationimp + +import ( + "encoding/json" + "errors" + "net/http" + "time" + + "github.com/tech/sendico/pkg/api/http/response" + "github.com/tech/sendico/pkg/model" + "github.com/tech/sendico/pkg/mutil/mzap" + emodel "github.com/tech/sendico/server/interface/model" + "go.uber.org/zap" +) + +func (a *ConfirmationAPI) resendCode(r *http.Request, account *model.Account, token *emodel.AccountToken) http.HandlerFunc { + var req confirmationRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + a.logger.Warn("Failed to decode confirmation resend request", zap.Error(err)) + return response.BadPayload(a.logger, a.Name(), err) + } + + target, err := a.parseTarget(req.Target) + if err != nil { + return response.BadRequest(a.logger, a.Name(), "invalid_target", err.Error()) + } + + if target == model.ConfirmationTargetLogin && (token == nil || !token.Pending) { + return response.Forbidden(a.logger, a.Name(), "pending_token_required", "login confirmation requires pending token") + } + + destination := a.resolveDestination(req.Destination, account) + if destination == "" { + return response.BadRequest(a.logger, a.Name(), "missing_destination", "email destination is required") + } + code, rec, err := a.store.Resend(r.Context(), account.ID, destination, target, a.config, a.generateCode) + switch { + case errors.Is(err, errConfirmationNotFound): + return response.NotFound(a.logger, a.Name(), "no_active_code_for_resend") + case errors.Is(err, errConfirmationCooldown): + return response.Forbidden(a.logger, a.Name(), "cooldown_active", "please wait before requesting another code") + case errors.Is(err, errConfirmationResendLimit): + return response.Forbidden(a.logger, a.Name(), "resend_limit_reached", "too many resend attempts") + case err != nil: + a.logger.Warn("Failed to resend confirmation code", zap.Error(err), mzap.ObjRef("account_ref", account.ID)) + return response.Internal(a.logger, a.Name(), err) + } + + a.sendCode(account, target, destination, code) + + return response.Accepted(a.logger, confirmationResponse{ + TTLSeconds: int(time.Until(rec.ExpiresAt).Seconds()), + CooldownSeconds: int(a.config.Cooldown.Seconds()), + Destination: maskEmail(destination), + }) +} diff --git a/api/server/internal/server/confirmationimp/service.go b/api/server/internal/server/confirmationimp/service.go new file mode 100644 index 0000000..737e8c8 --- /dev/null +++ b/api/server/internal/server/confirmationimp/service.go @@ -0,0 +1,158 @@ +package confirmationimp + +import ( + "context" + "crypto/rand" + "fmt" + "strings" + "time" + + "github.com/go-chi/jwtauth/v5" + api "github.com/tech/sendico/pkg/api/http" + "github.com/tech/sendico/pkg/db/refreshtokens" + "github.com/tech/sendico/pkg/merrors" + "github.com/tech/sendico/pkg/messaging" + cnotifications "github.com/tech/sendico/pkg/messaging/notifications/confirmation" + "github.com/tech/sendico/pkg/mlogger" + "github.com/tech/sendico/pkg/model" + "github.com/tech/sendico/pkg/mservice" + "github.com/tech/sendico/pkg/mutil/mzap" + eapi "github.com/tech/sendico/server/interface/api" + "github.com/tech/sendico/server/interface/api/sresponse" + "github.com/tech/sendico/server/interface/middleware" + emodel "github.com/tech/sendico/server/interface/model" + "go.uber.org/zap" +) + +type Config struct { + CodeLength int + TTL time.Duration + MaxAttempts int + Cooldown time.Duration + ResendLimit int +} + +func defaultConfig() Config { + return Config{ + CodeLength: 6, + TTL: 10 * time.Minute, + MaxAttempts: 5, + Cooldown: time.Minute, + ResendLimit: 5, + } +} + +func DefaultConfig() Config { + return defaultConfig() +} + +type ConfirmationAPI struct { + logger mlogger.Logger + config Config + store *ConfirmationStore + rtdb refreshtokens.DB + producer messaging.Producer + tokenConfig middleware.TokenConfig + signature middleware.Signature +} + +func (a *ConfirmationAPI) Name() mservice.Type { + return mservice.Confirmations +} + +func (a *ConfirmationAPI) Finish(_ context.Context) error { + return nil +} + +func CreateAPI(a eapi.API) (*ConfirmationAPI, error) { + cdb, err := a.DBFactory().NewConfirmationsDB() + if err != nil { + return nil, err + } + rtdb, err := a.DBFactory().NewRefreshTokensDB() + if err != nil { + return nil, err + } + + p := &ConfirmationAPI{ + logger: a.Logger().Named(mservice.Confirmations), + config: defaultConfig(), + store: NewStore(cdb), + rtdb: rtdb, + producer: a.Register().Messaging().Producer(), + tokenConfig: a.Config().Mw.Token, + signature: middleware.SignatureConf(a.Config().Mw), + } + + a.Register().PendingAccountHandler(p.Name(), "/confirmations", api.Post, p.requestCode) + a.Register().PendingAccountHandler(p.Name(), "/confirmations/resend", api.Post, p.resendCode) + a.Register().PendingAccountHandler(p.Name(), "/confirmations/verify", api.Post, p.verifyCode) + return p, nil +} + +func (a *ConfirmationAPI) generateCode() (string, error) { + const digits = "0123456789" + b := make([]byte, a.config.CodeLength) + _, err := rand.Read(b) + if err != nil { + return "", err + } + for i := range b { + b[i] = digits[int(b[i])%len(digits)] + } + return string(b), nil +} + +func (a *ConfirmationAPI) parseTarget(raw string) (model.ConfirmationTarget, error) { + switch strings.ToLower(strings.TrimSpace(raw)) { + case string(model.ConfirmationTargetLogin): + return model.ConfirmationTargetLogin, nil + case string(model.ConfirmationTargetPayout): + return model.ConfirmationTargetPayout, nil + default: + return "", merrors.InvalidArgument(fmt.Sprintf("unsupported target '%s'", raw), "target") + } +} + +func (a *ConfirmationAPI) resolveDestination(reqDest string, account *model.Account) string { + destination := strings.ToLower(strings.TrimSpace(reqDest)) + if destination == "" && account != nil { + destination = strings.ToLower(strings.TrimSpace(account.Login)) + } + return destination +} + +func (a *ConfirmationAPI) sendCode(account *model.Account, target model.ConfirmationTarget, destination, code string) { + a.logger.Info("Confirmation code generated", + zap.String("target", string(target)), + zap.String("destination", maskEmail(destination)), + mzap.ObjRef("account_ref", account.ID)) + if err := a.producer.SendMessage(cnotifications.Code(a.Name(), account.ID, destination, target, code)); err != nil { + a.logger.Warn("Failed to send confirmation code notification", zap.Error(err), mzap.ObjRef("account_ref", account.ID)) + } + a.logger.Debug("Confirmation code debug dump (do not log in production)", zap.String("code", code)) +} + +func maskEmail(email string) string { + parts := strings.Split(email, "@") + if len(parts) != 2 { + return email + } + local := parts[0] + if len(local) > 2 { + local = local[:1] + "***" + local[len(local)-1:] + } else { + local = local[:1] + "***" + } + return local + "@" + parts[1] +} + +func (a *ConfirmationAPI) createAccessToken(account *model.Account) (sresponse.TokenData, error) { + ja := jwtauth.New(a.signature.Algorithm, a.signature.PrivateKey, a.signature.PublicKey) + _, res, err := ja.Encode(emodel.Account2Claims(account, a.tokenConfig.Expiration.Account)) + token := sresponse.TokenData{ + Token: res, + Expiration: time.Now().Add(time.Duration(a.tokenConfig.Expiration.Account) * time.Hour), + } + return token, err +} diff --git a/api/server/internal/server/confirmationimp/store.go b/api/server/internal/server/confirmationimp/store.go new file mode 100644 index 0000000..a3e83fe --- /dev/null +++ b/api/server/internal/server/confirmationimp/store.go @@ -0,0 +1,181 @@ +package confirmationimp + +import ( + "context" + "crypto/rand" + "crypto/sha256" + "crypto/subtle" + "errors" + "time" + + "github.com/tech/sendico/pkg/db/confirmation" + "github.com/tech/sendico/pkg/merrors" + "github.com/tech/sendico/pkg/model" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +var ( + errConfirmationNotFound = errors.New("confirmation not found or expired") + errConfirmationUsed = errors.New("confirmation already used") + errConfirmationMismatch = errors.New("confirmation code mismatch") + errConfirmationAttemptsExceeded = errors.New("confirmation attempts exceeded") + errConfirmationCooldown = errors.New("confirmation cooldown active") + errConfirmationResendLimit = errors.New("confirmation resend limit reached") +) + +type ConfirmationStore struct { + db confirmation.DB +} + +func NewStore(db confirmation.DB) *ConfirmationStore { + return &ConfirmationStore{db: db} +} + +func (s *ConfirmationStore) Create( + ctx context.Context, + accountRef primitive.ObjectID, + destination string, + target model.ConfirmationTarget, + cfg Config, + generator func() (string, error), +) (string, *model.ConfirmationCode, error) { + if err := s.db.DeleteTuple(ctx, accountRef, destination, target); err != nil && !errors.Is(err, merrors.ErrNoData) { + return "", nil, err + } + + code, _, rec, err := s.buildRecord(accountRef, destination, target, cfg, generator) + if err != nil { + return "", nil, err + } + + if err := s.db.Create(ctx, rec); err != nil { + return "", nil, err + } + + return code, rec, nil +} + +func (s *ConfirmationStore) Resend( + ctx context.Context, + accountRef primitive.ObjectID, + destination string, + target model.ConfirmationTarget, + cfg Config, + generator func() (string, error), +) (string, *model.ConfirmationCode, error) { + now := time.Now().UTC() + active, err := s.db.FindActive(ctx, accountRef, destination, target, now.Unix()) + if errors.Is(err, merrors.ErrNoData) { + return s.Create(ctx, accountRef, destination, target, cfg, generator) + } + if err != nil { + return "", nil, err + } + if active.ResendCount >= active.ResendLimit { + return "", nil, errConfirmationResendLimit + } + if now.Before(active.CooldownUntil) { + return "", nil, errConfirmationCooldown + } + + code, salt, updated, err := s.buildRecord(accountRef, destination, target, cfg, generator) + if err != nil { + return "", nil, err + } + // Preserve attempt counters but bump resend count. + updated.ID = active.ID + updated.CreatedAt = active.CreatedAt + updated.Attempts = active.Attempts + updated.ResendCount = active.ResendCount + 1 + updated.Salt = salt + + if err := s.db.Update(ctx, updated); err != nil { + return "", nil, err + } + return code, updated, nil +} + +func (s *ConfirmationStore) Verify( + ctx context.Context, + accountRef primitive.ObjectID, + destination string, + target model.ConfirmationTarget, + code string, +) error { + now := time.Now().UTC() + rec, err := s.db.FindActive(ctx, accountRef, destination, target, now.Unix()) + if errors.Is(err, merrors.ErrNoData) { + return errConfirmationNotFound + } + if err != nil { + return err + } + if rec.Used { + return errConfirmationUsed + } + + rec.Attempts++ + if rec.Attempts > rec.MaxAttempts { + rec.Used = true + _ = s.db.Update(ctx, rec) + return errConfirmationAttemptsExceeded + } + + if subtle.ConstantTimeCompare(rec.CodeHash, hashCode(rec.Salt, code)) != 1 { + _ = s.db.Update(ctx, rec) + return errConfirmationMismatch + } + + rec.Used = true + return s.db.Update(ctx, rec) +} + +func (s *ConfirmationStore) buildRecord( + accountRef primitive.ObjectID, + destination string, + target model.ConfirmationTarget, + cfg Config, + generator func() (string, error), +) (string, []byte, *model.ConfirmationCode, error) { + code, err := generator() + if err != nil { + return "", nil, nil, err + } + salt, err := newSalt() + if err != nil { + return "", nil, nil, err + } + + now := time.Now().UTC() + rec := model.NewConfirmationCode(accountRef) + rec.Destination = destination + rec.Target = target + rec.CodeHash = hashCode(salt, code) + rec.Salt = salt + rec.ExpiresAt = now.Add(cfg.TTL) + rec.MaxAttempts = cfg.MaxAttempts + rec.ResendLimit = cfg.ResendLimit + rec.CooldownUntil = now.Add(cfg.Cooldown) + rec.Used = false + rec.Attempts = 0 + rec.ResendCount = 0 + rec.CreatedAt = now + rec.UpdatedAt = now + + return code, salt, rec, nil +} + +func hashCode(salt []byte, code string) []byte { + h := sha256.New() + h.Write(salt) + h.Write([]byte(code)) + return h.Sum(nil) +} + +func newSalt() ([]byte, error) { + buf := make([]byte, 16) + if _, err := rand.Read(buf); err != nil { + return nil, err + } + return buf, nil +} diff --git a/api/server/internal/server/confirmationimp/types.go b/api/server/internal/server/confirmationimp/types.go new file mode 100644 index 0000000..743c0b6 --- /dev/null +++ b/api/server/internal/server/confirmationimp/types.go @@ -0,0 +1,23 @@ +package confirmationimp + +import ( + "github.com/tech/sendico/pkg/model" +) + +type confirmationRequest struct { + Target string `json:"target"` + Destination string `json:"destination,omitempty"` +} + +type confirmationVerifyRequest struct { + Target string `json:"target"` + Code string `json:"code"` + Destination string `json:"destination,omitempty"` + SessionIdentifier model.SessionIdentifier `json:"sessionIdentifier"` +} + +type confirmationResponse struct { + TTLSeconds int `json:"ttl_seconds"` + CooldownSeconds int `json:"cooldown_seconds"` + Destination string `json:"destination"` +} diff --git a/api/server/internal/server/confirmationimp/verify.go b/api/server/internal/server/confirmationimp/verify.go new file mode 100644 index 0000000..4ff4434 --- /dev/null +++ b/api/server/internal/server/confirmationimp/verify.go @@ -0,0 +1,88 @@ +package confirmationimp + +import ( + "encoding/json" + "errors" + "net/http" + "strings" + + "github.com/tech/sendico/pkg/api/http/response" + "github.com/tech/sendico/pkg/model" + "github.com/tech/sendico/pkg/mutil/mzap" + "github.com/tech/sendico/server/interface/api/sresponse" + emodel "github.com/tech/sendico/server/interface/model" + rtokens "github.com/tech/sendico/server/internal/api/routers/tokens" + "go.uber.org/zap" +) + +func (a *ConfirmationAPI) verifyCode(r *http.Request, account *model.Account, token *emodel.AccountToken) http.HandlerFunc { + var req confirmationVerifyRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + a.logger.Warn("Failed to decode confirmation verification request", zap.Error(err)) + return response.BadPayload(a.logger, a.Name(), err) + } + + target, err := a.parseTarget(req.Target) + if err != nil { + return response.BadRequest(a.logger, a.Name(), "invalid_target", err.Error()) + } + + if target == model.ConfirmationTargetLogin && (token == nil || !token.Pending) { + return response.Forbidden(a.logger, a.Name(), "pending_token_required", "login confirmation requires pending token") + } + + if strings.TrimSpace(req.Code) == "" { + return response.BadRequest(a.logger, a.Name(), "missing_code", "confirmation code is required") + } + + destination := a.resolveDestination(req.Destination, account) + if destination == "" { + return response.BadRequest(a.logger, a.Name(), "missing_destination", "email destination is required") + } + err = a.store.Verify(r.Context(), account.ID, destination, target, strings.TrimSpace(req.Code)) + switch { + case errors.Is(err, errConfirmationNotFound): + return response.NotFound(a.logger, a.Name(), "code_not_found_or_expired") + case errors.Is(err, errConfirmationUsed): + return response.Forbidden(a.logger, a.Name(), "code_used", "code has already been used") + case errors.Is(err, errConfirmationAttemptsExceeded): + return response.Forbidden(a.logger, a.Name(), "attempt_limit_reached", "too many failed attempts") + case errors.Is(err, errConfirmationMismatch): + return response.Forbidden(a.logger, a.Name(), "invalid_code", "code does not match") + case err != nil: + a.logger.Warn("Failed to verify confirmation code", zap.Error(err), mzap.ObjRef("account_ref", account.ID)) + return response.Internal(a.logger, a.Name(), err) + } + + a.logger.Info("Confirmation code verified", zap.String("target", string(target)), mzap.ObjRef("account_ref", account.ID)) + if target == model.ConfirmationTargetLogin { + if req.SessionIdentifier.ClientID == "" || req.SessionIdentifier.DeviceID == "" { + return response.BadRequest(a.logger, a.Name(), "missing_session", "session identifier is required") + } + accessToken, err := a.createAccessToken(account) + if err != nil { + a.logger.Warn("Failed to generate access token", zap.Error(err)) + return response.Internal(a.logger, a.Name(), err) + } + refreshToken, err := rtokens.PrepareRefreshToken( + r.Context(), + r, + a.rtdb, + a.tokenConfig.Length, + a.tokenConfig.Expiration.Refresh, + &req.SessionIdentifier, + account, + a.logger, + ) + if err != nil { + a.logger.Warn("Failed to generate refresh token", zap.Error(err)) + return response.Internal(a.logger, a.Name(), err) + } + rt := sresponse.TokenData{ + Token: refreshToken.RefreshToken, + Expiration: refreshToken.ExpiresAt, + } + return sresponse.Login(a.logger, account, &accessToken, &rt) + } + return response.Success(a.logger) +} diff --git a/frontend/pshared/lib/api/responses/login_pending.dart b/frontend/pshared/lib/api/responses/login_pending.dart new file mode 100644 index 0000000..91e5241 --- /dev/null +++ b/frontend/pshared/lib/api/responses/login_pending.dart @@ -0,0 +1,26 @@ +import 'package:json_annotation/json_annotation.dart'; + +import 'package:pshared/api/responses/account.dart'; +import 'package:pshared/api/responses/token.dart'; + +part 'login_pending.g.dart'; + + +@JsonSerializable(explicitToJson: true) +class PendingLoginResponse { + final AccountResponse account; + final TokenData pendingToken; + final String destination; + final int ttlSeconds; + + const PendingLoginResponse({ + required this.account, + required this.pendingToken, + required this.destination, + required this.ttlSeconds, + }); + + factory PendingLoginResponse.fromJson(Map json) => _$PendingLoginResponseFromJson(json); + + Map toJson() => _$PendingLoginResponseToJson(this); +} diff --git a/frontend/pshared/lib/models/auth/login_outcome.dart b/frontend/pshared/lib/models/auth/login_outcome.dart new file mode 100644 index 0000000..24df29e --- /dev/null +++ b/frontend/pshared/lib/models/auth/login_outcome.dart @@ -0,0 +1,17 @@ +import 'package:pshared/models/account/account.dart'; +import 'package:pshared/models/auth/pending_login.dart'; + + +class LoginOutcome { + final Account? account; + final PendingLogin? pending; + + const LoginOutcome._({this.account, this.pending}); + + factory LoginOutcome.completed(Account account) => LoginOutcome._(account: account); + + factory LoginOutcome.pending(PendingLogin pending) => LoginOutcome._(pending: pending); + + bool get isPending => pending != null; + bool get isCompleted => account != null; +} diff --git a/frontend/pshared/lib/models/auth/pending_login.dart b/frontend/pshared/lib/models/auth/pending_login.dart new file mode 100644 index 0000000..3585bcc --- /dev/null +++ b/frontend/pshared/lib/models/auth/pending_login.dart @@ -0,0 +1,33 @@ +import 'package:pshared/api/responses/login_pending.dart'; +import 'package:pshared/api/responses/token.dart'; +import 'package:pshared/data/mapper/account/account.dart'; +import 'package:pshared/models/account/account.dart'; +import 'package:pshared/models/session_identifier.dart'; + + +class PendingLogin { + final Account account; + final TokenData pendingToken; + final String destination; + final int ttlSeconds; + final SessionIdentifier session; + + const PendingLogin({ + required this.account, + required this.pendingToken, + required this.destination, + required this.ttlSeconds, + required this.session, + }); + + factory PendingLogin.fromResponse( + PendingLoginResponse response, { + required SessionIdentifier session, + }) => PendingLogin( + account: response.account.account.toDomain(), + pendingToken: response.pendingToken, + destination: response.destination, + ttlSeconds: response.ttlSeconds, + session: session, + ); +} diff --git a/frontend/pshared/lib/models/session_identifier.dart b/frontend/pshared/lib/models/session_identifier.dart new file mode 100644 index 0000000..106f8f8 --- /dev/null +++ b/frontend/pshared/lib/models/session_identifier.dart @@ -0,0 +1,18 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'session_identifier.g.dart'; + +@JsonSerializable() +class SessionIdentifier { + final String clientId; + final String deviceId; + + const SessionIdentifier({ + required this.clientId, + required this.deviceId, + }); + + factory SessionIdentifier.fromJson(Map json) => _$SessionIdentifierFromJson(json); + + Map toJson() => _$SessionIdentifierToJson(this); +} diff --git a/frontend/pshared/lib/provider/account.dart b/frontend/pshared/lib/provider/account.dart index f44906d..fea9b02 100644 --- a/frontend/pshared/lib/provider/account.dart +++ b/frontend/pshared/lib/provider/account.dart @@ -7,6 +7,8 @@ import 'package:pshared/api/requests/signup.dart'; import 'package:pshared/api/requests/login_data.dart'; import 'package:pshared/config/constants.dart'; import 'package:pshared/models/account/account.dart'; +import 'package:pshared/models/auth/login_outcome.dart'; +import 'package:pshared/models/auth/pending_login.dart'; import 'package:pshared/models/describable.dart'; import 'package:pshared/models/storable.dart'; import 'package:pshared/provider/locale.dart'; @@ -23,8 +25,10 @@ class AccountProvider extends ChangeNotifier { Resource _resource = Resource(data: null); Resource get resource => _resource; late LocaleProvider _localeProvider; + PendingLogin? _pendingLogin; Account? get account => _resource.data; + PendingLogin? get pendingLogin => _pendingLogin; bool get isLoggedIn => account != null; bool get isLoading => _resource.isLoading; Object? get error => _resource.error; @@ -57,27 +61,38 @@ class AccountProvider extends ChangeNotifier { void _pickupLocale(String locale) => _localeProvider.setLocale(Locale(locale)); - Future login({ + Future login({ required String email, required String password, required String locale, }) async { _setResource(_resource.copyWith(isLoading: true, error: null)); try { - final acc = await AccountService.login(LoginData.build( + final outcome = await AccountService.login(LoginData.build( login: email, password: password, locale: locale, )); - _setResource(Resource(data: acc, isLoading: false)); - _pickupLocale(acc.locale); - return acc; + if (outcome.account != null) { + _setResource(Resource(data: outcome.account, isLoading: false)); + _pickupLocale(outcome.account!.locale); + } else { + _pendingLogin = outcome.pending; + _setResource(_resource.copyWith(isLoading: false)); + } + return outcome; } catch (e) { _setResource(_resource.copyWith(isLoading: false, error: toException(e))); rethrow; } } + void completePendingLogin(Account account) { + _pendingLogin = null; + _setResource(Resource(data: account, isLoading: false, error: null)); + _pickupLocale(account.locale); + } + Future isAuthorizationStored() async => AuthorizationService.isAuthorizationStored(); Future restore() async { diff --git a/frontend/pshared/lib/service/account.dart b/frontend/pshared/lib/service/account.dart index a935dc2..facb9a1 100644 --- a/frontend/pshared/lib/service/account.dart +++ b/frontend/pshared/lib/service/account.dart @@ -8,9 +8,13 @@ import 'package:pshared/api/requests/login_data.dart'; import 'package:pshared/api/requests/password/change.dart'; import 'package:pshared/api/requests/password/forgot.dart'; import 'package:pshared/api/requests/password/reset.dart'; +import 'package:pshared/api/responses/login.dart'; import 'package:pshared/data/mapper/account/account.dart'; import 'package:pshared/models/account/account.dart'; +import 'package:pshared/models/auth/login_outcome.dart'; +import 'package:pshared/models/auth/pending_login.dart'; import 'package:pshared/service/authorization/service.dart'; +import 'package:pshared/service/authorization/storage.dart'; import 'package:pshared/service/files.dart'; import 'package:pshared/service/services.dart'; import 'package:pshared/utils/http/requests.dart'; @@ -20,11 +24,46 @@ class AccountService { static final _logger = Logger('service.account'); static const String _objectType = Services.account; - static Future login(LoginData login) async { + static Future login(LoginData login) async { _logger.fine('Logging in'); return AuthorizationService.login(_objectType, login); } + static Future resendLoginCode(PendingLogin pending, {String? destination}) async { + await getPOSTResponse( + _objectType, + 'confirmations/resend', + { + 'target': 'login', + if (destination != null) 'destination': destination, + }, + authToken: pending.pendingToken.token, + ); + } + + static Future confirmLoginCode({ + required PendingLogin pending, + required String code, + String? destination, + }) async { + final response = await getPOSTResponse( + _objectType, + 'confirmations/verify', + { + 'target': 'login', + 'code': code, + if (destination != null) 'destination': destination, + 'sessionIdentifier': pending.session.toJson(), + }, + authToken: pending.pendingToken.token, + ); + + final loginResponse = LoginResponse.fromJson(response); + await AuthorizationStorage.updateToken(loginResponse.accessToken); + await AuthorizationStorage.updateRefreshToken(loginResponse.refreshToken); + return loginResponse.account.toDomain(); + } + static Future restore() async { return AuthorizationService.restore(); } diff --git a/frontend/pshared/lib/service/authorization/service.dart b/frontend/pshared/lib/service/authorization/service.dart index 57e615f..a73a752 100644 --- a/frontend/pshared/lib/service/authorization/service.dart +++ b/frontend/pshared/lib/service/authorization/service.dart @@ -5,9 +5,13 @@ import 'package:pshared/api/requests/login.dart'; import 'package:pshared/api/requests/login_data.dart'; import 'package:pshared/api/responses/account.dart'; import 'package:pshared/api/responses/login.dart'; +import 'package:pshared/api/responses/login_pending.dart'; import 'package:pshared/config/web.dart'; import 'package:pshared/data/mapper/account/account.dart'; import 'package:pshared/models/account/account.dart'; +import 'package:pshared/models/auth/login_outcome.dart'; +import 'package:pshared/models/auth/pending_login.dart'; +import 'package:pshared/models/session_identifier.dart'; import 'package:pshared/service/authorization/circuit_breaker.dart'; import 'package:pshared/service/authorization/retry_helper.dart'; import 'package:pshared/service/authorization/storage.dart'; @@ -22,7 +26,7 @@ import 'package:pshared/utils/http/requests.dart' as httpr; class AuthorizationService { static final _logger = Logger('service.authorization.auth_service'); - static Future login(String service, LoginData login) async { + static Future login(String service, LoginData login) async { _logger.fine('Logging in ${login.login} with ${login.locale} locale'); final deviceId = await DeviceIdManager.getDeviceId(); final response = await httpr.getPOSTResponse( @@ -31,7 +35,17 @@ class AuthorizationService { LoginRequest(login: login, deviceId: deviceId, clientId: Constants.clientId).toJson(), ); - return (await _completeLogin(response)).account.toDomain(); + if (response.containsKey('refreshToken')) { + return LoginOutcome.completed((await completeLogin(response)).account.toDomain()); + } + if (response.containsKey('pendingToken')) { + final pending = PendingLogin.fromResponse( + PendingLoginResponse.fromJson(response), + session: SessionIdentifier(clientId: Constants.clientId, deviceId: deviceId), + ); + return LoginOutcome.pending(pending); + } + throw AuthenticationFailedException('Unexpected login response', Exception(response.toString())); } static Future _updateAccessToken(AccountResponse response) async { @@ -49,6 +63,8 @@ class AuthorizationService { return lr; } + static Future completeLogin(Map response) => _completeLogin(response); + static Future restore() async { return (await TokenService.refreshAccessToken()).account.toDomain(); } diff --git a/frontend/pweb/lib/main.dart b/frontend/pweb/lib/main.dart index 3eaca66..a5bf863 100644 --- a/frontend/pweb/lib/main.dart +++ b/frontend/pweb/lib/main.dart @@ -24,7 +24,6 @@ import 'package:pweb/providers/two_factor.dart'; import 'package:pweb/providers/upload_history.dart'; import 'package:pweb/providers/wallets.dart'; import 'package:pweb/services/amplitude.dart'; -import 'package:pweb/services/auth.dart'; import 'package:pweb/services/balance.dart'; import 'package:pweb/services/payments/payment_methods.dart'; import 'package:pweb/services/payments/upload_history.dart'; @@ -53,17 +52,16 @@ void main() async { runApp( MultiProvider( providers: [ - Provider( - create: (_) => AuthenticationService(), - ), - ChangeNotifierProxyProvider( - create: (context) => TwoFactorProvider( - context.read(), - ), - update: (context, authService, previous) => TwoFactorProvider(authService), - ), ChangeNotifierProvider(create: (_) => LocaleProvider(null)), ChangeNotifierProvider(create: (_) => AccountProvider()), + ChangeNotifierProxyProvider( + create: (context) => TwoFactorProvider( + accountProvider: context.read(), + ), + update: (context, accountProvider, previous) => TwoFactorProvider( + accountProvider: accountProvider, + ), + ), ChangeNotifierProvider(create: (_) => OrganizationsProvider()), ChangeNotifierProvider(create: (_) => AccountProvider()), ChangeNotifierProvider(create: (_) => CarouselIndexProvider()), diff --git a/frontend/pweb/lib/pages/2fa/resend.dart b/frontend/pweb/lib/pages/2fa/resend.dart index 57bde0d..13eb971 100644 --- a/frontend/pweb/lib/pages/2fa/resend.dart +++ b/frontend/pweb/lib/pages/2fa/resend.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + import 'package:pweb/generated/i18n/app_localizations.dart'; +import 'package:pweb/providers/two_factor.dart'; class ResendCodeButton extends StatelessWidget { @@ -12,9 +15,7 @@ class ResendCodeButton extends StatelessWidget { final localizations = AppLocalizations.of(context)!; return TextButton( - onPressed: () { - // TODO: Add resend logic - }, + onPressed: () => context.read().resendCode(), style: TextButton.styleFrom( padding: EdgeInsets.zero, minimumSize: const Size(0, 0), @@ -28,4 +29,4 @@ class ResendCodeButton extends StatelessWidget { child: Text(localizations.twoFactorResend), ); } -} \ No newline at end of file +} diff --git a/frontend/pweb/lib/pages/login/form.dart b/frontend/pweb/lib/pages/login/form.dart index a145cb6..bf230b1 100644 --- a/frontend/pweb/lib/pages/login/form.dart +++ b/frontend/pweb/lib/pages/login/form.dart @@ -38,13 +38,16 @@ class _LoginFormState extends State { final provider = Provider.of(context, listen: false); try { - //final account = - await provider.login( + final outcome = await provider.login( email: _usernameController.text, password: _passwordController.text, locale: context.read().locale.languageCode, ); - onLogin(); + if (outcome.isPending) { + navigateAndReplace(context, Pages.sfactor); + } else { + onLogin(); + } return 'ok'; } catch (e) { onError(provider.error ?? e); @@ -92,7 +95,7 @@ class _LoginFormState extends State { onSignUp: () => navigate(context, Pages.signup), login: () => _login( context, - () => navigateAndReplace(context, Pages.sfactor), + () => navigateAndReplace(context, Pages.dashboard), (e) => postNotifyUserOfErrorX( context: context, errorSituation: AppLocalizations.of(context)!.errorLogin, diff --git a/frontend/pweb/lib/providers/two_factor.dart b/frontend/pweb/lib/providers/two_factor.dart index c5a74e4..f23bb52 100644 --- a/frontend/pweb/lib/providers/two_factor.dart +++ b/frontend/pweb/lib/providers/two_factor.dart @@ -1,38 +1,69 @@ import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; -import 'package:pweb/services/auth.dart'; - +import 'package:pshared/models/auth/pending_login.dart'; +import 'package:pshared/provider/account.dart'; +import 'package:pshared/service/account.dart'; class TwoFactorProvider extends ChangeNotifier { - final AuthenticationService _authService; + static final _logger = Logger('provider.two_factor'); + final AccountProvider _accountProvider; - TwoFactorProvider(this._authService); + TwoFactorProvider({required AccountProvider accountProvider}) : _accountProvider = accountProvider; bool _isSubmitting = false; bool _hasError = false; bool _verificationSuccess = false; + String? _errorMessage; bool get isSubmitting => _isSubmitting; bool get hasError => _hasError; bool get verificationSuccess => _verificationSuccess; + String? get errorMessage => _errorMessage; + PendingLogin? get pendingLogin => _accountProvider.pendingLogin; Future submitCode(String code) async { _isSubmitting = true; _hasError = false; + _errorMessage = null; _verificationSuccess = false; notifyListeners(); try { - final success = await _authService.verifyTwoFactorCode(code); - if (success) { - _verificationSuccess = true; + final pending = _accountProvider.pendingLogin; + if (pending == null) { + throw Exception('No pending login available'); } + final account = await AccountService.confirmLoginCode( + pending: pending, + code: code, + ); + _accountProvider.completePendingLogin(account); + _verificationSuccess = true; } catch (e) { _hasError = true; + _errorMessage = e.toString(); + _logger.warning('Failed to verify code', e); } finally { _isSubmitting = false; notifyListeners(); } } -} \ No newline at end of file + + Future resendCode() async { + final pending = _accountProvider.pendingLogin; + if (pending == null) { + _logger.warning('No pending login to resend code for'); + return; + } + try { + await AccountService.resendLoginCode(pending); + } catch (e) { + _logger.warning('Failed to resend login code', e); + _hasError = true; + _errorMessage = e.toString(); + notifyListeners(); + } + } +} diff --git a/frontend/pweb/lib/services/auth.dart b/frontend/pweb/lib/services/auth.dart deleted file mode 100644 index 95a75e7..0000000 --- a/frontend/pweb/lib/services/auth.dart +++ /dev/null @@ -1,12 +0,0 @@ - -class AuthenticationService { - Future verifyTwoFactorCode(String code) async { - await Future.delayed(const Duration(seconds: 2)); - - if (code == '000000') { - return true; - } else { - throw Exception('Wrong Code'); //TODO Localize - } - } -} diff --git a/frontend/pweb/macos/Flutter/GeneratedPluginRegistrant.swift b/frontend/pweb/macos/Flutter/GeneratedPluginRegistrant.swift index 79f5652..33de092 100644 --- a/frontend/pweb/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/frontend/pweb/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,7 +8,6 @@ import Foundation import amplitude_flutter import file_selector_macos import flutter_timezone -import path_provider_foundation import share_plus import shared_preferences_foundation import sqflite_darwin @@ -18,7 +17,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AmplitudeFlutterPlugin.register(with: registry.registrar(forPlugin: "AmplitudeFlutterPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FlutterTimezonePlugin.register(with: registry.registrar(forPlugin: "FlutterTimezonePlugin")) - PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))