Compare commits
25 Commits
357af99564
...
44446c6ad4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44446c6ad4 | ||
|
|
48ccbb1c82 | ||
|
|
68f0a1048f | ||
|
|
be913bf96c | ||
|
|
8e1d4bef59 | ||
|
|
c6da138184 | ||
|
|
d78619bccf | ||
|
|
85b780b57e | ||
|
|
1f31fedc3a | ||
|
|
bdf3a01f80 | ||
|
|
d126d5d5de | ||
|
|
26a1e284b2 | ||
|
|
fc0600d6c4 | ||
|
|
b855404999 | ||
|
|
e3a8fb4f2d | ||
|
|
d65e442cb6 | ||
|
|
b4f6f63871 | ||
|
|
803683be7c | ||
|
|
72271cfc9a | ||
|
|
cd79355e69 | ||
|
|
3f84f8c609 | ||
|
|
ae15e1887b | ||
|
|
8a41785b1d | ||
|
|
56abc10dce | ||
| d8a3a5550d |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@
|
|||||||
*.pb.gw.go
|
*.pb.gw.go
|
||||||
pubspec.lock
|
pubspec.lock
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
update_dep.sh
|
||||||
@@ -32,14 +32,14 @@ require (
|
|||||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.47.0 // indirect
|
github.com/nats-io/nats.go v1.47.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.11 // indirect
|
github.com/nats-io/nkeys v0.4.12 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/prometheus/client_golang v1.23.2
|
github.com/prometheus/client_golang v1.23.2
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.4 // indirect
|
github.com/prometheus/common v0.67.4 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.19.2 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.1.2 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
@@ -49,6 +49,6 @@ require (
|
|||||||
golang.org/x/sync v0.18.0 // indirect
|
golang.org/x/sync v0.18.0 // indirect
|
||||||
golang.org/x/sys v0.38.0 // indirect
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
golang.org/x/text v0.31.0 // indirect
|
golang.org/x/text v0.31.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 // indirect
|
||||||
google.golang.org/protobuf v1.36.10
|
google.golang.org/protobuf v1.36.10
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -97,8 +97,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
|||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.47.0 h1:YQdADw6J/UfGUd2Oy6tn4Hq6YHxCaJrVKayxxFqYrgM=
|
github.com/nats-io/nats.go v1.47.0 h1:YQdADw6J/UfGUd2Oy6tn4Hq6YHxCaJrVKayxxFqYrgM=
|
||||||
github.com/nats-io/nats.go v1.47.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.47.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
||||||
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
|
github.com/nats-io/nkeys v0.4.12 h1:nssm7JKOG9/x4J8II47VWCL1Ds29avyiQDRn0ckMvDc=
|
||||||
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
|
github.com/nats-io/nkeys v0.4.12/go.mod h1:MT59A1HYcjIcyQDJStTfaOY6vhy9XTUjOFo+SVsvpBg=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
@@ -141,8 +141,8 @@ github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfj
|
|||||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
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/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=
|
||||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=
|
||||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||||
@@ -212,8 +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=
|
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 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
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-20251124214823-79d6a2a48846 h1:Wgl1rcDNThT+Zn47YyCXOXyX/COgMTIdhJ717F0l4xk=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
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/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ require (
|
|||||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.47.0 // indirect
|
github.com/nats-io/nats.go v1.47.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.11 // indirect
|
github.com/nats-io/nkeys v0.4.12 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
@@ -73,7 +73,7 @@ require (
|
|||||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.1.2 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
@@ -86,5 +86,5 @@ require (
|
|||||||
golang.org/x/sys v0.38.0 // indirect
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
golang.org/x/text v0.31.0 // indirect
|
golang.org/x/text v0.31.0 // indirect
|
||||||
golang.org/x/time v0.14.0 // indirect
|
golang.org/x/time v0.14.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -209,8 +209,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
|||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.47.0 h1:YQdADw6J/UfGUd2Oy6tn4Hq6YHxCaJrVKayxxFqYrgM=
|
github.com/nats-io/nats.go v1.47.0 h1:YQdADw6J/UfGUd2Oy6tn4Hq6YHxCaJrVKayxxFqYrgM=
|
||||||
github.com/nats-io/nats.go v1.47.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.47.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
||||||
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
|
github.com/nats-io/nkeys v0.4.12 h1:nssm7JKOG9/x4J8II47VWCL1Ds29avyiQDRn0ckMvDc=
|
||||||
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
|
github.com/nats-io/nkeys v0.4.12/go.mod h1:MT59A1HYcjIcyQDJStTfaOY6vhy9XTUjOFo+SVsvpBg=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||||
@@ -283,8 +283,8 @@ github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
|
|||||||
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
|
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
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/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=
|
||||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=
|
||||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
|
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
|
||||||
@@ -362,8 +362,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=
|
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 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
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-20251124214823-79d6a2a48846 h1:Wgl1rcDNThT+Zn47YyCXOXyX/COgMTIdhJ717F0l4xk=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
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/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||||
|
|||||||
@@ -32,13 +32,13 @@ require (
|
|||||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.47.0 // indirect
|
github.com/nats-io/nats.go v1.47.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.11 // indirect
|
github.com/nats-io/nkeys v0.4.12 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.4 // indirect
|
github.com/prometheus/common v0.67.4 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.19.2 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.1.2 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||||
go.mongodb.org/mongo-driver v1.17.6 // indirect
|
go.mongodb.org/mongo-driver v1.17.6 // indirect
|
||||||
@@ -49,7 +49,7 @@ require (
|
|||||||
golang.org/x/sync v0.18.0 // indirect
|
golang.org/x/sync v0.18.0 // indirect
|
||||||
golang.org/x/sys v0.38.0 // indirect
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
golang.org/x/text v0.31.0 // indirect
|
golang.org/x/text v0.31.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 // indirect
|
||||||
google.golang.org/grpc v1.77.0 // indirect
|
google.golang.org/grpc v1.77.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.10 // indirect
|
google.golang.org/protobuf v1.36.10 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -97,8 +97,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
|||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.47.0 h1:YQdADw6J/UfGUd2Oy6tn4Hq6YHxCaJrVKayxxFqYrgM=
|
github.com/nats-io/nats.go v1.47.0 h1:YQdADw6J/UfGUd2Oy6tn4Hq6YHxCaJrVKayxxFqYrgM=
|
||||||
github.com/nats-io/nats.go v1.47.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.47.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
||||||
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
|
github.com/nats-io/nkeys v0.4.12 h1:nssm7JKOG9/x4J8II47VWCL1Ds29avyiQDRn0ckMvDc=
|
||||||
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
|
github.com/nats-io/nkeys v0.4.12/go.mod h1:MT59A1HYcjIcyQDJStTfaOY6vhy9XTUjOFo+SVsvpBg=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
@@ -141,8 +141,8 @@ github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfj
|
|||||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
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/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=
|
||||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=
|
||||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||||
@@ -212,8 +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=
|
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 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
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-20251124214823-79d6a2a48846 h1:Wgl1rcDNThT+Zn47YyCXOXyX/COgMTIdhJ717F0l4xk=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
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/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||||
|
|||||||
@@ -34,13 +34,13 @@ require (
|
|||||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.47.0 // indirect
|
github.com/nats-io/nats.go v1.47.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.11 // indirect
|
github.com/nats-io/nkeys v0.4.12 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.4 // indirect
|
github.com/prometheus/common v0.67.4 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.19.2 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.1.2 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
@@ -50,5 +50,5 @@ require (
|
|||||||
golang.org/x/sync v0.18.0 // indirect
|
golang.org/x/sync v0.18.0 // indirect
|
||||||
golang.org/x/sys v0.38.0 // indirect
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
golang.org/x/text v0.31.0 // indirect
|
golang.org/x/text v0.31.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -97,8 +97,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
|||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.47.0 h1:YQdADw6J/UfGUd2Oy6tn4Hq6YHxCaJrVKayxxFqYrgM=
|
github.com/nats-io/nats.go v1.47.0 h1:YQdADw6J/UfGUd2Oy6tn4Hq6YHxCaJrVKayxxFqYrgM=
|
||||||
github.com/nats-io/nats.go v1.47.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.47.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
||||||
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
|
github.com/nats-io/nkeys v0.4.12 h1:nssm7JKOG9/x4J8II47VWCL1Ds29avyiQDRn0ckMvDc=
|
||||||
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
|
github.com/nats-io/nkeys v0.4.12/go.mod h1:MT59A1HYcjIcyQDJStTfaOY6vhy9XTUjOFo+SVsvpBg=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
@@ -141,8 +141,8 @@ github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfj
|
|||||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
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/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=
|
||||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=
|
||||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||||
@@ -212,8 +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=
|
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 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
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-20251124214823-79d6a2a48846 h1:Wgl1rcDNThT+Zn47YyCXOXyX/COgMTIdhJ717F0l4xk=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
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/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ require (
|
|||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.1.2 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
|
|||||||
@@ -109,8 +109,8 @@ github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfj
|
|||||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
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/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=
|
||||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=
|
||||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import (
|
|||||||
|
|
||||||
// Client exposes typed helpers around the ledger gRPC API.
|
// Client exposes typed helpers around the ledger gRPC API.
|
||||||
type Client interface {
|
type Client interface {
|
||||||
|
CreateAccount(ctx context.Context, req *ledgerv1.CreateAccountRequest) (*ledgerv1.CreateAccountResponse, error)
|
||||||
|
ListAccounts(ctx context.Context, req *ledgerv1.ListAccountsRequest) (*ledgerv1.ListAccountsResponse, error)
|
||||||
PostCreditWithCharges(ctx context.Context, req *ledgerv1.PostCreditRequest) (*ledgerv1.PostResponse, error)
|
PostCreditWithCharges(ctx context.Context, req *ledgerv1.PostCreditRequest) (*ledgerv1.PostResponse, error)
|
||||||
PostDebitWithCharges(ctx context.Context, req *ledgerv1.PostDebitRequest) (*ledgerv1.PostResponse, error)
|
PostDebitWithCharges(ctx context.Context, req *ledgerv1.PostDebitRequest) (*ledgerv1.PostResponse, error)
|
||||||
TransferInternal(ctx context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error)
|
TransferInternal(ctx context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error)
|
||||||
@@ -29,6 +31,8 @@ type Client interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type grpcLedgerClient interface {
|
type grpcLedgerClient interface {
|
||||||
|
CreateAccount(ctx context.Context, in *ledgerv1.CreateAccountRequest, opts ...grpc.CallOption) (*ledgerv1.CreateAccountResponse, error)
|
||||||
|
ListAccounts(ctx context.Context, in *ledgerv1.ListAccountsRequest, opts ...grpc.CallOption) (*ledgerv1.ListAccountsResponse, error)
|
||||||
PostCreditWithCharges(ctx context.Context, in *ledgerv1.PostCreditRequest, opts ...grpc.CallOption) (*ledgerv1.PostResponse, error)
|
PostCreditWithCharges(ctx context.Context, in *ledgerv1.PostCreditRequest, opts ...grpc.CallOption) (*ledgerv1.PostResponse, error)
|
||||||
PostDebitWithCharges(ctx context.Context, in *ledgerv1.PostDebitRequest, opts ...grpc.CallOption) (*ledgerv1.PostResponse, error)
|
PostDebitWithCharges(ctx context.Context, in *ledgerv1.PostDebitRequest, opts ...grpc.CallOption) (*ledgerv1.PostResponse, error)
|
||||||
TransferInternal(ctx context.Context, in *ledgerv1.TransferRequest, opts ...grpc.CallOption) (*ledgerv1.PostResponse, error)
|
TransferInternal(ctx context.Context, in *ledgerv1.TransferRequest, opts ...grpc.CallOption) (*ledgerv1.PostResponse, error)
|
||||||
@@ -91,6 +95,18 @@ func (c *ledgerClient) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ledgerClient) CreateAccount(ctx context.Context, req *ledgerv1.CreateAccountRequest) (*ledgerv1.CreateAccountResponse, error) {
|
||||||
|
ctx, cancel := c.callContext(ctx)
|
||||||
|
defer cancel()
|
||||||
|
return c.client.CreateAccount(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ledgerClient) ListAccounts(ctx context.Context, req *ledgerv1.ListAccountsRequest) (*ledgerv1.ListAccountsResponse, error) {
|
||||||
|
ctx, cancel := c.callContext(ctx)
|
||||||
|
defer cancel()
|
||||||
|
return c.client.ListAccounts(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *ledgerClient) PostCreditWithCharges(ctx context.Context, req *ledgerv1.PostCreditRequest) (*ledgerv1.PostResponse, error) {
|
func (c *ledgerClient) PostCreditWithCharges(ctx context.Context, req *ledgerv1.PostCreditRequest) (*ledgerv1.PostResponse, error) {
|
||||||
ctx, cancel := c.callContext(ctx)
|
ctx, cancel := c.callContext(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|||||||
@@ -34,14 +34,14 @@ require (
|
|||||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.47.0 // indirect
|
github.com/nats-io/nats.go v1.47.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.11 // indirect
|
github.com/nats-io/nkeys v0.4.12 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.4 // indirect
|
github.com/prometheus/common v0.67.4 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.19.2 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.1.2 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
@@ -51,5 +51,5 @@ require (
|
|||||||
golang.org/x/sync v0.18.0 // indirect
|
golang.org/x/sync v0.18.0 // indirect
|
||||||
golang.org/x/sys v0.38.0 // indirect
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
golang.org/x/text v0.31.0 // indirect
|
golang.org/x/text v0.31.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -97,8 +97,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
|||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.47.0 h1:YQdADw6J/UfGUd2Oy6tn4Hq6YHxCaJrVKayxxFqYrgM=
|
github.com/nats-io/nats.go v1.47.0 h1:YQdADw6J/UfGUd2Oy6tn4Hq6YHxCaJrVKayxxFqYrgM=
|
||||||
github.com/nats-io/nats.go v1.47.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.47.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
||||||
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
|
github.com/nats-io/nkeys v0.4.12 h1:nssm7JKOG9/x4J8II47VWCL1Ds29avyiQDRn0ckMvDc=
|
||||||
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
|
github.com/nats-io/nkeys v0.4.12/go.mod h1:MT59A1HYcjIcyQDJStTfaOY6vhy9XTUjOFo+SVsvpBg=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
@@ -143,8 +143,8 @@ github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfj
|
|||||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
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/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=
|
||||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=
|
||||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||||
@@ -214,8 +214,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=
|
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 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
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-20251124214823-79d6a2a48846 h1:Wgl1rcDNThT+Zn47YyCXOXyX/COgMTIdhJ717F0l4xk=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
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/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||||
|
|||||||
47
api/ledger/internal/service/ledger/list_accounts.go
Normal file
47
api/ledger/internal/service/ledger/list_accounts.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package ledger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/pkg/api/routers/gsresponse"
|
||||||
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
|
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Service) listAccountsResponder(_ context.Context, req *ledgerv1.ListAccountsRequest) gsresponse.Responder[ledgerv1.ListAccountsResponse] {
|
||||||
|
return func(ctx context.Context) (*ledgerv1.ListAccountsResponse, error) {
|
||||||
|
if s.storage == nil {
|
||||||
|
return nil, errStorageNotInitialized
|
||||||
|
}
|
||||||
|
if req == nil {
|
||||||
|
return nil, merrors.InvalidArgument("request is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
orgRefStr := strings.TrimSpace(req.GetOrganizationRef())
|
||||||
|
if orgRefStr == "" {
|
||||||
|
return nil, merrors.InvalidArgument("organization_ref is required")
|
||||||
|
}
|
||||||
|
orgRef, err := parseObjectID(orgRefStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// No pagination requested; return all accounts for the organization.
|
||||||
|
accounts, err := s.storage.Accounts().ListByOrganization(ctx, orgRef, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Warn("failed to list ledger accounts", zap.Error(err), zap.String("organizationRef", orgRef.Hex()))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := &ledgerv1.ListAccountsResponse{
|
||||||
|
Accounts: make([]*ledgerv1.LedgerAccount, 0, len(accounts)),
|
||||||
|
}
|
||||||
|
for _, acc := range accounts {
|
||||||
|
resp.Accounts = append(resp.Accounts, toProtoAccount(acc))
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -81,6 +81,12 @@ func (s *Service) Register(router routers.GRPC) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListAccounts lists ledger accounts for an organization.
|
||||||
|
func (s *Service) ListAccounts(ctx context.Context, req *ledgerv1.ListAccountsRequest) (*ledgerv1.ListAccountsResponse, error) {
|
||||||
|
responder := s.listAccountsResponder(ctx, req)
|
||||||
|
return responder(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
// CreateAccount provisions a new ledger account scoped to an organization.
|
// CreateAccount provisions a new ledger account scoped to an organization.
|
||||||
func (s *Service) CreateAccount(ctx context.Context, req *ledgerv1.CreateAccountRequest) (*ledgerv1.CreateAccountResponse, error) {
|
func (s *Service) CreateAccount(ctx context.Context, req *ledgerv1.CreateAccountRequest) (*ledgerv1.CreateAccountResponse, error) {
|
||||||
responder := s.createAccountResponder(ctx, req)
|
responder := s.createAccountResponder(ctx, req)
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ api:
|
|||||||
settings:
|
settings:
|
||||||
username_env: MAIL_USER
|
username_env: MAIL_USER
|
||||||
password_env: MAIL_SECRET
|
password_env: MAIL_SECRET
|
||||||
host: "smtp.mail.ru"
|
host: "mail.sendico.io"
|
||||||
port: 465
|
port: 465
|
||||||
from: "Sendico Tech"
|
from: "Sendico Tech"
|
||||||
network_timeout: 10
|
network_timeout: 10
|
||||||
@@ -65,7 +65,7 @@ api:
|
|||||||
|
|
||||||
localizer:
|
localizer:
|
||||||
path: "./i18n"
|
path: "./i18n"
|
||||||
languages: ["en", "ru", "uk"]
|
languages: ["en", "ru"]
|
||||||
service_name: "Sendico"
|
service_name: "Sendico"
|
||||||
support: "support@sendico.io"
|
support: "support@sendico.io"
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ require (
|
|||||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.47.0 // indirect
|
github.com/nats-io/nats.go v1.47.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.11 // indirect
|
github.com/nats-io/nkeys v0.4.12 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/prometheus/client_golang v1.23.2 // indirect
|
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
@@ -43,7 +43,7 @@ require (
|
|||||||
github.com/sendgrid/rest v2.6.9+incompatible // indirect
|
github.com/sendgrid/rest v2.6.9+incompatible // indirect
|
||||||
github.com/toorop/go-dkim v0.0.0-20250226130143-9025cce95817 // indirect
|
github.com/toorop/go-dkim v0.0.0-20250226130143-9025cce95817 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.1.2 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
@@ -52,7 +52,7 @@ require (
|
|||||||
golang.org/x/net v0.47.0 // indirect
|
golang.org/x/net v0.47.0 // indirect
|
||||||
golang.org/x/sync v0.18.0 // indirect
|
golang.org/x/sync v0.18.0 // indirect
|
||||||
golang.org/x/sys v0.38.0 // indirect
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 // indirect
|
||||||
google.golang.org/grpc v1.77.0 // indirect
|
google.golang.org/grpc v1.77.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.10 // indirect
|
google.golang.org/protobuf v1.36.10 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -101,8 +101,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
|||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.47.0 h1:YQdADw6J/UfGUd2Oy6tn4Hq6YHxCaJrVKayxxFqYrgM=
|
github.com/nats-io/nats.go v1.47.0 h1:YQdADw6J/UfGUd2Oy6tn4Hq6YHxCaJrVKayxxFqYrgM=
|
||||||
github.com/nats-io/nats.go v1.47.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.47.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
||||||
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
|
github.com/nats-io/nkeys v0.4.12 h1:nssm7JKOG9/x4J8II47VWCL1Ds29avyiQDRn0ckMvDc=
|
||||||
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
|
github.com/nats-io/nkeys v0.4.12/go.mod h1:MT59A1HYcjIcyQDJStTfaOY6vhy9XTUjOFo+SVsvpBg=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.6.0 h1:C/m2NNWNiTB6SK4Ao8df5EWm3JETSTIGNXBpMJTxzxQ=
|
github.com/nicksnyder/go-i18n/v2 v2.6.0 h1:C/m2NNWNiTB6SK4Ao8df5EWm3JETSTIGNXBpMJTxzxQ=
|
||||||
@@ -154,8 +154,8 @@ github.com/toorop/go-dkim v0.0.0-20250226130143-9025cce95817 h1:q0hKh5a5FRkhuTb5
|
|||||||
github.com/toorop/go-dkim v0.0.0-20250226130143-9025cce95817/go.mod h1:BzWtXXrXzZUvMacR0oF/fbDDgUPO8L36tDMmRAf14ns=
|
github.com/toorop/go-dkim v0.0.0-20250226130143-9025cce95817/go.mod h1:BzWtXXrXzZUvMacR0oF/fbDDgUPO8L36tDMmRAf14ns=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
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/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=
|
||||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=
|
||||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||||
github.com/xhit/go-simple-mail/v2 v2.16.0 h1:ouGy/Ww4kuaqu2E2UrDw7SvLaziWTB60ICLkIkNVccA=
|
github.com/xhit/go-simple-mail/v2 v2.16.0 h1:ouGy/Ww4kuaqu2E2UrDw7SvLaziWTB60ICLkIkNVccA=
|
||||||
@@ -227,8 +227,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=
|
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 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
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-20251124214823-79d6a2a48846 h1:Wgl1rcDNThT+Zn47YyCXOXyX/COgMTIdhJ717F0l4xk=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
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/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -46,7 +46,7 @@ func (m *EmailNotificationTemplate) prepareUnsubscribe(msg mmail.Message) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
localization.AddLocData(d, "UnsubscribeLink", unsLink)
|
localization.AddLocData(d, "UnsubscribeLink", unsLink)
|
||||||
if block, err = m.l.LocalizeTemplate("mail.template.unsubscribe.block", d, nil, msg.Locale()); err != nil {
|
if block, err = renderUnsubscribeBlock(d); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,7 +58,7 @@ func (m *EmailNotificationTemplate) prepareButton(msg mmail.Message) error {
|
|||||||
var block string
|
var block string
|
||||||
if m.hasButton {
|
if m.hasButton {
|
||||||
var err error
|
var err error
|
||||||
if block, err = m.l.LocalizeTemplate("mail.template.btn.block", m.data, nil, msg.Locale()); err != nil {
|
if block, err = renderButtonBlock(m.data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,7 +91,7 @@ func (m *EmailNotificationTemplate) SignatureData(msg mmail.Message, content, su
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.l.LocalizeTemplate("mail.template.one_button", m.data, nil, msg.Locale())
|
return renderOneButtonEmail(m.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *EmailNotificationTemplate) putOnHTMLTemplate(msg mmail.Message, content, subj string) (string, error) {
|
func (m *EmailNotificationTemplate) putOnHTMLTemplate(msg mmail.Message, content, subj string) (string, error) {
|
||||||
|
|||||||
@@ -0,0 +1,285 @@
|
|||||||
|
package mailimp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
unsubscribeBlockTpl = template.Must(template.New("unsubscribeBlock").Parse(`
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
style="-webkit-text-size-adjust:none;-ms-text-size-adjust:none;mso-line-height-rule:exactly;text-decoration:none;color:#2D3142;font-size:13px"
|
||||||
|
href=""
|
||||||
|
>
|
||||||
|
</a>
|
||||||
|
•
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
style="-webkit-text-size-adjust:none;-ms-text-size-adjust:none;mso-line-height-rule:exactly;text-decoration:none;color:#2D3142;font-size:14px"
|
||||||
|
href="{{.UnsubscribeLink}}"
|
||||||
|
>
|
||||||
|
{{.Unsubscribe}}
|
||||||
|
</a>`))
|
||||||
|
|
||||||
|
buttonBlockTpl = template.Must(template.New("buttonBlock").Parse(`
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="padding:0;Margin:0">
|
||||||
|
<!--[if mso]>
|
||||||
|
<a href="" target="_blank" hidden>
|
||||||
|
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" esdevVmlButton href="{{.ButtonLink}}"
|
||||||
|
style="height:56px; v-text-anchor:middle; width:520px" arcsize="14%" stroke="f" fillcolor="#0b58ff">
|
||||||
|
<w:anchorlock></w:anchorlock>
|
||||||
|
<center style='color:#ffffff; font-family:montserrat, roboto; font-size:22px; font-weight:700; line-height:22px; mso-text-raise:1px'>{{.ButtonText}}</center>
|
||||||
|
</v:roundrect>
|
||||||
|
</a>
|
||||||
|
<![endif]-->
|
||||||
|
<!--[if !mso]><!-- -->
|
||||||
|
<span class="msohide es-button-border" style="border-style:solid;border-color:#0b58ff;background:#0b58ff;border-width:0px;display:block;border-radius:8px;width:auto;mso-border-alt:10px;mso-hide:all;width:520px">
|
||||||
|
<a
|
||||||
|
href="{{.ButtonLink}}"
|
||||||
|
class="es-button msohide"
|
||||||
|
target="_blank"
|
||||||
|
style="mso-style-priority:100 !important;text-decoration:none;-webkit-text-size-adjust:none;-ms-text-size-adjust:none;mso-line-height-rule:exactly;color:#ffffff;font-size:22px;padding:15px 20px 15px 20px;display:block;background:#0b58ff;border-radius:8px;font-family:montserrat, roboto;font-weight:bold;font-style:normal;line-height:26px;width:auto;text-align:center;border-color:#0b58ff;mso-hide:all;padding-left:5px;padding-right:5px"
|
||||||
|
>
|
||||||
|
{{.ButtonText}}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<!--<![endif]-->
|
||||||
|
</td>
|
||||||
|
</tr>`))
|
||||||
|
|
||||||
|
oneButtonEmailTpl = template.Must(template.New("oneButtonEmail").Parse(`<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office" style="font-family:montserrat, roboto">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta content="width=device-width, initial-scale=1" name="viewport">
|
||||||
|
<meta name="x-apple-disable-message-reformatting">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta content="telephone=no" name="format-detection">
|
||||||
|
<title>{{.MessageTitle}}</title>
|
||||||
|
<!--[if (mso 16)]>
|
||||||
|
<style type="text/css">
|
||||||
|
a {text-decoration: none;}
|
||||||
|
</style>
|
||||||
|
<![endif]-->
|
||||||
|
<!--[if gte mso 9]><style>sup { font-size: 100% !important; }</style><![endif]-->
|
||||||
|
<!--[if gte mso 9]>
|
||||||
|
<xml>
|
||||||
|
<o:OfficeDocumentSettings>
|
||||||
|
<o:AllowPNG></o:AllowPNG>
|
||||||
|
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||||
|
</o:OfficeDocumentSettings>
|
||||||
|
</xml>
|
||||||
|
<![endif]-->
|
||||||
|
<!--[if !mso]><!-- -->
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Imprima&display=swap" rel="stylesheet">
|
||||||
|
<!--<![endif]-->
|
||||||
|
<!--[if !mso]><!-- -->
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Montserrat:100,300,400,500,700,900">
|
||||||
|
<!--<![endif]-->
|
||||||
|
<style type="text/css">
|
||||||
|
#outlook a { padding:0; }
|
||||||
|
.es-button { mso-style-priority:100!important; text-decoration:none!important; }
|
||||||
|
a[x-apple-data-detectors] {
|
||||||
|
color:inherit!important;
|
||||||
|
text-decoration:none!important;
|
||||||
|
font-size:inherit!important;
|
||||||
|
font-family:inherit!important;
|
||||||
|
font-weight:inherit!important;
|
||||||
|
line-height:inherit!important;
|
||||||
|
}
|
||||||
|
.es-desk-hidden { display:none; float:left; overflow:hidden; width:0; max-height:0; line-height:0; mso-hide:all; }
|
||||||
|
@media only screen and (max-width:600px) {
|
||||||
|
p, ul li, ol li, a { line-height:150%!important }
|
||||||
|
h1, h2, h3, h1 a, h2 a, h3 a { line-height:120% }
|
||||||
|
h1 { font-size:30px!important; text-align:left }
|
||||||
|
h2 { font-size:24px!important; text-align:left }
|
||||||
|
h3 { font-size:20px!important; text-align:left }
|
||||||
|
.es-header-body h1 a, .es-content-body h1 a, .es-footer-body h1 a { font-size:30px!important; text-align:left }
|
||||||
|
.es-header-body h2 a, .es-content-body h2 a, .es-footer-body h2 a { font-size:24px!important; text-align:left }
|
||||||
|
.es-header-body h3 a, .es-content-body h3 a, .es-footer-body h3 a { font-size:20px!important; text-align:left }
|
||||||
|
.es-menu td a { font-size:14px!important }
|
||||||
|
.es-header-body p, .es-header-body ul li, .es-header-body ol li, .es-header-body a { font-size:14px!important }
|
||||||
|
.es-content-body p, .es-content-body ul li, .es-content-body ol li, .es-content-body a { font-size:14px!important }
|
||||||
|
.es-footer-body p, .es-footer-body ul li, .es-footer-body ol li, .es-footer-body a { font-size:14px!important }
|
||||||
|
.es-infoblock p, .es-infoblock ul li, .es-infoblock ol li, .es-infoblock a { font-size:12px!important }
|
||||||
|
*[class="gmail-fix"] { display:none!important }
|
||||||
|
.es-m-txt-c, .es-m-txt-c h1, .es-m-txt-c h2, .es-m-txt-c h3 { text-align:center!important }
|
||||||
|
.es-m-txt-r, .es-m-txt-r h1, .es-m-txt-r h2, .es-m-txt-r h3 { text-align:right!important }
|
||||||
|
.es-m-txt-l, .es-m-txt-l h1, .es-m-txt-l h2, .es-m-txt-l h3 { text-align:left!important }
|
||||||
|
.es-m-txt-r img, .es-m-txt-c img, .es-m-txt-l img { display:inline!important }
|
||||||
|
.es-button-border { display:block!important }
|
||||||
|
a.es-button, button.es-button { font-size:18px!important; display:block!important; border-right-width:0px!important; border-left-width:0px!important; border-top-width:15px!important; border-bottom-width:15px!important; padding-left:0px!important; padding-right:0px!important }
|
||||||
|
.es-adaptive table, .es-left, .es-right { width:100%!important }
|
||||||
|
.es-content table, .es-header table, .es-footer table, .es-content, .es-footer, .es-header { width:100%!important; max-width:600px!important }
|
||||||
|
.es-adapt-td { display:block!important; width:100%!important }
|
||||||
|
.adapt-img { width:100%!important; height:auto!important }
|
||||||
|
.es-m-p0 { padding:0px!important }
|
||||||
|
.es-m-p0r { padding-right:0px!important }
|
||||||
|
.es-m-p0l { padding-left:0px!important }
|
||||||
|
.es-m-p0t { padding-top:0px!important }
|
||||||
|
.es-m-p0b { padding-bottom:0!important }
|
||||||
|
.es-m-p20b { padding-bottom:20px!important }
|
||||||
|
.es-mobile-hidden, .es-hidden { display:none!important }
|
||||||
|
tr.es-desk-hidden, td.es-desk-hidden, table.es-desk-hidden { width:auto!important; overflow:visible!important; float:none!important; max-height:inherit!important; line-height:inherit!important }
|
||||||
|
tr.es-desk-hidden { display:table-row!important }
|
||||||
|
table.es-desk-hidden { display:table!important }
|
||||||
|
td.es-desk-menu-hidden { display:table-cell!important }
|
||||||
|
.es-menu td { width:1%!important }
|
||||||
|
table.es-table-not-adapt, .esd-block-html table { width:auto!important }
|
||||||
|
table.es-social { display:inline-block!important }
|
||||||
|
table.es-social td { display:inline-block!important }
|
||||||
|
.es-desk-hidden { display:table-row!important; width:auto!important; overflow:visible!important; max-height:inherit!important }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="width:100%;font-family:montserrat, roboto;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;padding:0;Margin:0">
|
||||||
|
<div class="es-wrapper-color" style="background-color:#FFFFFF">
|
||||||
|
<!--[if gte mso 9]>
|
||||||
|
<v:background xmlns:v="urn:schemas-microsoft-com:vml" fill="t">
|
||||||
|
<v:fill type="tile" color="#ffffff"></v:fill>
|
||||||
|
</v:background>
|
||||||
|
<![endif]-->
|
||||||
|
<table class="es-wrapper" width="100%" cellspacing="0" cellpadding="0" style="mso-table-lspace:0pt;mso-table-rspace:0pt;border-collapse:collapse;border-spacing:0px;padding:0;Margin:0;width:100%;height:100%;background-repeat:repeat;background-position:center top;background-color:#FFFFFF">
|
||||||
|
<tr>
|
||||||
|
<td valign="top" style="padding:0;Margin:0">
|
||||||
|
<table cellpadding="0" cellspacing="0" class="es-content" align="center" style="mso-table-lspace:0pt;mso-table-rspace:0pt;border-collapse:collapse;border-spacing:0px;table-layout:fixed !important;width:100%">
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="padding:0;Margin:0">
|
||||||
|
<table bgcolor="#ffffff" class="es-content-body" align="center" cellpadding="0" cellspacing="0" style="mso-table-lspace:0pt;mso-table-rspace:0pt;border-collapse:collapse;border-spacing:0px;background-color:#FFFFFF;border-radius:20px 20px 0 0;width:600px">
|
||||||
|
<tr>
|
||||||
|
<td align="left" style="padding:0;Margin:0;padding-top:20px;padding-left:40px;padding-right:40px;border-radius:8px 8px 0px 0px">
|
||||||
|
<table cellpadding="0" cellspacing="0" width="100%" style="mso-table-lspace:0pt;mso-table-rspace:0pt;border-collapse:collapse;border-spacing:0px">
|
||||||
|
<tr>
|
||||||
|
<td align="center" valign="top" style="padding:0;Margin:0;width:520px">
|
||||||
|
<table cellpadding="0" cellspacing="0" width="100%" bgcolor="#fafafa" style="mso-table-lspace:0pt;mso-table-rspace:0pt;border-collapse:separate;border-spacing:0px;background-color:#fafafa;border-radius:10px" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<td align="left" style="padding:20px;Margin:0">
|
||||||
|
<h3 style="Margin:0;line-height:34px;mso-line-height-rule:exactly;font-family:montserrat, roboto;font-size:28px;font-style:normal;font-weight:bold;color:#2D3142">
|
||||||
|
{{.Greeting}}
|
||||||
|
</h3>
|
||||||
|
<p style="Margin:0;-webkit-text-size-adjust:none;-ms-text-size-adjust:none;mso-line-height-rule:exactly;font-family:montserrat, roboto;line-height:27px;color:#2D3142;font-size:18px"><br></p>
|
||||||
|
<p style="Margin:0;-webkit-text-size-adjust:none;-ms-text-size-adjust:none;mso-line-height-rule:exactly;font-family:montserrat, roboto;line-height:27px;color:#2D3142;font-size:18px">
|
||||||
|
{{.Content}}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table cellpadding="0" cellspacing="0" class="es-content" align="center" style="mso-table-lspace:0pt;mso-table-rspace:0pt;border-collapse:collapse;border-spacing:0px;table-layout:fixed !important;width:100%">
|
||||||
|
{{.ButtonBlock}}
|
||||||
|
</table>
|
||||||
|
<table cellpadding="0" cellspacing="0" class="es-footer" align="center" style="mso-table-lspace:0pt;mso-table-rspace:0pt;border-collapse:collapse;border-spacing:0px;table-layout:fixed !important;width:100%;background-color:transparent;background-repeat:repeat;background-position:center top">
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="padding:0;Margin:0">
|
||||||
|
<table bgcolor="#bcb8b1" class="es-footer-body" align="center" cellpadding="0" cellspacing="0" style="mso-table-lspace:0pt;mso-table-rspace:0pt;border-collapse:collapse;border-spacing:0px;background-color:#FFFFFF;width:600px">
|
||||||
|
<tr>
|
||||||
|
<td align="left" style="Margin:0;padding-left:20px;padding-right:20px;padding-bottom:30px;padding-top:40px">
|
||||||
|
<!--[if mso]><table style="width:560px" cellpadding="0" cellspacing="0"><tr><td style="width:82px" valign="top"><![endif]-->
|
||||||
|
<table cellpadding="0" cellspacing="0" align="left" class="es-left" style="mso-table-lspace:0pt;mso-table-rspace:0pt;border-collapse:collapse;border-spacing:0px;float:left">
|
||||||
|
<tr>
|
||||||
|
<td align="left" class="es-m-p20b" style="padding:0;Margin:0;width:82px">
|
||||||
|
<table cellpadding="0" cellspacing="0" width="100%" role="presentation" style="mso-table-lspace:0pt;mso-table-rspace:0pt;border-collapse:collapse;border-spacing:0px">
|
||||||
|
<tr>
|
||||||
|
<td
|
||||||
|
align="center"
|
||||||
|
style="padding:0;Margin:0;padding-left:20px;font-size:0px">
|
||||||
|
<img class="adapt-img"
|
||||||
|
src="{{.LogoLink}}"
|
||||||
|
alt
|
||||||
|
style="display:block;border:0;outline:none;text-decoration:none;-ms-interpolation-mode:bicubic" width="62"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<!--[if mso]></td><td style="width:20px"></td><td style="width:458px" valign="top"><![endif]-->
|
||||||
|
<table cellpadding="0" cellspacing="0" class="es-right" align="right" style="mso-table-lspace:0pt;mso-table-rspace:0pt;border-collapse:collapse;border-spacing:0px;float:right">
|
||||||
|
<tr>
|
||||||
|
<td align="left" style="padding:0;Margin:0;width:458px">
|
||||||
|
<table cellpadding="0" cellspacing="0" width="100%" role="presentation" style="mso-table-lspace:0pt;mso-table-rspace:0pt;border-collapse:collapse;border-spacing:0px">
|
||||||
|
<tr>
|
||||||
|
<td align="left" style="padding:0;Margin:0">
|
||||||
|
<p style="Margin:0;-webkit-text-size-adjust:none;-ms-text-size-adjust:none;mso-line-height-rule:exactly;font-family:montserrat, roboto;line-height:20px;color:#2D3142;font-size:13px">
|
||||||
|
<a target="_blank" style="-webkit-text-size-adjust:none;-ms-text-size-adjust:none;mso-line-height-rule:exactly;text-decoration:none;color:#2D3142;font-size:14px" href=""></a>
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
style="-webkit-text-size-adjust:none;-ms-text-size-adjust:none;mso-line-height-rule:exactly;text-decoration:none;color:#2D3142;font-size:14px"
|
||||||
|
href="{{.PolicyLink}}"
|
||||||
|
>
|
||||||
|
{{.Privacy}}
|
||||||
|
</a>
|
||||||
|
{{.UnsubscribeBlock}}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="left" style="padding:0;Margin:0">
|
||||||
|
<p style="Margin:0;-webkit-text-size-adjust:none;-ms-text-size-adjust:none;mso-line-height-rule:exactly;font-family:montserrat, roboto;line-height:20px;color:#2D3142;font-size:13px">
|
||||||
|
{{.ServiceOwner}}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="left" style="padding:0;Margin:0">
|
||||||
|
<p style="Margin:0;-webkit-text-size-adjust:none;-ms-text-size-adjust:none;mso-line-height-rule:exactly;font-family:montserrat, roboto;line-height:20px;color:#2D3142;font-size:13px">
|
||||||
|
{{.OwnerAddress}}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="left" style="padding:0;Margin:0">
|
||||||
|
<p style="Margin:0;-webkit-text-size-adjust:none;-ms-text-size-adjust:none;mso-line-height-rule:exactly;font-family:montserrat, roboto;line-height:20px;color:#2D3142;font-size:13px">
|
||||||
|
{{.OwnerPhone}}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<!--[if mso]></td></tr></table><![endif]-->
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>`))
|
||||||
|
)
|
||||||
|
|
||||||
|
func renderTemplate(tpl *template.Template, data any) (string, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := tpl.Execute(&buf, data); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return buf.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderUnsubscribeBlock(data any) (string, error) {
|
||||||
|
return renderTemplate(unsubscribeBlockTpl, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderButtonBlock(data any) (string, error) {
|
||||||
|
return renderTemplate(buttonBlockTpl, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderOneButtonEmail(data any) (string, error) {
|
||||||
|
return renderTemplate(oneButtonEmailTpl, data)
|
||||||
|
}
|
||||||
@@ -43,13 +43,13 @@ require (
|
|||||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.47.0 // indirect
|
github.com/nats-io/nats.go v1.47.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.11 // indirect
|
github.com/nats-io/nkeys v0.4.12 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.4 // indirect
|
github.com/prometheus/common v0.67.4 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.19.2 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.1.2 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
@@ -59,5 +59,5 @@ require (
|
|||||||
golang.org/x/sync v0.18.0 // indirect
|
golang.org/x/sync v0.18.0 // indirect
|
||||||
golang.org/x/sys v0.38.0 // indirect
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
golang.org/x/text v0.31.0 // indirect
|
golang.org/x/text v0.31.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -97,8 +97,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
|||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.47.0 h1:YQdADw6J/UfGUd2Oy6tn4Hq6YHxCaJrVKayxxFqYrgM=
|
github.com/nats-io/nats.go v1.47.0 h1:YQdADw6J/UfGUd2Oy6tn4Hq6YHxCaJrVKayxxFqYrgM=
|
||||||
github.com/nats-io/nats.go v1.47.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.47.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
||||||
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
|
github.com/nats-io/nkeys v0.4.12 h1:nssm7JKOG9/x4J8II47VWCL1Ds29avyiQDRn0ckMvDc=
|
||||||
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
|
github.com/nats-io/nkeys v0.4.12/go.mod h1:MT59A1HYcjIcyQDJStTfaOY6vhy9XTUjOFo+SVsvpBg=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
@@ -144,8 +144,8 @@ github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9R
|
|||||||
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
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 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=
|
||||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=
|
||||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||||
@@ -215,8 +215,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=
|
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 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
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-20251124214823-79d6a2a48846 h1:Wgl1rcDNThT+Zn47YyCXOXyX/COgMTIdhJ717F0l4xk=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
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/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||||
|
|||||||
@@ -47,6 +47,12 @@ func (n *Enforcer) Enforce(
|
|||||||
permissionRef, accountRef, organizationRef, objectRef primitive.ObjectID,
|
permissionRef, accountRef, organizationRef, objectRef primitive.ObjectID,
|
||||||
action model.Action,
|
action model.Action,
|
||||||
) (bool, error) {
|
) (bool, error) {
|
||||||
|
if organizationRef.IsZero() {
|
||||||
|
n.logger.Warn("Missing organization context", mzap.ObjRef("account_ref", accountRef),
|
||||||
|
mzap.ObjRef("organization_ref", organizationRef), mzap.ObjRef("permission_ref", permissionRef),
|
||||||
|
mzap.ObjRef("object", objectRef), zap.String("action", string(action)))
|
||||||
|
return false, merrors.InvalidArgument("organization context missing", "organizationRef")
|
||||||
|
}
|
||||||
roleAssignments, err := n.rdb.Roles(ctx, accountRef, organizationRef)
|
roleAssignments, err := n.rdb.Roles(ctx, accountRef, organizationRef)
|
||||||
if errors.Is(err, merrors.ErrNoData) {
|
if errors.Is(err, merrors.ErrNoData) {
|
||||||
n.logger.Debug("No roles defined for account", mzap.ObjRef("account_ref", accountRef))
|
n.logger.Debug("No roles defined for account", mzap.ObjRef("account_ref", accountRef))
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ package mongo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/tech/sendico/pkg/auth"
|
"github.com/tech/sendico/pkg/auth"
|
||||||
@@ -28,7 +31,6 @@ import (
|
|||||||
"github.com/tech/sendico/pkg/mservice"
|
"github.com/tech/sendico/pkg/mservice"
|
||||||
mutil "github.com/tech/sendico/pkg/mutil/config"
|
mutil "github.com/tech/sendico/pkg/mutil/config"
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
"go.mongodb.org/mongo-driver/mongo/options"
|
|
||||||
"go.mongodb.org/mongo-driver/mongo/readpref"
|
"go.mongodb.org/mongo-driver/mongo/readpref"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
@@ -44,6 +46,13 @@ type Config struct {
|
|||||||
DatabaseEnv *string `mapstructure:"database_env"`
|
DatabaseEnv *string `mapstructure:"database_env"`
|
||||||
Host *string `mapstructure:"host"`
|
Host *string `mapstructure:"host"`
|
||||||
HostEnv *string `mapstructure:"host_env"`
|
HostEnv *string `mapstructure:"host_env"`
|
||||||
|
Hosts []string `mapstructure:"hosts,omitempty"`
|
||||||
|
HostsEnvPrefix *string `mapstructure:"hosts_env_prefix,omitempty"`
|
||||||
|
HostsEnvPrefixEnv *string `mapstructure:"hosts_env_prefix_env,omitempty"`
|
||||||
|
PortsEnvPrefix *string `mapstructure:"ports_env_prefix,omitempty"`
|
||||||
|
PortsEnvPrefixEnv *string `mapstructure:"ports_env_prefix_env,omitempty"`
|
||||||
|
URI *string `mapstructure:"uri,omitempty"`
|
||||||
|
URIEnv *string `mapstructure:"uri_env,omitempty"`
|
||||||
AuthSource *string `mapstructure:"auth_source,omitempty"`
|
AuthSource *string `mapstructure:"auth_source,omitempty"`
|
||||||
AuthSourceEnv *string `mapstructure:"auth_source_env,omitempty"`
|
AuthSourceEnv *string `mapstructure:"auth_source_env,omitempty"`
|
||||||
AuthMechanism *string `mapstructure:"auth_mechanism,omitempty"`
|
AuthMechanism *string `mapstructure:"auth_mechanism,omitempty"`
|
||||||
@@ -55,6 +64,7 @@ type Config struct {
|
|||||||
|
|
||||||
type DBSettings struct {
|
type DBSettings struct {
|
||||||
Host string
|
Host string
|
||||||
|
Hosts []string
|
||||||
Port string
|
Port string
|
||||||
User string
|
User string
|
||||||
Password string
|
Password string
|
||||||
@@ -62,6 +72,7 @@ type DBSettings struct {
|
|||||||
AuthSource string
|
AuthSource string
|
||||||
AuthMechanism string
|
AuthMechanism string
|
||||||
ReplicaSet string
|
ReplicaSet string
|
||||||
|
URI string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newProtectedDB[T any](
|
func newProtectedDB[T any](
|
||||||
@@ -87,6 +98,19 @@ func Config2DBSettings(logger mlogger.Logger, config *Config) *DBSettings {
|
|||||||
p.AuthSource = mutil.GetConfigValue(logger, "auth_source", "auth_source_env", config.AuthSource, config.AuthSourceEnv)
|
p.AuthSource = mutil.GetConfigValue(logger, "auth_source", "auth_source_env", config.AuthSource, config.AuthSourceEnv)
|
||||||
p.AuthMechanism = mutil.GetConfigValue(logger, "auth_mechanism", "auth_mechanism_env", config.AuthMechanism, config.AuthMechanismEnv)
|
p.AuthMechanism = mutil.GetConfigValue(logger, "auth_mechanism", "auth_mechanism_env", config.AuthMechanism, config.AuthMechanismEnv)
|
||||||
p.ReplicaSet = mutil.GetConfigValue(logger, "replica_set", "replica_set_env", config.ReplicaSet, config.ReplicaSetEnv)
|
p.ReplicaSet = mutil.GetConfigValue(logger, "replica_set", "replica_set_env", config.ReplicaSet, config.ReplicaSetEnv)
|
||||||
|
p.URI = mutil.GetConfigValue(logger, "uri", "uri_env", config.URI, config.URIEnv)
|
||||||
|
|
||||||
|
hostPrefix := mutil.GetConfigValue(logger, "hosts_env_prefix", "hosts_env_prefix_env", config.HostsEnvPrefix, config.HostsEnvPrefixEnv)
|
||||||
|
portPrefix := mutil.GetConfigValue(logger, "ports_env_prefix", "ports_env_prefix_env", config.PortsEnvPrefix, config.PortsEnvPrefixEnv)
|
||||||
|
|
||||||
|
if hostPrefix == "" && p.ReplicaSet != "" {
|
||||||
|
hostPrefix = "MONGO_HOSTS_"
|
||||||
|
}
|
||||||
|
if portPrefix == "" && p.ReplicaSet != "" {
|
||||||
|
portPrefix = "MONGO_PORTS_"
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Hosts = collectReplicaHosts(config.Hosts, p.ReplicaSet, p.Port, hostPrefix, portPrefix)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,21 +125,19 @@ func decodeConfig(logger mlogger.Logger, settings model.SettingsT) (*Config, *DB
|
|||||||
}
|
}
|
||||||
|
|
||||||
func dialMongo(logger mlogger.Logger, dbSettings *DBSettings) (*mongo.Client, error) {
|
func dialMongo(logger mlogger.Logger, dbSettings *DBSettings) (*mongo.Client, error) {
|
||||||
cred := options.Credential{
|
opts := buildOptions(dbSettings)
|
||||||
AuthMechanism: dbSettings.AuthMechanism,
|
|
||||||
AuthSource: dbSettings.AuthSource,
|
|
||||||
Username: dbSettings.User,
|
|
||||||
Password: dbSettings.Password,
|
|
||||||
}
|
|
||||||
dbURI := buildURI(dbSettings)
|
|
||||||
|
|
||||||
client, err := mongo.Connect(context.Background(), options.Client().ApplyURI(dbURI).SetAuth(cred))
|
client, err := mongo.Connect(context.Background(), opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Unable to connect to database", zap.Error(err))
|
logger.Error("Unable to connect to database", zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info("Connected successfully", zap.String("uri", dbURI))
|
if dbSettings.URI != "" {
|
||||||
|
logger.Info("Connected successfully", zap.Bool("uri_provided", true))
|
||||||
|
} else {
|
||||||
|
logger.Info("Connected successfully", zap.Strings("hosts", opts.Hosts), zap.String("replica_set", dbSettings.ReplicaSet))
|
||||||
|
}
|
||||||
|
|
||||||
if err := client.Ping(context.Background(), readpref.Primary()); err != nil {
|
if err := client.Ping(context.Background(), readpref.Primary()); err != nil {
|
||||||
logger.Error("Unable to ping database", zap.Error(err))
|
logger.Error("Unable to ping database", zap.Error(err))
|
||||||
@@ -199,6 +221,70 @@ func (db *DB) TransactionFactory() transaction.Factory {
|
|||||||
return transactionimp.CreateFactory(db.client)
|
return transactionimp.CreateFactory(db.client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func collectReplicaHosts(configuredHosts []string, replicaSet, defaultPort, hostPrefix, portPrefix string) []string {
|
||||||
|
normalize := func(host, port string) string {
|
||||||
|
host = strings.TrimSpace(host)
|
||||||
|
port = strings.TrimSpace(port)
|
||||||
|
if host == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
// If host already has a port, keep it; otherwise apply provided/default port.
|
||||||
|
if _, _, err := net.SplitHostPort(host); err == nil {
|
||||||
|
return host
|
||||||
|
}
|
||||||
|
if port != "" {
|
||||||
|
return net.JoinHostPort(host, port)
|
||||||
|
}
|
||||||
|
if defaultPort != "" {
|
||||||
|
return net.JoinHostPort(host, defaultPort)
|
||||||
|
}
|
||||||
|
return host
|
||||||
|
}
|
||||||
|
|
||||||
|
appendHost := func(list []string, host, port string) []string {
|
||||||
|
if normalized := normalize(host, port); normalized != "" {
|
||||||
|
return append(list, normalized)
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
var hosts []string
|
||||||
|
for _, h := range configuredHosts {
|
||||||
|
hosts = appendHost(hosts, h, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
if replicaSet == "" || hostPrefix == "" {
|
||||||
|
return hosts
|
||||||
|
}
|
||||||
|
|
||||||
|
index := 0
|
||||||
|
for {
|
||||||
|
hostEnv := os.Getenv(fmt.Sprintf("%s%d", hostPrefix, index))
|
||||||
|
portEnv := ""
|
||||||
|
if portPrefix != "" {
|
||||||
|
portEnv = os.Getenv(fmt.Sprintf("%s%d", portPrefix, index))
|
||||||
|
}
|
||||||
|
|
||||||
|
if hostEnv == "" && index == 0 {
|
||||||
|
hostEnv = os.Getenv(fmt.Sprintf("%s%d", hostPrefix, 1))
|
||||||
|
if portPrefix != "" {
|
||||||
|
portEnv = os.Getenv(fmt.Sprintf("%s%d", portPrefix, 1))
|
||||||
|
}
|
||||||
|
if hostEnv == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
index = 1
|
||||||
|
} else if hostEnv == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
hosts = appendHost(hosts, hostEnv, portEnv)
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
|
||||||
|
return hosts
|
||||||
|
}
|
||||||
|
|
||||||
func (db *DB) Permissions() auth.Provider {
|
func (db *DB) Permissions() auth.Provider {
|
||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,49 @@
|
|||||||
package mongo
|
package mongo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
)
|
)
|
||||||
|
|
||||||
func buildURI(s *DBSettings) string {
|
func buildOptions(s *DBSettings) *options.ClientOptions {
|
||||||
u := &url.URL{
|
opts := options.Client()
|
||||||
Scheme: "mongodb",
|
|
||||||
Host: s.Host,
|
if s.URI != "" {
|
||||||
Path: "/" + url.PathEscape(s.Database), // /my%20db
|
return opts.ApplyURI(s.URI)
|
||||||
|
}
|
||||||
|
|
||||||
|
hosts := make([]string, 0, len(s.Hosts)+1)
|
||||||
|
for _, h := range s.Hosts {
|
||||||
|
if trimmed := strings.TrimSpace(h); trimmed != "" {
|
||||||
|
hosts = append(hosts, trimmed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(hosts) == 0 && s.Host != "" {
|
||||||
|
host := s.Host
|
||||||
|
if _, _, err := net.SplitHostPort(host); err != nil && s.Port != "" {
|
||||||
|
host = net.JoinHostPort(host, s.Port)
|
||||||
|
}
|
||||||
|
hosts = append(hosts, host)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(hosts) > 0 {
|
||||||
|
opts.SetHosts(hosts)
|
||||||
}
|
}
|
||||||
|
|
||||||
q := url.Values{}
|
|
||||||
if s.ReplicaSet != "" {
|
if s.ReplicaSet != "" {
|
||||||
q.Set("replicaSet", s.ReplicaSet)
|
opts.SetReplicaSet(s.ReplicaSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
u.RawQuery = q.Encode()
|
cred := options.Credential{
|
||||||
|
AuthMechanism: s.AuthMechanism,
|
||||||
|
AuthSource: s.AuthSource,
|
||||||
|
Username: s.User,
|
||||||
|
Password: s.Password,
|
||||||
|
}
|
||||||
|
opts.SetAuth(cred)
|
||||||
|
|
||||||
return u.String()
|
return opts
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ require (
|
|||||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||||
github.com/morikuni/aec v1.0.0 // indirect
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.11 // indirect
|
github.com/nats-io/nkeys v0.4.12 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||||
@@ -76,7 +76,7 @@ require (
|
|||||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||||
github.com/tklauser/numcpus v0.10.0 // indirect
|
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.1.2 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
@@ -93,6 +93,6 @@ require (
|
|||||||
golang.org/x/sys v0.38.0 // indirect
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
golang.org/x/text v0.31.0 // indirect
|
golang.org/x/text v0.31.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -108,8 +108,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
|||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.47.0 h1:YQdADw6J/UfGUd2Oy6tn4Hq6YHxCaJrVKayxxFqYrgM=
|
github.com/nats-io/nats.go v1.47.0 h1:YQdADw6J/UfGUd2Oy6tn4Hq6YHxCaJrVKayxxFqYrgM=
|
||||||
github.com/nats-io/nats.go v1.47.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.47.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
||||||
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
|
github.com/nats-io/nkeys v0.4.12 h1:nssm7JKOG9/x4J8II47VWCL1Ds29avyiQDRn0ckMvDc=
|
||||||
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
|
github.com/nats-io/nkeys v0.4.12/go.mod h1:MT59A1HYcjIcyQDJStTfaOY6vhy9XTUjOFo+SVsvpBg=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
@@ -156,8 +156,8 @@ github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfj
|
|||||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
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/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=
|
||||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=
|
||||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||||
@@ -269,8 +269,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
|||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
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 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
|
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-20251124214823-79d6a2a48846 h1:Wgl1rcDNThT+Zn47YyCXOXyX/COgMTIdhJ717F0l4xk=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
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/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package model
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/pkg/db/storable"
|
||||||
"github.com/tech/sendico/pkg/mservice"
|
"github.com/tech/sendico/pkg/mservice"
|
||||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
)
|
)
|
||||||
@@ -16,8 +17,9 @@ const (
|
|||||||
|
|
||||||
// ConfirmationCode stores verification codes for operations like login or payouts.
|
// ConfirmationCode stores verification codes for operations like login or payouts.
|
||||||
type ConfirmationCode struct {
|
type ConfirmationCode struct {
|
||||||
AccountBoundBase `bson:",inline" json:",inline"`
|
storable.Base `bson:",inline" json:",inline"`
|
||||||
|
|
||||||
|
AccountRef primitive.ObjectID `bson:"accountRef" json:"accountRef"`
|
||||||
Destination string `bson:"destination" json:"destination"`
|
Destination string `bson:"destination" json:"destination"`
|
||||||
Target ConfirmationTarget `bson:"target" json:"target"`
|
Target ConfirmationTarget `bson:"target" json:"target"`
|
||||||
CodeHash []byte `bson:"codeHash" json:"-"`
|
CodeHash []byte `bson:"codeHash" json:"-"`
|
||||||
@@ -34,10 +36,3 @@ type ConfirmationCode struct {
|
|||||||
func (c *ConfirmationCode) Collection() string {
|
func (c *ConfirmationCode) Collection() string {
|
||||||
return mservice.Confirmations
|
return mservice.Confirmations
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfirmationCode(accountRef primitive.ObjectID) *ConfirmationCode {
|
|
||||||
cc := &ConfirmationCode{}
|
|
||||||
cc.SetID(primitive.NewObjectID())
|
|
||||||
cc.AccountRef = &accountRef
|
|
||||||
return cc
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ type UserDataBase struct {
|
|||||||
|
|
||||||
type LoginData struct {
|
type LoginData struct {
|
||||||
UserDataBase `bson:",inline" json:",inline"`
|
UserDataBase `bson:",inline" json:",inline"`
|
||||||
Password string `json:"password"`
|
Password string ` bson:"-" json:"password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccountData struct {
|
type AccountData struct {
|
||||||
@@ -21,6 +21,7 @@ func (ad *AccountData) ToAccount() *Account {
|
|||||||
AccountPublic: AccountPublic{
|
AccountPublic: AccountPublic{
|
||||||
AccountBase: AccountBase{
|
AccountBase: AccountBase{
|
||||||
Describable: ad.Describable,
|
Describable: ad.Describable,
|
||||||
|
LastName: ad.LastName,
|
||||||
},
|
},
|
||||||
UserDataBase: ad.UserDataBase,
|
UserDataBase: ad.UserDataBase,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ const (
|
|||||||
Roles Type = "roles" // Represents roles in access control
|
Roles Type = "roles" // Represents roles in access control
|
||||||
Storage Type = "storage" // Represents statuses of tasks or projects
|
Storage Type = "storage" // Represents statuses of tasks or projects
|
||||||
Tenants Type = "tenants" // Represents tenants managed in the system
|
Tenants Type = "tenants" // Represents tenants managed in the system
|
||||||
|
Wallets Type = "wallets" // Represents workflows for tasks or projects
|
||||||
Workflows Type = "workflows" // Represents workflows for tasks or projects
|
Workflows Type = "workflows" // Represents workflows for tasks or projects
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,9 @@ service LedgerService {
|
|||||||
rpc GetBalance (GetBalanceRequest) returns (BalanceResponse);
|
rpc GetBalance (GetBalanceRequest) returns (BalanceResponse);
|
||||||
rpc GetJournalEntry (GetEntryRequest) returns (JournalEntryResponse);
|
rpc GetJournalEntry (GetEntryRequest) returns (JournalEntryResponse);
|
||||||
rpc GetStatement (GetStatementRequest) returns (StatementResponse);
|
rpc GetStatement (GetStatementRequest) returns (StatementResponse);
|
||||||
|
|
||||||
|
// Lists ledger accounts for an organization.
|
||||||
|
rpc ListAccounts (ListAccountsRequest) returns (ListAccountsResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
message CreateAccountRequest {
|
message CreateAccountRequest {
|
||||||
@@ -192,3 +195,11 @@ message StatementResponse {
|
|||||||
repeated JournalEntryResponse entries = 1;
|
repeated JournalEntryResponse entries = 1;
|
||||||
string next_cursor = 2;
|
string next_cursor = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message ListAccountsRequest {
|
||||||
|
string organization_ref = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListAccountsResponse {
|
||||||
|
repeated LedgerAccount accounts = 1;
|
||||||
|
}
|
||||||
|
|||||||
@@ -85,6 +85,12 @@ api:
|
|||||||
chain: ARBITRUM_ONE
|
chain: ARBITRUM_ONE
|
||||||
token_symbol: USDT
|
token_symbol: USDT
|
||||||
contract_address: ""
|
contract_address: ""
|
||||||
|
ledger:
|
||||||
|
address: sendico_ledger:50052
|
||||||
|
address_env: LEDGER_ADDRESS
|
||||||
|
dial_timeout_seconds: 5
|
||||||
|
call_timeout_seconds: 5
|
||||||
|
insecure: true
|
||||||
|
|
||||||
app:
|
app:
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,15 @@ go 1.25.3
|
|||||||
|
|
||||||
replace github.com/tech/sendico/pkg => ../pkg
|
replace github.com/tech/sendico/pkg => ../pkg
|
||||||
|
|
||||||
|
replace github.com/tech/sendico/ledger => ../ledger
|
||||||
|
|
||||||
replace github.com/tech/sendico/chain/gateway => ../chain/gateway
|
replace github.com/tech/sendico/chain/gateway => ../chain/gateway
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aws/aws-sdk-go-v2 v1.40.0
|
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/config v1.32.2
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.0
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.2
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.92.0
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.92.1
|
||||||
github.com/go-chi/chi/v5 v5.2.3
|
github.com/go-chi/chi/v5 v5.2.3
|
||||||
github.com/go-chi/cors v1.2.2
|
github.com/go-chi/cors v1.2.2
|
||||||
github.com/go-chi/jwtauth/v5 v5.3.3
|
github.com/go-chi/jwtauth/v5 v5.3.3
|
||||||
@@ -19,12 +21,14 @@ require (
|
|||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/tech/sendico/chain/gateway v0.1.0
|
github.com/tech/sendico/chain/gateway v0.1.0
|
||||||
|
github.com/tech/sendico/ledger v0.0.0-00010101000000-000000000000
|
||||||
github.com/tech/sendico/pkg v0.1.0
|
github.com/tech/sendico/pkg v0.1.0
|
||||||
github.com/testcontainers/testcontainers-go v0.33.0
|
github.com/testcontainers/testcontainers-go v0.33.0
|
||||||
github.com/testcontainers/testcontainers-go/modules/mongodb v0.33.0
|
github.com/testcontainers/testcontainers-go/modules/mongodb v0.33.0
|
||||||
go.mongodb.org/mongo-driver v1.17.6
|
go.mongodb.org/mongo-driver v1.17.6
|
||||||
go.uber.org/zap v1.27.1
|
go.uber.org/zap v1.27.1
|
||||||
golang.org/x/net v0.47.0
|
golang.org/x/net v0.47.0
|
||||||
|
google.golang.org/protobuf v1.36.10
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
moul.io/chizap v1.0.3
|
moul.io/chizap v1.0.3
|
||||||
)
|
)
|
||||||
@@ -49,10 +53,10 @@ require (
|
|||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.5 // 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/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/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/signin v1.0.2 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.4 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.5 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.8 // indirect
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.10 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.1 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sts v1.41.2 // indirect
|
||||||
github.com/aws/smithy-go v1.23.2 // indirect
|
github.com/aws/smithy-go v1.23.2 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/casbin/mongodb-adapter/v3 v3.7.0 // indirect
|
github.com/casbin/mongodb-adapter/v3 v3.7.0 // indirect
|
||||||
@@ -96,7 +100,7 @@ require (
|
|||||||
github.com/morikuni/aec v1.0.0 // indirect
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nats-io/nats.go v1.47.0 // indirect
|
github.com/nats-io/nats.go v1.47.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.11 // indirect
|
github.com/nats-io/nkeys v0.4.12 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||||
@@ -114,7 +118,7 @@ require (
|
|||||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.1.2 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
@@ -131,7 +135,6 @@ require (
|
|||||||
golang.org/x/sync v0.18.0 // indirect
|
golang.org/x/sync v0.18.0 // indirect
|
||||||
golang.org/x/sys v0.38.0 // indirect
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
golang.org/x/text v0.31.0 // indirect
|
golang.org/x/text v0.31.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 // indirect
|
||||||
google.golang.org/grpc v1.77.0 // indirect
|
google.golang.org/grpc v1.77.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.10 // indirect
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ github.com/aws/aws-sdk-go-v2 v1.40.0 h1:/WMUA0kjhZExjOQN2z3oLALDREea1A7TobfuiBrK
|
|||||||
github.com/aws/aws-sdk-go-v2 v1.40.0/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE=
|
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 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/aws/protocol/eventstream v1.7.3/go.mod h1:xdCzcZEtnSTKVDOmUZs4l/j3pSV6rpo1WXl5ugNsL8Y=
|
||||||
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.2 h1:4liUsdEpUUPZs5WVapsJLx5NPmQhQdez7nYFcovrytk=
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.32.0/go.mod h1:pSRm/+D3TxBixGMXlgtX4+MPO9VNtEEtiFmNpxksoxw=
|
github.com/aws/aws-sdk-go-v2/config v1.32.2/go.mod h1:l0hs06IFz1eCT+jTacU/qZtC33nvcnLADAPL/XyrkZI=
|
||||||
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.2 h1:qZry8VUyTK4VIo5aEdUcBjPZHL2v4FyQ3QEOaWcFLu4=
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.0/go.mod h1:pHKPblrT7hqFGkNLxqoS3FlGoPrQg4hMIa+4asZzBfs=
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.2/go.mod h1:YUqm5a1/kBnoK+/NY5WEiMocZihKSo15/tJdmdXnM5g=
|
||||||
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 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/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 h1:PZHqQACxYb8mYgms4RZbhZG0a7dPW06xOjmaH0EJC/I=
|
||||||
@@ -32,16 +32,16 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14 h1:FIouAnCE
|
|||||||
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/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 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/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.1 h1:OgQy/+0+Kc3khtqiEOk23xQAglXi3Tj0y5doOxbi5tg=
|
||||||
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/s3 v1.92.1/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.2 h1:MxMBdKTYBjPQChlJhi4qlEueqB1p1KcbTEa7tD5aqPs=
|
||||||
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/signin v1.0.2/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.5 h1:ksUT5KtgpZd3SAiFJNJ0AFEJVva3gjBmN7eXUZjzUwQ=
|
||||||
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/sso v1.30.5/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.10 h1:GtsxyiF3Nd3JahRBJbxLCCdYW9ltGQYrFWg8XdkGDd8=
|
||||||
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/ssooidc v1.35.10/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.2 h1:a5UTtD4mHBU3t0o6aHQZFJTNKVfxFWfPX7J0Lr7G+uY=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.1/go.mod h1:6TxbXoDSgBQ225Qd8Q+MbxUxUh6TtNKwbRt/EPS9xso=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.41.2/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 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM=
|
||||||
github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
|
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=
|
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
@@ -177,8 +177,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
|||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nats-io/nats.go v1.47.0 h1:YQdADw6J/UfGUd2Oy6tn4Hq6YHxCaJrVKayxxFqYrgM=
|
github.com/nats-io/nats.go v1.47.0 h1:YQdADw6J/UfGUd2Oy6tn4Hq6YHxCaJrVKayxxFqYrgM=
|
||||||
github.com/nats-io/nats.go v1.47.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
github.com/nats-io/nats.go v1.47.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
||||||
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
|
github.com/nats-io/nkeys v0.4.12 h1:nssm7JKOG9/x4J8II47VWCL1Ds29avyiQDRn0ckMvDc=
|
||||||
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
|
github.com/nats-io/nkeys v0.4.12/go.mod h1:MT59A1HYcjIcyQDJStTfaOY6vhy9XTUjOFo+SVsvpBg=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
@@ -236,8 +236,8 @@ github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9R
|
|||||||
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
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 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=
|
||||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=
|
||||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||||
@@ -359,8 +359,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
|||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
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 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
|
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-20251124214823-79d6a2a48846 h1:Wgl1rcDNThT+Zn47YyCXOXyX/COgMTIdhJ717F0l4xk=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
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/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||||
|
|||||||
@@ -239,11 +239,6 @@ func (s *service) JoinOrganization(
|
|||||||
s.logger.Debug("Account is already a member", mzap.StorableRef(org), mzap.StorableRef(account))
|
s.logger.Debug("Account is already a member", mzap.StorableRef(org), mzap.StorableRef(account))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
org.Members = append(org.Members, account.ID)
|
|
||||||
if err := s.orgDB.Update(ctx, *account.GetID(), org); err != nil {
|
|
||||||
s.logger.Warn("Failed to update organization members list", zap.Error(err), mzap.StorableRef(account))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
role := &model.Role{
|
role := &model.Role{
|
||||||
DescriptionRef: roleDescID,
|
DescriptionRef: roleDescID,
|
||||||
@@ -251,7 +246,15 @@ func (s *service) JoinOrganization(
|
|||||||
AccountRef: account.ID,
|
AccountRef: account.ID,
|
||||||
}
|
}
|
||||||
if err := s.roleManager.Assign(ctx, role); err != nil {
|
if err := s.roleManager.Assign(ctx, role); err != nil {
|
||||||
s.logger.Warn("Failed to assign role to account", zap.Error(err), mzap.StorableRef(account))
|
s.logger.Warn("Failed to assign role to account", zap.Error(err), mzap.StorableRef(account),
|
||||||
|
mzap.StorableRef(org), mzap.ObjRef("role_description_ref", roleDescID))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
org.Members = append(org.Members, account.ID)
|
||||||
|
if err := s.orgDB.Update(ctx, *account.GetID(), org); err != nil {
|
||||||
|
s.logger.Warn("Failed to update organization members list",
|
||||||
|
zap.Error(err), mzap.StorableRef(account), mzap.StorableRef(org))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ type Config struct {
|
|||||||
Mw *mwa.Config `yaml:"middleware"`
|
Mw *mwa.Config `yaml:"middleware"`
|
||||||
Storage *fsc.Config `yaml:"storage"`
|
Storage *fsc.Config `yaml:"storage"`
|
||||||
ChainGateway *ChainGatewayConfig `yaml:"chain_gateway"`
|
ChainGateway *ChainGatewayConfig `yaml:"chain_gateway"`
|
||||||
|
Ledger *LedgerConfig `yaml:"ledger"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChainGatewayConfig struct {
|
type ChainGatewayConfig struct {
|
||||||
@@ -25,3 +26,11 @@ type ChainGatewayAssetConfig struct {
|
|||||||
TokenSymbol string `yaml:"token_symbol"`
|
TokenSymbol string `yaml:"token_symbol"`
|
||||||
ContractAddress string `yaml:"contract_address"`
|
ContractAddress string `yaml:"contract_address"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LedgerConfig struct {
|
||||||
|
Address string `yaml:"address"`
|
||||||
|
AddressEnv string `yaml:"address_env"`
|
||||||
|
DialTimeoutSeconds int `yaml:"dial_timeout_seconds"`
|
||||||
|
CallTimeoutSeconds int `yaml:"call_timeout_seconds"`
|
||||||
|
Insecure bool `yaml:"insecure"`
|
||||||
|
}
|
||||||
|
|||||||
106
api/server/interface/api/sresponse/ledger.go
Normal file
106
api/server/interface/api/sresponse/ledger.go
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
package sresponse
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/pkg/api/http/response"
|
||||||
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
|
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
||||||
|
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ledgerAccount struct {
|
||||||
|
LedgerAccountRef string `json:"ledgerAccountRef"`
|
||||||
|
OrganizationRef string `json:"organizationRef"`
|
||||||
|
AccountCode string `json:"accountCode"`
|
||||||
|
AccountType string `json:"accountType"`
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
AllowNegative bool `json:"allowNegative"`
|
||||||
|
IsSettlement bool `json:"isSettlement"`
|
||||||
|
Metadata map[string]string `json:"metadata,omitempty"`
|
||||||
|
CreatedAt time.Time `json:"createdAt,omitempty"`
|
||||||
|
UpdatedAt time.Time `json:"updatedAt,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ledgerAccountsResponse struct {
|
||||||
|
authResponse `json:",inline"`
|
||||||
|
Accounts []ledgerAccount `json:"accounts"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ledgerMoney struct {
|
||||||
|
Amount string `json:"amount"`
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ledgerBalance struct {
|
||||||
|
LedgerAccountRef string `json:"ledgerAccountRef"`
|
||||||
|
Balance *ledgerMoney `json:"balance,omitempty"`
|
||||||
|
Version int64 `json:"version"`
|
||||||
|
LastUpdated time.Time `json:"lastUpdated,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ledgerBalanceResponse struct {
|
||||||
|
authResponse `json:",inline"`
|
||||||
|
Balance ledgerBalance `json:"balance"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func LedgerAccounts(logger mlogger.Logger, accounts []*ledgerv1.LedgerAccount, accessToken *TokenData) http.HandlerFunc {
|
||||||
|
dto := make([]ledgerAccount, 0, len(accounts))
|
||||||
|
for _, acc := range accounts {
|
||||||
|
dto = append(dto, toLedgerAccount(acc))
|
||||||
|
}
|
||||||
|
return response.Ok(logger, ledgerAccountsResponse{
|
||||||
|
Accounts: dto,
|
||||||
|
authResponse: authResponse{AccessToken: *accessToken},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func LedgerBalance(logger mlogger.Logger, resp *ledgerv1.BalanceResponse, accessToken *TokenData) http.HandlerFunc {
|
||||||
|
return response.Ok(logger, ledgerBalanceResponse{
|
||||||
|
Balance: toLedgerBalance(resp),
|
||||||
|
authResponse: authResponse{AccessToken: *accessToken},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func toLedgerAccount(acc *ledgerv1.LedgerAccount) ledgerAccount {
|
||||||
|
if acc == nil {
|
||||||
|
return ledgerAccount{}
|
||||||
|
}
|
||||||
|
return ledgerAccount{
|
||||||
|
LedgerAccountRef: acc.GetLedgerAccountRef(),
|
||||||
|
OrganizationRef: acc.GetOrganizationRef(),
|
||||||
|
AccountCode: acc.GetAccountCode(),
|
||||||
|
AccountType: acc.GetAccountType().String(),
|
||||||
|
Currency: acc.GetCurrency(),
|
||||||
|
Status: acc.GetStatus().String(),
|
||||||
|
AllowNegative: acc.GetAllowNegative(),
|
||||||
|
IsSettlement: acc.GetIsSettlement(),
|
||||||
|
Metadata: acc.GetMetadata(),
|
||||||
|
CreatedAt: acc.GetCreatedAt().AsTime(),
|
||||||
|
UpdatedAt: acc.GetUpdatedAt().AsTime(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toLedgerBalance(resp *ledgerv1.BalanceResponse) ledgerBalance {
|
||||||
|
if resp == nil {
|
||||||
|
return ledgerBalance{}
|
||||||
|
}
|
||||||
|
return ledgerBalance{
|
||||||
|
LedgerAccountRef: resp.GetLedgerAccountRef(),
|
||||||
|
Balance: toLedgerMoney(resp.GetBalance()),
|
||||||
|
Version: resp.GetVersion(),
|
||||||
|
LastUpdated: resp.GetLastUpdated().AsTime(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toLedgerMoney(m *moneyv1.Money) *ledgerMoney {
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &ledgerMoney{
|
||||||
|
Amount: m.GetAmount(),
|
||||||
|
Currency: m.GetCurrency(),
|
||||||
|
}
|
||||||
|
}
|
||||||
132
api/server/interface/api/sresponse/wallet.go
Normal file
132
api/server/interface/api/sresponse/wallet.go
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
package sresponse
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/pkg/api/http/response"
|
||||||
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
|
gatewayv1 "github.com/tech/sendico/pkg/proto/chain/gateway/v1"
|
||||||
|
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
||||||
|
paginationv1 "github.com/tech/sendico/pkg/proto/common/pagination/v1"
|
||||||
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type walletAsset struct {
|
||||||
|
Chain string `json:"chain"`
|
||||||
|
TokenSymbol string `json:"tokenSymbol"`
|
||||||
|
ContractAddress string `json:"contractAddress"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type wallet struct {
|
||||||
|
WalletRef string `json:"walletRef"`
|
||||||
|
OrganizationRef string `json:"organizationRef"`
|
||||||
|
OwnerRef string `json:"ownerRef"`
|
||||||
|
Asset walletAsset `json:"asset"`
|
||||||
|
DepositAddress string `json:"depositAddress"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Metadata map[string]string `json:"metadata,omitempty"`
|
||||||
|
CreatedAt string `json:"createdAt,omitempty"`
|
||||||
|
UpdatedAt string `json:"updatedAt,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type walletsResponse struct {
|
||||||
|
authResponse `json:",inline"`
|
||||||
|
Wallets []wallet `json:"wallets"`
|
||||||
|
Page *paginationv1.CursorPageResponse `json:"page,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type walletMoney struct {
|
||||||
|
Amount string `json:"amount"`
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type walletBalance struct {
|
||||||
|
Available *walletMoney `json:"available,omitempty"`
|
||||||
|
PendingInbound *walletMoney `json:"pendingInbound,omitempty"`
|
||||||
|
PendingOutbound *walletMoney `json:"pendingOutbound,omitempty"`
|
||||||
|
CalculatedAt string `json:"calculatedAt,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type walletBalanceResponse struct {
|
||||||
|
authResponse `json:",inline"`
|
||||||
|
Balance walletBalance `json:"balance"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Wallets(logger mlogger.Logger, resp *gatewayv1.ListManagedWalletsResponse, accessToken *TokenData) http.HandlerFunc {
|
||||||
|
dto := walletsResponse{
|
||||||
|
Page: resp.GetPage(),
|
||||||
|
authResponse: authResponse{AccessToken: *accessToken},
|
||||||
|
}
|
||||||
|
dto.Wallets = make([]wallet, 0, len(resp.GetWallets()))
|
||||||
|
for _, w := range resp.GetWallets() {
|
||||||
|
dto.Wallets = append(dto.Wallets, toWallet(w))
|
||||||
|
}
|
||||||
|
return response.Ok(logger, dto)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WalletBalance(logger mlogger.Logger, bal *gatewayv1.WalletBalance, accessToken *TokenData) http.HandlerFunc {
|
||||||
|
return response.Ok(logger, walletBalanceResponse{
|
||||||
|
Balance: toWalletBalance(bal),
|
||||||
|
authResponse: authResponse{AccessToken: *accessToken},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func toWallet(w *gatewayv1.ManagedWallet) wallet {
|
||||||
|
if w == nil {
|
||||||
|
return wallet{}
|
||||||
|
}
|
||||||
|
asset := w.GetAsset()
|
||||||
|
chain := ""
|
||||||
|
token := ""
|
||||||
|
contract := ""
|
||||||
|
if asset != nil {
|
||||||
|
chain = asset.GetChain().String()
|
||||||
|
token = asset.GetTokenSymbol()
|
||||||
|
contract = asset.GetContractAddress()
|
||||||
|
}
|
||||||
|
return wallet{
|
||||||
|
WalletRef: w.GetWalletRef(),
|
||||||
|
OrganizationRef: w.GetOrganizationRef(),
|
||||||
|
OwnerRef: w.GetOwnerRef(),
|
||||||
|
Asset: walletAsset{
|
||||||
|
Chain: chain,
|
||||||
|
TokenSymbol: token,
|
||||||
|
ContractAddress: contract,
|
||||||
|
},
|
||||||
|
DepositAddress: w.GetDepositAddress(),
|
||||||
|
Status: w.GetStatus().String(),
|
||||||
|
Metadata: w.GetMetadata(),
|
||||||
|
CreatedAt: tsToString(w.GetCreatedAt()),
|
||||||
|
UpdatedAt: tsToString(w.GetUpdatedAt()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toWalletBalance(b *gatewayv1.WalletBalance) walletBalance {
|
||||||
|
if b == nil {
|
||||||
|
return walletBalance{}
|
||||||
|
}
|
||||||
|
return walletBalance{
|
||||||
|
Available: toMoney(b.GetAvailable()),
|
||||||
|
PendingInbound: toMoney(b.GetPendingInbound()),
|
||||||
|
PendingOutbound: toMoney(b.GetPendingOutbound()),
|
||||||
|
CalculatedAt: tsToString(b.GetCalculatedAt()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toMoney(m *moneyv1.Money) *walletMoney {
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &walletMoney{
|
||||||
|
Amount: m.GetAmount(),
|
||||||
|
Currency: m.GetCurrency(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func tsToString(ts *timestamppb.Timestamp) string {
|
||||||
|
if ts == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return ts.AsTime().UTC().Format(time.RFC3339)
|
||||||
|
}
|
||||||
11
api/server/interface/services/ledger/ledger.go
Normal file
11
api/server/interface/services/ledger/ledger.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package ledger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tech/sendico/pkg/mservice"
|
||||||
|
"github.com/tech/sendico/server/interface/api"
|
||||||
|
"github.com/tech/sendico/server/internal/server/ledgerapiimp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Create(a api.API) (mservice.MicroService, error) {
|
||||||
|
return ledgerapiimp.CreateAPI(a)
|
||||||
|
}
|
||||||
11
api/server/interface/services/wallet/wallet.go
Normal file
11
api/server/interface/services/wallet/wallet.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package wallet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tech/sendico/pkg/mservice"
|
||||||
|
"github.com/tech/sendico/server/interface/api"
|
||||||
|
"github.com/tech/sendico/server/internal/server/walletapiimp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Create(a api.API) (mservice.MicroService, error) {
|
||||||
|
return walletapiimp.CreateAPI(a)
|
||||||
|
}
|
||||||
@@ -18,6 +18,8 @@ import (
|
|||||||
"github.com/tech/sendico/server/interface/services/organization"
|
"github.com/tech/sendico/server/interface/services/organization"
|
||||||
"github.com/tech/sendico/server/interface/services/permission"
|
"github.com/tech/sendico/server/interface/services/permission"
|
||||||
"github.com/tech/sendico/server/interface/services/site"
|
"github.com/tech/sendico/server/interface/services/site"
|
||||||
|
"github.com/tech/sendico/server/interface/services/wallet"
|
||||||
|
"github.com/tech/sendico/server/interface/services/ledger"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -83,6 +85,8 @@ func (a *APIImp) installServices() error {
|
|||||||
srvf = append(srvf, logo.Create)
|
srvf = append(srvf, logo.Create)
|
||||||
srvf = append(srvf, permission.Create)
|
srvf = append(srvf, permission.Create)
|
||||||
srvf = append(srvf, site.Create)
|
srvf = append(srvf, site.Create)
|
||||||
|
srvf = append(srvf, wallet.Create)
|
||||||
|
srvf = append(srvf, ledger.Create)
|
||||||
|
|
||||||
for _, v := range srvf {
|
for _, v := range srvf {
|
||||||
if err := a.addMicroservice(v); err != nil {
|
if err := a.addMicroservice(v); err != nil {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
|
|
||||||
const pendingLoginTTLMinutes = 10
|
const pendingLoginTTLMinutes = 10
|
||||||
|
|
||||||
func (pr *PublicRouter) logUserIn(ctx context.Context, r *http.Request, req *srequest.Login) http.HandlerFunc {
|
func (pr *PublicRouter) logUserIn(ctx context.Context, _ *http.Request, req *srequest.Login) http.HandlerFunc {
|
||||||
// Get the account database entry
|
// Get the account database entry
|
||||||
trimmedLogin := strings.TrimSpace(req.Login)
|
trimmedLogin := strings.TrimSpace(req.Login)
|
||||||
account, err := pr.db.GetByEmail(ctx, strings.ToLower(trimmedLogin))
|
account, err := pr.db.GetByEmail(ctx, strings.ToLower(trimmedLogin))
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/tech/sendico/pkg/api/http/response"
|
"github.com/tech/sendico/pkg/api/http/response"
|
||||||
|
"github.com/tech/sendico/pkg/db/storable"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
"github.com/tech/sendico/pkg/model"
|
"github.com/tech/sendico/pkg/model"
|
||||||
"github.com/tech/sendico/pkg/mservice"
|
"github.com/tech/sendico/pkg/mservice"
|
||||||
@@ -31,10 +32,20 @@ func (a *AccountAPI) createOrg(ctx context.Context, sr *srequest.Signup, permiss
|
|||||||
return nil, merrors.DataConflict(fmt.Sprintf("invalid time zone '%s' provided, error %s", sr.OrganizationTimeZone, err.Error()))
|
return nil, merrors.DataConflict(fmt.Sprintf("invalid time zone '%s' provided, error %s", sr.OrganizationTimeZone, err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// explicitly set org ref for permission related checks as unprotected template implementation
|
||||||
|
// is not aware of permisssions and won't set org
|
||||||
|
orgRef := primitive.NewObjectID()
|
||||||
org := &model.Organization{
|
org := &model.Organization{
|
||||||
OrganizationBase: model.OrganizationBase{
|
OrganizationBase: model.OrganizationBase{
|
||||||
PermissionBound: model.PermissionBound{
|
PermissionBound: model.PermissionBound{
|
||||||
|
Base: storable.Base{
|
||||||
|
ID: orgRef,
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
},
|
||||||
PermissionRef: permissionRef,
|
PermissionRef: permissionRef,
|
||||||
|
OrganizationBoundBase: model.OrganizationBoundBase{
|
||||||
|
OrganizationRef: orgRef,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Describable: model.Describable{
|
Describable: model.Describable{
|
||||||
Name: name,
|
Name: name,
|
||||||
@@ -134,25 +145,30 @@ func (a *AccountAPI) signupTransactionBody(ctx context.Context, sr *srequest.Sig
|
|||||||
a.logger.Warn("Failed to create organization", zap.Error(err))
|
a.logger.Warn("Failed to create organization", zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
a.logger.Info("Organization created", mzap.StorableRef(org), zap.String("account", sr.Account.Login))
|
||||||
|
|
||||||
if err := a.openOrgWallet(ctx, org, sr); err != nil {
|
if err := a.openOrgWallet(ctx, org, sr); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
a.logger.Info("Organization wallet created", mzap.StorableRef(org), zap.String("account", sr.Account.Login))
|
||||||
|
|
||||||
roleDescription, err := a.pmanager.Role().Create(ctx, org.ID, &sr.OwnerRole)
|
roleDescription, err := a.pmanager.Role().Create(ctx, org.ID, &sr.OwnerRole)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.logger.Warn("Failed to create owner role", zap.Error(err), zap.String("login", newAccount.Login))
|
a.logger.Warn("Failed to create owner role", zap.Error(err), zap.String("login", newAccount.Login))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
a.logger.Info("Organization owner role created", mzap.StorableRef(org), zap.String("account", sr.Account.Login))
|
||||||
|
|
||||||
if err := a.grantAllPermissions(ctx, org.ID, roleDescription.ID, newAccount); err != nil {
|
if err := a.grantAllPermissions(ctx, org.ID, roleDescription.ID, newAccount); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
a.logger.Info("Organization owner role permissions granted", mzap.StorableRef(org), zap.String("account", sr.Account.Login))
|
||||||
|
|
||||||
if err := a.accService.CreateAccount(ctx, org, newAccount, roleDescription.ID); err != nil {
|
if err := a.accService.CreateAccount(ctx, org, newAccount, roleDescription.ID); err != nil {
|
||||||
a.logger.Warn("Failed to create account", zap.Error(err), zap.String("login", newAccount.Login))
|
a.logger.Warn("Failed to create account", zap.Error(err), zap.String("login", newAccount.Login))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
a.logger.Info("Organization owner account registered", mzap.StorableRef(org), zap.String("account", sr.Account.Login))
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,9 +84,9 @@ func CreateAPI(a eapi.API) (*ConfirmationAPI, error) {
|
|||||||
signature: middleware.SignatureConf(a.Config().Mw),
|
signature: middleware.SignatureConf(a.Config().Mw),
|
||||||
}
|
}
|
||||||
|
|
||||||
a.Register().PendingAccountHandler(p.Name(), "/confirmations", api.Post, p.requestCode)
|
a.Register().PendingAccountHandler(p.Name(), "/", api.Post, p.requestCode)
|
||||||
a.Register().PendingAccountHandler(p.Name(), "/confirmations/resend", api.Post, p.resendCode)
|
a.Register().PendingAccountHandler(p.Name(), "/resend", api.Post, p.resendCode)
|
||||||
a.Register().PendingAccountHandler(p.Name(), "/confirmations/verify", api.Post, p.verifyCode)
|
a.Register().PendingAccountHandler(p.Name(), "/verify", api.Post, p.verifyCode)
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ func (a *ConfirmationAPI) sendCode(account *model.Account, target model.Confirma
|
|||||||
if err := a.producer.SendMessage(cnotifications.Code(a.Name(), account.ID, destination, target, code)); err != nil {
|
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.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))
|
a.logger.Debug("Confirmation code debug dump", zap.String("code", code))
|
||||||
}
|
}
|
||||||
|
|
||||||
func maskEmail(email string) string {
|
func maskEmail(email string) string {
|
||||||
|
|||||||
@@ -147,20 +147,20 @@ func (s *ConfirmationStore) buildRecord(
|
|||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now().UTC()
|
now := time.Now().UTC()
|
||||||
rec := model.NewConfirmationCode(accountRef)
|
rec := &model.ConfirmationCode{
|
||||||
rec.Destination = destination
|
AccountRef: accountRef,
|
||||||
rec.Target = target
|
Destination: destination,
|
||||||
rec.CodeHash = hashCode(salt, code)
|
Target: target,
|
||||||
rec.Salt = salt
|
CodeHash: hashCode(salt, code),
|
||||||
rec.ExpiresAt = now.Add(cfg.TTL)
|
Salt: salt,
|
||||||
rec.MaxAttempts = cfg.MaxAttempts
|
ExpiresAt: now.Add(cfg.TTL),
|
||||||
rec.ResendLimit = cfg.ResendLimit
|
MaxAttempts: cfg.MaxAttempts,
|
||||||
rec.CooldownUntil = now.Add(cfg.Cooldown)
|
ResendLimit: cfg.ResendLimit,
|
||||||
rec.Used = false
|
CooldownUntil: now.Add(cfg.Cooldown),
|
||||||
rec.Attempts = 0
|
Used: false,
|
||||||
rec.ResendCount = 0
|
Attempts: 0,
|
||||||
rec.CreatedAt = now
|
ResendCount: 0,
|
||||||
rec.UpdatedAt = now
|
}
|
||||||
|
|
||||||
return code, salt, rec, nil
|
return code, salt, rec, nil
|
||||||
}
|
}
|
||||||
|
|||||||
52
api/server/internal/server/ledgerapiimp/balance.go
Normal file
52
api/server/internal/server/ledgerapiimp/balance.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package ledgerapiimp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/pkg/api/http/response"
|
||||||
|
"github.com/tech/sendico/pkg/model"
|
||||||
|
"github.com/tech/sendico/pkg/mservice"
|
||||||
|
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||||
|
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *LedgerAPI) getBalance(r *http.Request, account *model.Account, token *sresponse.TokenData) http.HandlerFunc {
|
||||||
|
orgRef, err := a.oph.GetRef(r)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Warn("Failed to parse organization reference for ledger balance", zap.Error(err), zap.String(a.oph.Name(), a.oph.GetID(r)))
|
||||||
|
return response.BadReference(a.logger, a.Name(), a.oph.Name(), a.oph.GetID(r), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
accountRef := strings.TrimSpace(a.aph.GetID(r))
|
||||||
|
if accountRef == "" {
|
||||||
|
return response.BadReference(a.logger, a.Name(), a.aph.Name(), a.aph.GetID(r), errors.New("ledger account reference is required"))
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := r.Context()
|
||||||
|
res, err := a.enf.Enforce(ctx, a.balancePerm, account.ID, orgRef, primitive.NilObjectID, model.ActionRead)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Warn("Failed to check ledger balance access permissions", zap.Error(err), zap.String(a.oph.Name(), orgRef.Hex()), zap.String("ledger_account_ref", accountRef))
|
||||||
|
return response.Auto(a.logger, a.Name(), err)
|
||||||
|
}
|
||||||
|
if !res {
|
||||||
|
a.logger.Debug("Access denied when reading ledger balance", zap.String(a.oph.Name(), orgRef.Hex()), zap.String("ledger_account_ref", accountRef))
|
||||||
|
return response.AccessDenied(a.logger, a.Name(), "ledger balance read permission denied")
|
||||||
|
}
|
||||||
|
if a.client == nil {
|
||||||
|
return response.Internal(a.logger, mservice.Ledger, errors.New("ledger client is not configured"))
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := a.client.GetBalance(ctx, &ledgerv1.GetBalanceRequest{
|
||||||
|
LedgerAccountRef: accountRef,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Warn("Failed to fetch ledger balance", zap.Error(err), zap.String("ledger_account_ref", accountRef))
|
||||||
|
return response.Auto(a.logger, mservice.Ledger, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sresponse.LedgerBalance(a.logger, resp, token)
|
||||||
|
}
|
||||||
46
api/server/internal/server/ledgerapiimp/list.go
Normal file
46
api/server/internal/server/ledgerapiimp/list.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package ledgerapiimp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/pkg/api/http/response"
|
||||||
|
"github.com/tech/sendico/pkg/model"
|
||||||
|
"github.com/tech/sendico/pkg/mservice"
|
||||||
|
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||||
|
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *LedgerAPI) listAccounts(r *http.Request, account *model.Account, token *sresponse.TokenData) http.HandlerFunc {
|
||||||
|
orgRef, err := a.oph.GetRef(r)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Warn("Failed to parse organization reference for ledger account list", zap.Error(err), zap.String(a.oph.Name(), a.oph.GetID(r)))
|
||||||
|
return response.BadReference(a.logger, a.Name(), a.oph.Name(), a.oph.GetID(r), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := r.Context()
|
||||||
|
res, err := a.enf.Enforce(ctx, a.permissionRef, account.ID, orgRef, primitive.NilObjectID, model.ActionRead)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Warn("Failed to check ledger accounts access permissions", zap.Error(err), zap.String(a.oph.Name(), orgRef.Hex()))
|
||||||
|
return response.Auto(a.logger, a.Name(), err)
|
||||||
|
}
|
||||||
|
if !res {
|
||||||
|
a.logger.Debug("Access denied when listing ledger accounts", zap.String(a.oph.Name(), orgRef.Hex()))
|
||||||
|
return response.AccessDenied(a.logger, a.Name(), "ledger accounts read permission denied")
|
||||||
|
}
|
||||||
|
if a.client == nil {
|
||||||
|
return response.Internal(a.logger, mservice.Ledger, errors.New("ledger client is not configured"))
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := a.client.ListAccounts(ctx, &ledgerv1.ListAccountsRequest{
|
||||||
|
OrganizationRef: orgRef.Hex(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Warn("Failed to list ledger accounts", zap.Error(err), zap.String("organization_ref", orgRef.Hex()))
|
||||||
|
return response.Auto(a.logger, mservice.Ledger, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sresponse.LedgerAccounts(a.logger, resp.GetAccounts(), token)
|
||||||
|
}
|
||||||
110
api/server/internal/server/ledgerapiimp/service.go
Normal file
110
api/server/internal/server/ledgerapiimp/service.go
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
package ledgerapiimp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
ledgerclient "github.com/tech/sendico/ledger/client"
|
||||||
|
api "github.com/tech/sendico/pkg/api/http"
|
||||||
|
"github.com/tech/sendico/pkg/auth"
|
||||||
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
|
"github.com/tech/sendico/pkg/mservice"
|
||||||
|
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||||
|
eapi "github.com/tech/sendico/server/interface/api"
|
||||||
|
mutil "github.com/tech/sendico/server/internal/mutil/param"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ledgerClient interface {
|
||||||
|
ListAccounts(ctx context.Context, req *ledgerv1.ListAccountsRequest) (*ledgerv1.ListAccountsResponse, error)
|
||||||
|
GetBalance(ctx context.Context, req *ledgerv1.GetBalanceRequest) (*ledgerv1.BalanceResponse, error)
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type LedgerAPI struct {
|
||||||
|
logger mlogger.Logger
|
||||||
|
client ledgerClient
|
||||||
|
enf auth.Enforcer
|
||||||
|
oph mutil.ParamHelper
|
||||||
|
aph mutil.ParamHelper
|
||||||
|
permissionRef primitive.ObjectID
|
||||||
|
balancePerm primitive.ObjectID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LedgerAPI) Name() mservice.Type { return mservice.LedgerAccounts }
|
||||||
|
|
||||||
|
func (a *LedgerAPI) Finish(ctx context.Context) error {
|
||||||
|
if a.client != nil {
|
||||||
|
if err := a.client.Close(); err != nil {
|
||||||
|
a.logger.Warn("Failed to close ledger client", zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateAPI(apiCtx eapi.API) (*LedgerAPI, error) {
|
||||||
|
p := &LedgerAPI{
|
||||||
|
logger: apiCtx.Logger().Named(mservice.LedgerAccounts),
|
||||||
|
enf: apiCtx.Permissions().Enforcer(),
|
||||||
|
oph: mutil.CreatePH(mservice.Organizations),
|
||||||
|
aph: mutil.CreatePH("ledger_account"),
|
||||||
|
}
|
||||||
|
|
||||||
|
desc, err := apiCtx.Permissions().GetPolicyDescription(context.Background(), mservice.LedgerAccounts)
|
||||||
|
if err != nil {
|
||||||
|
p.logger.Warn("Failed to fetch ledger accounts permission description", zap.Error(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.permissionRef = desc.ID
|
||||||
|
|
||||||
|
bdesc, err := apiCtx.Permissions().GetPolicyDescription(context.Background(), mservice.LedgerBalances)
|
||||||
|
if err != nil {
|
||||||
|
p.logger.Warn("Failed to fetch ledger balances permission description", zap.Error(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.balancePerm = bdesc.ID
|
||||||
|
|
||||||
|
if err := p.initLedgerClient(apiCtx.Config().Ledger); err != nil {
|
||||||
|
p.logger.Error("Failed to initialize ledger client", zap.Error(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
apiCtx.Register().AccountHandler(p.Name(), p.oph.AddRef("/"), api.Get, p.listAccounts)
|
||||||
|
apiCtx.Register().AccountHandler(p.Name(), p.aph.AddRef(p.oph.AddRef("/"))+"/balance", api.Get, p.getBalance)
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LedgerAPI) initLedgerClient(cfg *eapi.LedgerConfig) error {
|
||||||
|
if cfg == nil {
|
||||||
|
return merrors.InvalidArgument("ledger configuration is not provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
address := strings.TrimSpace(cfg.Address)
|
||||||
|
if address == "" {
|
||||||
|
address = strings.TrimSpace(os.Getenv(cfg.AddressEnv))
|
||||||
|
}
|
||||||
|
if address == "" {
|
||||||
|
return merrors.InvalidArgument(fmt.Sprintf("ledger address is not specified and address env %s is empty", cfg.AddressEnv))
|
||||||
|
}
|
||||||
|
|
||||||
|
clientCfg := ledgerclient.Config{
|
||||||
|
Address: address,
|
||||||
|
DialTimeout: time.Duration(cfg.DialTimeoutSeconds) * time.Second,
|
||||||
|
CallTimeout: time.Duration(cfg.CallTimeoutSeconds) * time.Second,
|
||||||
|
Insecure: cfg.Insecure,
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := ledgerclient.New(context.Background(), clientCfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
a.client = client
|
||||||
|
return nil
|
||||||
|
}
|
||||||
55
api/server/internal/server/walletapiimp/balance.go
Normal file
55
api/server/internal/server/walletapiimp/balance.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package walletapiimp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/pkg/api/http/response"
|
||||||
|
"github.com/tech/sendico/pkg/model"
|
||||||
|
"github.com/tech/sendico/pkg/mservice"
|
||||||
|
gatewayv1 "github.com/tech/sendico/pkg/proto/chain/gateway/v1"
|
||||||
|
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *WalletAPI) getWalletBalance(r *http.Request, account *model.Account, token *sresponse.TokenData) http.HandlerFunc {
|
||||||
|
orgRef, err := a.oph.GetRef(r)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Warn("Failed to parse organization reference for wallet balance", zap.Error(err), zap.String(a.oph.Name(), a.oph.GetID(r)))
|
||||||
|
return response.BadReference(a.logger, a.Name(), a.oph.Name(), a.oph.GetID(r), err)
|
||||||
|
}
|
||||||
|
walletRef := strings.TrimSpace(a.wph.GetID(r))
|
||||||
|
if walletRef == "" {
|
||||||
|
return response.BadReference(a.logger, a.Name(), a.wph.Name(), a.wph.GetID(r), errors.New("wallet reference is required"))
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := r.Context()
|
||||||
|
res, err := a.enf.Enforce(ctx, a.balancesPermissionRef, account.ID, orgRef, primitive.NilObjectID, model.ActionRead)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Warn("Failed to check wallet balance permissions", zap.Error(err), zap.String(a.oph.Name(), orgRef.Hex()), zap.String("wallet_ref", walletRef))
|
||||||
|
return response.Auto(a.logger, a.Name(), err)
|
||||||
|
}
|
||||||
|
if !res {
|
||||||
|
a.logger.Debug("Access denied when reading wallet balance", zap.String(a.oph.Name(), orgRef.Hex()), zap.String("wallet_ref", walletRef))
|
||||||
|
return response.AccessDenied(a.logger, a.Name(), "wallet balance read permission denied")
|
||||||
|
}
|
||||||
|
if a.chainGateway == nil {
|
||||||
|
return response.Internal(a.logger, mservice.ChainGateway, errors.New("chain gateway client is not configured"))
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := a.chainGateway.GetWalletBalance(ctx, &gatewayv1.GetWalletBalanceRequest{WalletRef: walletRef})
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Warn("Failed to fetch wallet balance", zap.Error(err), zap.String("wallet_ref", walletRef))
|
||||||
|
return response.Auto(a.logger, mservice.ChainGateway, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bal := resp.GetBalance()
|
||||||
|
if bal == nil {
|
||||||
|
a.logger.Warn("Wallet balance missing in response", zap.String("wallet_ref", walletRef))
|
||||||
|
return response.Auto(a.logger, mservice.ChainGateway, errors.New("wallet balance not available"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return sresponse.WalletBalance(a.logger, bal, token)
|
||||||
|
}
|
||||||
52
api/server/internal/server/walletapiimp/list.go
Normal file
52
api/server/internal/server/walletapiimp/list.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package walletapiimp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/pkg/api/http/response"
|
||||||
|
"github.com/tech/sendico/pkg/model"
|
||||||
|
"github.com/tech/sendico/pkg/mservice"
|
||||||
|
gatewayv1 "github.com/tech/sendico/pkg/proto/chain/gateway/v1"
|
||||||
|
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *WalletAPI) listWallets(r *http.Request, account *model.Account, token *sresponse.TokenData) http.HandlerFunc {
|
||||||
|
orgRef, err := a.oph.GetRef(r)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Warn("Failed to parse organization reference for wallet list", zap.Error(err), zap.String(a.oph.Name(), a.oph.GetID(r)))
|
||||||
|
return response.BadReference(a.logger, a.Name(), a.oph.Name(), a.oph.GetID(r), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := r.Context()
|
||||||
|
res, err := a.enf.Enforce(ctx, a.walletsPermissionRef, account.ID, orgRef, primitive.NilObjectID, model.ActionRead)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Warn("Failed to check chain wallet access permissions", zap.Error(err), zap.String(a.oph.Name(), orgRef.Hex()))
|
||||||
|
return response.Auto(a.logger, a.Name(), err)
|
||||||
|
}
|
||||||
|
if !res {
|
||||||
|
a.logger.Debug("Access denied when listing organization wallets", zap.String(a.oph.Name(), orgRef.Hex()))
|
||||||
|
return response.AccessDenied(a.logger, a.Name(), "wallets read permission denied")
|
||||||
|
}
|
||||||
|
if a.chainGateway == nil {
|
||||||
|
return response.Internal(a.logger, mservice.ChainGateway, errors.New("chain gateway client is not configured"))
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &gatewayv1.ListManagedWalletsRequest{
|
||||||
|
OrganizationRef: orgRef.Hex(),
|
||||||
|
}
|
||||||
|
if owner := strings.TrimSpace(r.URL.Query().Get("owner_ref")); owner != "" {
|
||||||
|
req.OwnerRef = owner
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := a.chainGateway.ListManagedWallets(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Warn("Failed to list managed wallets", zap.Error(err), zap.String("organization_ref", orgRef.Hex()))
|
||||||
|
return response.Auto(a.logger, mservice.ChainGateway, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sresponse.Wallets(a.logger, resp, token)
|
||||||
|
}
|
||||||
115
api/server/internal/server/walletapiimp/service.go
Normal file
115
api/server/internal/server/walletapiimp/service.go
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
package walletapiimp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
chaingatewayclient "github.com/tech/sendico/chain/gateway/client"
|
||||||
|
api "github.com/tech/sendico/pkg/api/http"
|
||||||
|
"github.com/tech/sendico/pkg/auth"
|
||||||
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
|
"github.com/tech/sendico/pkg/mservice"
|
||||||
|
gatewayv1 "github.com/tech/sendico/pkg/proto/chain/gateway/v1"
|
||||||
|
eapi "github.com/tech/sendico/server/interface/api"
|
||||||
|
mutil "github.com/tech/sendico/server/internal/mutil/param"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WalletAPI struct {
|
||||||
|
logger mlogger.Logger
|
||||||
|
chainGateway chainWalletClient
|
||||||
|
enf auth.Enforcer
|
||||||
|
oph mutil.ParamHelper
|
||||||
|
wph mutil.ParamHelper
|
||||||
|
walletsPermissionRef primitive.ObjectID
|
||||||
|
balancesPermissionRef primitive.ObjectID
|
||||||
|
}
|
||||||
|
|
||||||
|
type chainWalletClient interface {
|
||||||
|
ListManagedWallets(ctx context.Context, req *gatewayv1.ListManagedWalletsRequest) (*gatewayv1.ListManagedWalletsResponse, error)
|
||||||
|
GetWalletBalance(ctx context.Context, req *gatewayv1.GetWalletBalanceRequest) (*gatewayv1.GetWalletBalanceResponse, error)
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *WalletAPI) Name() mservice.Type { return mservice.ChainWallets }
|
||||||
|
|
||||||
|
func (a *WalletAPI) Finish(ctx context.Context) error {
|
||||||
|
if a.chainGateway != nil {
|
||||||
|
if err := a.chainGateway.Close(); err != nil {
|
||||||
|
a.logger.Warn("Failed to close chain gateway client", zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateAPI(apiCtx eapi.API) (*WalletAPI, error) {
|
||||||
|
p := &WalletAPI{
|
||||||
|
logger: apiCtx.Logger().Named(mservice.Wallets),
|
||||||
|
enf: apiCtx.Permissions().Enforcer(),
|
||||||
|
oph: mutil.CreatePH(mservice.Organizations),
|
||||||
|
wph: mutil.CreatePH(mservice.Wallets),
|
||||||
|
}
|
||||||
|
|
||||||
|
walletsPolicy, err := apiCtx.Permissions().GetPolicyDescription(context.Background(), mservice.ChainWallets)
|
||||||
|
if err != nil {
|
||||||
|
p.logger.Warn("Failed to fetch chain wallets permission policy description", zap.Error(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.walletsPermissionRef = walletsPolicy.ID
|
||||||
|
|
||||||
|
balancesPolicy, err := apiCtx.Permissions().GetPolicyDescription(context.Background(), mservice.ChainWalletBalances)
|
||||||
|
if err != nil {
|
||||||
|
p.logger.Warn("Failed to fetch chain wallet balances permission policy description", zap.Error(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.balancesPermissionRef = balancesPolicy.ID
|
||||||
|
|
||||||
|
cfg := apiCtx.Config()
|
||||||
|
if cfg == nil {
|
||||||
|
p.logger.Error("Failed to fetch service configuration")
|
||||||
|
return nil, merrors.InvalidArgument("No configuration provided")
|
||||||
|
}
|
||||||
|
if err := p.initChainGateway(cfg.ChainGateway); err != nil {
|
||||||
|
p.logger.Error("Failed to initialize chain gateway client", zap.Error(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
apiCtx.Register().AccountHandler(p.Name(), p.oph.AddRef("/"), api.Get, p.listWallets)
|
||||||
|
apiCtx.Register().AccountHandler(p.Name(), p.wph.AddRef(p.oph.AddRef("/"))+"/balance", api.Get, p.getWalletBalance)
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *WalletAPI) initChainGateway(cfg *eapi.ChainGatewayConfig) error {
|
||||||
|
if cfg == nil {
|
||||||
|
return merrors.InvalidArgument("chain gateway configuration is not provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.Address = strings.TrimSpace(cfg.Address)
|
||||||
|
if cfg.Address == "" {
|
||||||
|
cfg.Address = strings.TrimSpace(os.Getenv(cfg.AddressEnv))
|
||||||
|
}
|
||||||
|
if cfg.Address == "" {
|
||||||
|
return merrors.InvalidArgument(fmt.Sprintf("chain gateway address is not specified and address env %s is empty", cfg.AddressEnv))
|
||||||
|
}
|
||||||
|
|
||||||
|
clientCfg := chaingatewayclient.Config{
|
||||||
|
Address: cfg.Address,
|
||||||
|
DialTimeout: time.Duration(cfg.DialTimeoutSeconds) * time.Second,
|
||||||
|
CallTimeout: time.Duration(cfg.CallTimeoutSeconds) * time.Second,
|
||||||
|
Insecure: cfg.Insecure,
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := chaingatewayclient.New(context.Background(), clientCfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
a.chainGateway = client
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -8,6 +8,13 @@ MONGO_REPLICA_SET=sendico-rs
|
|||||||
MONGO_AUTH_SOURCE=admin
|
MONGO_AUTH_SOURCE=admin
|
||||||
MONGO_DATABASE=sendico
|
MONGO_DATABASE=sendico
|
||||||
MONGO_ARCH=linux/arm64
|
MONGO_ARCH=linux/arm64
|
||||||
|
MONGO_HOSTS_0=sendico_db1
|
||||||
|
MONGO_PORTS_0=27017
|
||||||
|
MONGO_HOSTS_1=sendico_db2
|
||||||
|
MONGO_PORTS_1=27017
|
||||||
|
MONGO_HOSTS_2=sendico_db3
|
||||||
|
MONGO_PORTS_2=27017
|
||||||
|
|
||||||
PERMISSION_MODEL=/app/env/permissions_model.conf
|
PERMISSION_MODEL=/app/env/permissions_model.conf
|
||||||
PERMISSION_COLLECTION=permissions
|
PERMISSION_COLLECTION=permissions
|
||||||
PERMISSION_TIMEOUT=5
|
PERMISSION_TIMEOUT=5
|
||||||
@@ -81,6 +88,7 @@ LEDGER_COMPOSE_PROJECT=sendico-ledger
|
|||||||
LEDGER_SERVICE_NAME=sendico_ledger
|
LEDGER_SERVICE_NAME=sendico_ledger
|
||||||
LEDGER_GRPC_PORT=50052
|
LEDGER_GRPC_PORT=50052
|
||||||
LEDGER_METRICS_PORT=9401
|
LEDGER_METRICS_PORT=9401
|
||||||
|
LEDGER_ADDRESS=sendico_ledger:50520
|
||||||
|
|
||||||
# Ledger Mongo settings
|
# Ledger Mongo settings
|
||||||
LEDGER_MONGO_HOST=sendico_db1
|
LEDGER_MONGO_HOST=sendico_db1
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ services:
|
|||||||
NATS_PORT: ${NATS_PORT}
|
NATS_PORT: ${NATS_PORT}
|
||||||
NATS_USER: ${NATS_USER}
|
NATS_USER: ${NATS_USER}
|
||||||
NATS_PASSWORD: ${NATS_PASSWORD}
|
NATS_PASSWORD: ${NATS_PASSWORD}
|
||||||
CHAIN_GATEWAY_ADDRESS: ${CHAIN_GATEWAY_ADDRESS}
|
CHAIN_GATEWAY_ADDRESS: ${CHAIN_GATEWAY_SERVICE_NAME}:${CHAIN_GATEWAY_GRPC_PORT}
|
||||||
|
LEDGER_ADDRESS: ${LEDGER_SERVICE_NAME}:${LEDGER_GRPC_PORT}
|
||||||
MONGO_HOST: ${MONGO_HOST}
|
MONGO_HOST: ${MONGO_HOST}
|
||||||
MONGO_PORT: ${MONGO_PORT}
|
MONGO_PORT: ${MONGO_PORT}
|
||||||
MONGO_DATABASE: ${MONGO_DATABASE}
|
MONGO_DATABASE: ${MONGO_DATABASE}
|
||||||
@@ -37,6 +38,12 @@ services:
|
|||||||
MONGO_PASSWORD: ${MONGO_PASSWORD}
|
MONGO_PASSWORD: ${MONGO_PASSWORD}
|
||||||
MONGO_AUTH_SOURCE: ${MONGO_AUTH_SOURCE}
|
MONGO_AUTH_SOURCE: ${MONGO_AUTH_SOURCE}
|
||||||
MONGO_REPLICA_SET: ${MONGO_REPLICA_SET}
|
MONGO_REPLICA_SET: ${MONGO_REPLICA_SET}
|
||||||
|
MONGO_HOSTS_0: ${MONGO_HOSTS_0}
|
||||||
|
MONGO_PORTS_0: ${MONGO_PORTS_0}
|
||||||
|
MONGO_HOSTS_1: ${MONGO_HOSTS_1}
|
||||||
|
MONGO_PORTS_1: ${MONGO_PORTS_1}
|
||||||
|
MONGO_HOSTS_2: ${MONGO_HOSTS_2}
|
||||||
|
MONGO_PORTS_2: ${MONGO_PORTS_2}
|
||||||
PERMISSION_MODEL: ${PERMISSION_MODEL}
|
PERMISSION_MODEL: ${PERMISSION_MODEL}
|
||||||
PERMISSION_COLLECTION: ${PERMISSION_COLLECTION}
|
PERMISSION_COLLECTION: ${PERMISSION_COLLECTION}
|
||||||
PERMISSION_TIMEOUT: ${PERMISSION_TIMEOUT}
|
PERMISSION_TIMEOUT: ${PERMISSION_TIMEOUT}
|
||||||
|
|||||||
@@ -25,6 +25,12 @@ services:
|
|||||||
FEES_MONGO_PASSWORD: ${FEES_MONGO_PASSWORD}
|
FEES_MONGO_PASSWORD: ${FEES_MONGO_PASSWORD}
|
||||||
FEES_MONGO_AUTH_SOURCE: ${FEES_MONGO_AUTH_SOURCE}
|
FEES_MONGO_AUTH_SOURCE: ${FEES_MONGO_AUTH_SOURCE}
|
||||||
FEES_MONGO_REPLICA_SET: ${FEES_MONGO_REPLICA_SET}
|
FEES_MONGO_REPLICA_SET: ${FEES_MONGO_REPLICA_SET}
|
||||||
|
MONGO_HOSTS_0: ${MONGO_HOSTS_0}
|
||||||
|
MONGO_PORTS_0: ${MONGO_PORTS_0}
|
||||||
|
MONGO_HOSTS_1: ${MONGO_HOSTS_1}
|
||||||
|
MONGO_PORTS_1: ${MONGO_PORTS_1}
|
||||||
|
MONGO_HOSTS_2: ${MONGO_HOSTS_2}
|
||||||
|
MONGO_PORTS_2: ${MONGO_PORTS_2}
|
||||||
FEES_GRPC_PORT: ${FEES_GRPC_PORT}
|
FEES_GRPC_PORT: ${FEES_GRPC_PORT}
|
||||||
FEES_METRICS_PORT: ${FEES_METRICS_PORT}
|
FEES_METRICS_PORT: ${FEES_METRICS_PORT}
|
||||||
NATS_URL: ${NATS_URL}
|
NATS_URL: ${NATS_URL}
|
||||||
|
|||||||
@@ -33,6 +33,12 @@ services:
|
|||||||
CHAIN_GATEWAY_MONGO_PASSWORD: ${CHAIN_GATEWAY_MONGO_PASSWORD}
|
CHAIN_GATEWAY_MONGO_PASSWORD: ${CHAIN_GATEWAY_MONGO_PASSWORD}
|
||||||
CHAIN_GATEWAY_MONGO_AUTH_SOURCE: ${CHAIN_GATEWAY_MONGO_AUTH_SOURCE}
|
CHAIN_GATEWAY_MONGO_AUTH_SOURCE: ${CHAIN_GATEWAY_MONGO_AUTH_SOURCE}
|
||||||
CHAIN_GATEWAY_MONGO_REPLICA_SET: ${CHAIN_GATEWAY_MONGO_REPLICA_SET}
|
CHAIN_GATEWAY_MONGO_REPLICA_SET: ${CHAIN_GATEWAY_MONGO_REPLICA_SET}
|
||||||
|
MONGO_HOSTS_0: ${MONGO_HOSTS_0}
|
||||||
|
MONGO_PORTS_0: ${MONGO_PORTS_0}
|
||||||
|
MONGO_HOSTS_1: ${MONGO_HOSTS_1}
|
||||||
|
MONGO_PORTS_1: ${MONGO_PORTS_1}
|
||||||
|
MONGO_HOSTS_2: ${MONGO_HOSTS_2}
|
||||||
|
MONGO_PORTS_2: ${MONGO_PORTS_2}
|
||||||
NATS_URL: ${NATS_URL}
|
NATS_URL: ${NATS_URL}
|
||||||
NATS_HOST: ${NATS_HOST}
|
NATS_HOST: ${NATS_HOST}
|
||||||
NATS_PORT: ${NATS_PORT}
|
NATS_PORT: ${NATS_PORT}
|
||||||
|
|||||||
@@ -25,6 +25,12 @@ services:
|
|||||||
FX_MONGO_PASSWORD: ${FX_MONGO_PASSWORD}
|
FX_MONGO_PASSWORD: ${FX_MONGO_PASSWORD}
|
||||||
FX_MONGO_AUTH_SOURCE: ${FX_MONGO_AUTH_SOURCE}
|
FX_MONGO_AUTH_SOURCE: ${FX_MONGO_AUTH_SOURCE}
|
||||||
FX_MONGO_REPLICA_SET: ${FX_MONGO_REPLICA_SET}
|
FX_MONGO_REPLICA_SET: ${FX_MONGO_REPLICA_SET}
|
||||||
|
MONGO_HOSTS_0: ${MONGO_HOSTS_0}
|
||||||
|
MONGO_PORTS_0: ${MONGO_PORTS_0}
|
||||||
|
MONGO_HOSTS_1: ${MONGO_HOSTS_1}
|
||||||
|
MONGO_PORTS_1: ${MONGO_PORTS_1}
|
||||||
|
MONGO_HOSTS_2: ${MONGO_HOSTS_2}
|
||||||
|
MONGO_PORTS_2: ${MONGO_PORTS_2}
|
||||||
FX_INGESTOR_METRICS_PORT: ${FX_INGESTOR_METRICS_PORT}
|
FX_INGESTOR_METRICS_PORT: ${FX_INGESTOR_METRICS_PORT}
|
||||||
command: ["--config.file", "/app/config.yml"]
|
command: ["--config.file", "/app/config.yml"]
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -25,6 +25,12 @@ services:
|
|||||||
FX_MONGO_PASSWORD: ${FX_MONGO_PASSWORD}
|
FX_MONGO_PASSWORD: ${FX_MONGO_PASSWORD}
|
||||||
FX_MONGO_AUTH_SOURCE: ${FX_MONGO_AUTH_SOURCE}
|
FX_MONGO_AUTH_SOURCE: ${FX_MONGO_AUTH_SOURCE}
|
||||||
FX_MONGO_REPLICA_SET: ${FX_MONGO_REPLICA_SET}
|
FX_MONGO_REPLICA_SET: ${FX_MONGO_REPLICA_SET}
|
||||||
|
MONGO_HOSTS_0: ${MONGO_HOSTS_0}
|
||||||
|
MONGO_PORTS_0: ${MONGO_PORTS_0}
|
||||||
|
MONGO_HOSTS_1: ${MONGO_HOSTS_1}
|
||||||
|
MONGO_PORTS_1: ${MONGO_PORTS_1}
|
||||||
|
MONGO_HOSTS_2: ${MONGO_HOSTS_2}
|
||||||
|
MONGO_PORTS_2: ${MONGO_PORTS_2}
|
||||||
FX_ORACLE_GRPC_PORT: ${FX_ORACLE_GRPC_PORT}
|
FX_ORACLE_GRPC_PORT: ${FX_ORACLE_GRPC_PORT}
|
||||||
FX_ORACLE_METRICS_PORT: ${FX_ORACLE_METRICS_PORT}
|
FX_ORACLE_METRICS_PORT: ${FX_ORACLE_METRICS_PORT}
|
||||||
NATS_URL: ${FX_NATS_URL}
|
NATS_URL: ${FX_NATS_URL}
|
||||||
|
|||||||
@@ -25,6 +25,12 @@ services:
|
|||||||
LEDGER_MONGO_PASSWORD: ${LEDGER_MONGO_PASSWORD}
|
LEDGER_MONGO_PASSWORD: ${LEDGER_MONGO_PASSWORD}
|
||||||
LEDGER_MONGO_AUTH_SOURCE: ${LEDGER_MONGO_AUTH_SOURCE}
|
LEDGER_MONGO_AUTH_SOURCE: ${LEDGER_MONGO_AUTH_SOURCE}
|
||||||
LEDGER_MONGO_REPLICA_SET: ${LEDGER_MONGO_REPLICA_SET}
|
LEDGER_MONGO_REPLICA_SET: ${LEDGER_MONGO_REPLICA_SET}
|
||||||
|
MONGO_HOSTS_0: ${MONGO_HOSTS_0}
|
||||||
|
MONGO_PORTS_0: ${MONGO_PORTS_0}
|
||||||
|
MONGO_HOSTS_1: ${MONGO_HOSTS_1}
|
||||||
|
MONGO_PORTS_1: ${MONGO_PORTS_1}
|
||||||
|
MONGO_HOSTS_2: ${MONGO_HOSTS_2}
|
||||||
|
MONGO_PORTS_2: ${MONGO_PORTS_2}
|
||||||
LEDGER_GRPC_PORT: ${LEDGER_GRPC_PORT}
|
LEDGER_GRPC_PORT: ${LEDGER_GRPC_PORT}
|
||||||
LEDGER_METRICS_PORT: ${LEDGER_METRICS_PORT}
|
LEDGER_METRICS_PORT: ${LEDGER_METRICS_PORT}
|
||||||
NATS_URL: ${NATS_URL}
|
NATS_URL: ${NATS_URL}
|
||||||
|
|||||||
@@ -41,6 +41,12 @@ services:
|
|||||||
MONGO_PASSWORD: ${MONGO_PASSWORD}
|
MONGO_PASSWORD: ${MONGO_PASSWORD}
|
||||||
MONGO_AUTH_SOURCE: ${MONGO_AUTH_SOURCE}
|
MONGO_AUTH_SOURCE: ${MONGO_AUTH_SOURCE}
|
||||||
MONGO_REPLICA_SET: ${MONGO_REPLICA_SET}
|
MONGO_REPLICA_SET: ${MONGO_REPLICA_SET}
|
||||||
|
MONGO_HOSTS_0: ${MONGO_HOSTS_0}
|
||||||
|
MONGO_PORTS_0: ${MONGO_PORTS_0}
|
||||||
|
MONGO_HOSTS_1: ${MONGO_HOSTS_1}
|
||||||
|
MONGO_PORTS_1: ${MONGO_PORTS_1}
|
||||||
|
MONGO_HOSTS_2: ${MONGO_HOSTS_2}
|
||||||
|
MONGO_PORTS_2: ${MONGO_PORTS_2}
|
||||||
PERMISSION_MODEL: ${PERMISSION_MODEL}
|
PERMISSION_MODEL: ${PERMISSION_MODEL}
|
||||||
PERMISSION_COLLECTION: ${PERMISSION_COLLECTION}
|
PERMISSION_COLLECTION: ${PERMISSION_COLLECTION}
|
||||||
PERMISSION_TIMEOUT: ${PERMISSION_TIMEOUT}
|
PERMISSION_TIMEOUT: ${PERMISSION_TIMEOUT}
|
||||||
|
|||||||
@@ -25,6 +25,12 @@ services:
|
|||||||
PAYMENTS_MONGO_PASSWORD: ${PAYMENTS_MONGO_PASSWORD}
|
PAYMENTS_MONGO_PASSWORD: ${PAYMENTS_MONGO_PASSWORD}
|
||||||
PAYMENTS_MONGO_AUTH_SOURCE: ${PAYMENTS_MONGO_AUTH_SOURCE}
|
PAYMENTS_MONGO_AUTH_SOURCE: ${PAYMENTS_MONGO_AUTH_SOURCE}
|
||||||
PAYMENTS_MONGO_REPLICA_SET: ${PAYMENTS_MONGO_REPLICA_SET}
|
PAYMENTS_MONGO_REPLICA_SET: ${PAYMENTS_MONGO_REPLICA_SET}
|
||||||
|
MONGO_HOSTS_0: ${MONGO_HOSTS_0}
|
||||||
|
MONGO_PORTS_0: ${MONGO_PORTS_0}
|
||||||
|
MONGO_HOSTS_1: ${MONGO_HOSTS_1}
|
||||||
|
MONGO_PORTS_1: ${MONGO_PORTS_1}
|
||||||
|
MONGO_HOSTS_2: ${MONGO_HOSTS_2}
|
||||||
|
MONGO_PORTS_2: ${MONGO_PORTS_2}
|
||||||
NATS_URL: ${NATS_URL}
|
NATS_URL: ${NATS_URL}
|
||||||
NATS_HOST: ${NATS_HOST}
|
NATS_HOST: ${NATS_HOST}
|
||||||
NATS_PORT: ${NATS_PORT}
|
NATS_PORT: ${NATS_PORT}
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/models/confirmation_target.dart';
|
||||||
|
import 'package:pshared/api/requests/tokens/session_identifier.dart';
|
||||||
|
|
||||||
|
part 'login_confirmation.g.dart';
|
||||||
|
|
||||||
|
|
||||||
|
@JsonSerializable(explicitToJson: true)
|
||||||
|
class LoginConfirmationRequest {
|
||||||
|
final ConfirmationTarget target;
|
||||||
|
final String? destination;
|
||||||
|
|
||||||
|
const LoginConfirmationRequest({
|
||||||
|
this.target = ConfirmationTarget.login,
|
||||||
|
this.destination,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory LoginConfirmationRequest.fromJson(Map<String, dynamic> json) => _$LoginConfirmationRequestFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() => _$LoginConfirmationRequestToJson(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable(explicitToJson: true)
|
||||||
|
class LoginConfirmationVerifyRequest {
|
||||||
|
final ConfirmationTarget target;
|
||||||
|
final String code;
|
||||||
|
final String? destination;
|
||||||
|
final SessionIdentifierDto sessionIdentifier;
|
||||||
|
|
||||||
|
const LoginConfirmationVerifyRequest({
|
||||||
|
this.target = ConfirmationTarget.login,
|
||||||
|
required this.code,
|
||||||
|
this.destination,
|
||||||
|
required this.sessionIdentifier,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory LoginConfirmationVerifyRequest.fromJson(Map<String, dynamic> json) => _$LoginConfirmationVerifyRequestFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() => _$LoginConfirmationVerifyRequestToJson(this);
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'session_identifier.g.dart';
|
||||||
|
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class SessionIdentifierDto {
|
||||||
|
final String clientId;
|
||||||
|
final String deviceId;
|
||||||
|
|
||||||
|
const SessionIdentifierDto({
|
||||||
|
required this.clientId,
|
||||||
|
required this.deviceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory SessionIdentifierDto.fromJson(Map<String, dynamic> json) => _$SessionIdentifierDtoFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() => _$SessionIdentifierDtoToJson(this);
|
||||||
|
}
|
||||||
@@ -1,21 +1,13 @@
|
|||||||
|
|
||||||
enum MessageType {
|
enum MessageType {
|
||||||
success,
|
success,
|
||||||
|
processed,
|
||||||
error,
|
error,
|
||||||
request
|
request
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MessageTypeExtension on MessageType {
|
extension MessageTypeExtension on MessageType {
|
||||||
static String toJson(MessageType value) {
|
static String toJson(MessageType value) => value.name;
|
||||||
switch (value) {
|
|
||||||
case MessageType.success:
|
|
||||||
return 'success';
|
|
||||||
case MessageType.error:
|
|
||||||
return 'error';
|
|
||||||
case MessageType.request:
|
|
||||||
return 'request';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static MessageType fromJson(String json) {
|
static MessageType fromJson(String json) {
|
||||||
switch (json) {
|
switch (json) {
|
||||||
@@ -25,6 +17,8 @@ extension MessageTypeExtension on MessageType {
|
|||||||
return MessageType.error;
|
return MessageType.error;
|
||||||
case 'request':
|
case 'request':
|
||||||
return MessageType.request;
|
return MessageType.request;
|
||||||
|
case 'processed':
|
||||||
|
return MessageType.processed;
|
||||||
default:
|
default:
|
||||||
throw ArgumentError('Unknown HTTPMType string: $json');
|
throw ArgumentError('Unknown HTTPMType string: $json');
|
||||||
}
|
}
|
||||||
|
|||||||
16
frontend/pshared/lib/api/responses/wallet_balance.dart
Normal file
16
frontend/pshared/lib/api/responses/wallet_balance.dart
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/data/dto/wallet/balance.dart';
|
||||||
|
|
||||||
|
part 'wallet_balance.g.dart';
|
||||||
|
|
||||||
|
|
||||||
|
@JsonSerializable(explicitToJson: true)
|
||||||
|
class WalletBalanceResponse {
|
||||||
|
final WalletBalanceDTO balance;
|
||||||
|
|
||||||
|
const WalletBalanceResponse({required this.balance});
|
||||||
|
|
||||||
|
factory WalletBalanceResponse.fromJson(Map<String, dynamic> json) => _$WalletBalanceResponseFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() => _$WalletBalanceResponseToJson(this);
|
||||||
|
}
|
||||||
16
frontend/pshared/lib/api/responses/wallets.dart
Normal file
16
frontend/pshared/lib/api/responses/wallets.dart
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/data/dto/wallet/wallet.dart';
|
||||||
|
|
||||||
|
part 'wallets.g.dart';
|
||||||
|
|
||||||
|
|
||||||
|
@JsonSerializable(explicitToJson: true)
|
||||||
|
class WalletsResponse {
|
||||||
|
final List<WalletDTO> wallets;
|
||||||
|
|
||||||
|
const WalletsResponse({required this.wallets});
|
||||||
|
|
||||||
|
factory WalletsResponse.fromJson(Map<String, dynamic> json) => _$WalletsResponseFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() => _$WalletsResponseToJson(this);
|
||||||
|
}
|
||||||
20
frontend/pshared/lib/data/dto/wallet/asset.dart
Normal file
20
frontend/pshared/lib/data/dto/wallet/asset.dart
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'asset.g.dart';
|
||||||
|
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class WalletAssetDTO {
|
||||||
|
final String chain;
|
||||||
|
final String tokenSymbol;
|
||||||
|
final String contractAddress;
|
||||||
|
|
||||||
|
const WalletAssetDTO({
|
||||||
|
required this.chain,
|
||||||
|
required this.tokenSymbol,
|
||||||
|
required this.contractAddress,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory WalletAssetDTO.fromJson(Map<String, dynamic> json) => _$WalletAssetDTOFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() => _$WalletAssetDTOToJson(this);
|
||||||
|
}
|
||||||
24
frontend/pshared/lib/data/dto/wallet/balance.dart
Normal file
24
frontend/pshared/lib/data/dto/wallet/balance.dart
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/data/dto/wallet/money.dart';
|
||||||
|
|
||||||
|
part 'balance.g.dart';
|
||||||
|
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class WalletBalanceDTO {
|
||||||
|
final MoneyDTO? available;
|
||||||
|
final MoneyDTO? pendingInbound;
|
||||||
|
final MoneyDTO? pendingOutbound;
|
||||||
|
final String? calculatedAt;
|
||||||
|
|
||||||
|
const WalletBalanceDTO({
|
||||||
|
required this.available,
|
||||||
|
required this.pendingInbound,
|
||||||
|
required this.pendingOutbound,
|
||||||
|
required this.calculatedAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory WalletBalanceDTO.fromJson(Map<String, dynamic> json) => _$WalletBalanceDTOFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() => _$WalletBalanceDTOToJson(this);
|
||||||
|
}
|
||||||
18
frontend/pshared/lib/data/dto/wallet/money.dart
Normal file
18
frontend/pshared/lib/data/dto/wallet/money.dart
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'money.g.dart';
|
||||||
|
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class MoneyDTO {
|
||||||
|
final String amount;
|
||||||
|
final String currency;
|
||||||
|
|
||||||
|
const MoneyDTO({
|
||||||
|
required this.amount,
|
||||||
|
required this.currency,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory MoneyDTO.fromJson(Map<String, dynamic> json) => _$MoneyDTOFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() => _$MoneyDTOToJson(this);
|
||||||
|
}
|
||||||
34
frontend/pshared/lib/data/dto/wallet/wallet.dart
Normal file
34
frontend/pshared/lib/data/dto/wallet/wallet.dart
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/data/dto/wallet/asset.dart';
|
||||||
|
|
||||||
|
part 'wallet.g.dart';
|
||||||
|
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class WalletDTO {
|
||||||
|
final String walletRef;
|
||||||
|
final String organizationRef;
|
||||||
|
final String ownerRef;
|
||||||
|
final WalletAssetDTO asset;
|
||||||
|
final String depositAddress;
|
||||||
|
final String status;
|
||||||
|
final Map<String, String>? metadata;
|
||||||
|
final String? createdAt;
|
||||||
|
final String? updatedAt;
|
||||||
|
|
||||||
|
const WalletDTO({
|
||||||
|
required this.walletRef,
|
||||||
|
required this.organizationRef,
|
||||||
|
required this.ownerRef,
|
||||||
|
required this.asset,
|
||||||
|
required this.depositAddress,
|
||||||
|
required this.status,
|
||||||
|
this.metadata,
|
||||||
|
this.createdAt,
|
||||||
|
this.updatedAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory WalletDTO.fromJson(Map<String, dynamic> json) => _$WalletDTOFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() => _$WalletDTOToJson(this);
|
||||||
|
}
|
||||||
16
frontend/pshared/lib/data/mapper/session_identifier.dart
Normal file
16
frontend/pshared/lib/data/mapper/session_identifier.dart
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import 'package:pshared/api/requests/tokens/session_identifier.dart';
|
||||||
|
import 'package:pshared/models/session_identifier.dart';
|
||||||
|
|
||||||
|
extension SessionIdentifierMapper on SessionIdentifier {
|
||||||
|
SessionIdentifierDto toDTO() => SessionIdentifierDto(
|
||||||
|
clientId: clientId,
|
||||||
|
deviceId: deviceId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SessionIdentifierDtoMapper on SessionIdentifierDto {
|
||||||
|
SessionIdentifier toDomain() => SessionIdentifier(
|
||||||
|
clientId: clientId,
|
||||||
|
deviceId: deviceId,
|
||||||
|
);
|
||||||
|
}
|
||||||
15
frontend/pshared/lib/data/mapper/wallet/balance.dart
Normal file
15
frontend/pshared/lib/data/mapper/wallet/balance.dart
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import 'package:pshared/data/dto/wallet/balance.dart';
|
||||||
|
import 'package:pshared/data/mapper/wallet/money.dart';
|
||||||
|
import 'package:pshared/models/wallet/balance.dart';
|
||||||
|
|
||||||
|
|
||||||
|
extension WalletBalanceDTOMapper on WalletBalanceDTO {
|
||||||
|
WalletBalance toDomain() => WalletBalance(
|
||||||
|
available: available?.toDomain(),
|
||||||
|
pendingInbound: pendingInbound?.toDomain(),
|
||||||
|
pendingOutbound: pendingOutbound?.toDomain(),
|
||||||
|
calculatedAt: (calculatedAt == null || calculatedAt!.isEmpty)
|
||||||
|
? null
|
||||||
|
: DateTime.tryParse(calculatedAt!),
|
||||||
|
);
|
||||||
|
}
|
||||||
10
frontend/pshared/lib/data/mapper/wallet/money.dart
Normal file
10
frontend/pshared/lib/data/mapper/wallet/money.dart
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import 'package:pshared/data/dto/wallet/money.dart';
|
||||||
|
import 'package:pshared/models/wallet/money.dart';
|
||||||
|
|
||||||
|
|
||||||
|
extension MoneyDTOMapper on MoneyDTO {
|
||||||
|
WalletMoney toDomain() => WalletMoney(
|
||||||
|
amount: amount,
|
||||||
|
currency: currency,
|
||||||
|
);
|
||||||
|
}
|
||||||
14
frontend/pshared/lib/data/mapper/wallet/response.dart
Normal file
14
frontend/pshared/lib/data/mapper/wallet/response.dart
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import 'package:pshared/api/responses/wallet_balance.dart';
|
||||||
|
import 'package:pshared/api/responses/wallets.dart';
|
||||||
|
import 'package:pshared/data/mapper/wallet/balance.dart';
|
||||||
|
import 'package:pshared/data/mapper/wallet/wallet.dart';
|
||||||
|
import 'package:pshared/models/wallet/balance.dart';
|
||||||
|
import 'package:pshared/models/wallet/wallet.dart';
|
||||||
|
|
||||||
|
extension WalletsResponseMapper on WalletsResponse {
|
||||||
|
List<WalletModel> toDomain() => wallets.map((w) => w.toDomain()).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
extension WalletBalanceResponseMapper on WalletBalanceResponse {
|
||||||
|
WalletBalance toDomain() => balance.toDomain();
|
||||||
|
}
|
||||||
26
frontend/pshared/lib/data/mapper/wallet/wallet.dart
Normal file
26
frontend/pshared/lib/data/mapper/wallet/wallet.dart
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import 'package:pshared/data/dto/wallet/balance.dart';
|
||||||
|
import 'package:pshared/data/dto/wallet/wallet.dart';
|
||||||
|
import 'package:pshared/data/mapper/wallet/balance.dart';
|
||||||
|
import 'package:pshared/data/mapper/wallet/money.dart';
|
||||||
|
import 'package:pshared/models/wallet/wallet.dart';
|
||||||
|
|
||||||
|
|
||||||
|
extension WalletDTOMapper on WalletDTO {
|
||||||
|
WalletModel toDomain({WalletBalanceDTO? balance}) => WalletModel(
|
||||||
|
walletRef: walletRef,
|
||||||
|
organizationRef: organizationRef,
|
||||||
|
ownerRef: ownerRef,
|
||||||
|
asset: WalletAsset(
|
||||||
|
chain: asset.chain,
|
||||||
|
tokenSymbol: asset.tokenSymbol,
|
||||||
|
contractAddress: asset.contractAddress,
|
||||||
|
),
|
||||||
|
depositAddress: depositAddress,
|
||||||
|
status: status,
|
||||||
|
metadata: metadata,
|
||||||
|
createdAt: (createdAt == null || createdAt!.isEmpty) ? null : DateTime.tryParse(createdAt!),
|
||||||
|
updatedAt: (updatedAt == null || updatedAt!.isEmpty) ? null : DateTime.tryParse(updatedAt!),
|
||||||
|
balance: balance?.toDomain(),
|
||||||
|
availableMoney: balance?.available?.toDomain(),
|
||||||
|
);
|
||||||
|
}
|
||||||
11
frontend/pshared/lib/models/confirmation_target.dart
Normal file
11
frontend/pshared/lib/models/confirmation_target.dart
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
|
||||||
|
/// Targets for confirmation codes.
|
||||||
|
@JsonEnum(alwaysCreate: true)
|
||||||
|
enum ConfirmationTarget {
|
||||||
|
@JsonValue('login')
|
||||||
|
login,
|
||||||
|
@JsonValue('payout')
|
||||||
|
payout,
|
||||||
|
}
|
||||||
@@ -22,10 +22,6 @@ enum ResourceType {
|
|||||||
@JsonValue('clients')
|
@JsonValue('clients')
|
||||||
clients,
|
clients,
|
||||||
|
|
||||||
/// Represents comments on tasks or other resources
|
|
||||||
@JsonValue('comments')
|
|
||||||
comments,
|
|
||||||
|
|
||||||
/// Represents invitations sent to users
|
/// Represents invitations sent to users
|
||||||
@JsonValue('invitations')
|
@JsonValue('invitations')
|
||||||
invitations,
|
invitations,
|
||||||
@@ -46,6 +42,9 @@ enum ResourceType {
|
|||||||
@JsonValue('organizations')
|
@JsonValue('organizations')
|
||||||
organizations,
|
organizations,
|
||||||
|
|
||||||
|
@JsonValue('chain_wallets')
|
||||||
|
chainWallets,
|
||||||
|
|
||||||
/// Represents permissions service
|
/// Represents permissions service
|
||||||
@JsonValue('permissions')
|
@JsonValue('permissions')
|
||||||
permissions,
|
permissions,
|
||||||
@@ -54,25 +53,6 @@ enum ResourceType {
|
|||||||
@JsonValue('policies')
|
@JsonValue('policies')
|
||||||
policies,
|
policies,
|
||||||
|
|
||||||
/// Represents task or project priorities
|
|
||||||
@JsonValue('priorities')
|
|
||||||
priorities,
|
|
||||||
|
|
||||||
/// Represents priority groups
|
|
||||||
@JsonValue('priority_groups')
|
|
||||||
priorityGroups,
|
|
||||||
|
|
||||||
/// Represents projects managed in the system
|
|
||||||
@JsonValue('projects')
|
|
||||||
projects,
|
|
||||||
|
|
||||||
@JsonValue('properties')
|
|
||||||
properties,
|
|
||||||
|
|
||||||
/// Represents reactions
|
|
||||||
@JsonValue('reactions')
|
|
||||||
reactions,
|
|
||||||
|
|
||||||
/// Represents refresh tokens for authentication
|
/// Represents refresh tokens for authentication
|
||||||
@JsonValue('refresh_tokens')
|
@JsonValue('refresh_tokens')
|
||||||
refreshTokens,
|
refreshTokens,
|
||||||
@@ -88,20 +68,4 @@ enum ResourceType {
|
|||||||
/// Represents steps in workflows or processes
|
/// Represents steps in workflows or processes
|
||||||
@JsonValue('steps')
|
@JsonValue('steps')
|
||||||
steps,
|
steps,
|
||||||
|
|
||||||
/// Represents tasks managed in the system
|
|
||||||
@JsonValue('tasks')
|
|
||||||
tasks,
|
|
||||||
|
|
||||||
/// Represents teams managed in the system
|
|
||||||
@JsonValue('teams')
|
|
||||||
teams,
|
|
||||||
|
|
||||||
/// Represents workflows for tasks or projects
|
|
||||||
@JsonValue('workflows')
|
|
||||||
workflows,
|
|
||||||
|
|
||||||
/// Represents workspaces containing projects and teams
|
|
||||||
@JsonValue('workspaces')
|
|
||||||
workspaces;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
|
||||||
|
|
||||||
part 'session_identifier.g.dart';
|
|
||||||
|
|
||||||
@JsonSerializable()
|
|
||||||
class SessionIdentifier {
|
class SessionIdentifier {
|
||||||
final String clientId;
|
final String clientId;
|
||||||
final String deviceId;
|
final String deviceId;
|
||||||
@@ -11,8 +6,4 @@ class SessionIdentifier {
|
|||||||
required this.clientId,
|
required this.clientId,
|
||||||
required this.deviceId,
|
required this.deviceId,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory SessionIdentifier.fromJson(Map<String, dynamic> json) => _$SessionIdentifierFromJson(json);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$SessionIdentifierToJson(this);
|
|
||||||
}
|
}
|
||||||
|
|||||||
16
frontend/pshared/lib/models/wallet/balance.dart
Normal file
16
frontend/pshared/lib/models/wallet/balance.dart
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import 'package:pshared/models/wallet/money.dart';
|
||||||
|
|
||||||
|
|
||||||
|
class WalletBalance {
|
||||||
|
final WalletMoney? available;
|
||||||
|
final WalletMoney? pendingInbound;
|
||||||
|
final WalletMoney? pendingOutbound;
|
||||||
|
final DateTime? calculatedAt;
|
||||||
|
|
||||||
|
const WalletBalance({
|
||||||
|
required this.available,
|
||||||
|
required this.pendingInbound,
|
||||||
|
required this.pendingOutbound,
|
||||||
|
required this.calculatedAt,
|
||||||
|
});
|
||||||
|
}
|
||||||
9
frontend/pshared/lib/models/wallet/money.dart
Normal file
9
frontend/pshared/lib/models/wallet/money.dart
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
class WalletMoney {
|
||||||
|
final String amount;
|
||||||
|
final String currency;
|
||||||
|
|
||||||
|
const WalletMoney({
|
||||||
|
required this.amount,
|
||||||
|
required this.currency,
|
||||||
|
});
|
||||||
|
}
|
||||||
62
frontend/pshared/lib/models/wallet/wallet.dart
Normal file
62
frontend/pshared/lib/models/wallet/wallet.dart
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import 'package:pshared/models/wallet/balance.dart';
|
||||||
|
import 'package:pshared/models/wallet/money.dart';
|
||||||
|
|
||||||
|
|
||||||
|
class WalletAsset {
|
||||||
|
final String chain;
|
||||||
|
final String tokenSymbol;
|
||||||
|
final String contractAddress;
|
||||||
|
|
||||||
|
const WalletAsset({
|
||||||
|
required this.chain,
|
||||||
|
required this.tokenSymbol,
|
||||||
|
required this.contractAddress,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class WalletModel {
|
||||||
|
final String walletRef;
|
||||||
|
final String organizationRef;
|
||||||
|
final String ownerRef;
|
||||||
|
final WalletAsset asset;
|
||||||
|
final String depositAddress;
|
||||||
|
final String status;
|
||||||
|
final Map<String, String>? metadata;
|
||||||
|
final DateTime? createdAt;
|
||||||
|
final DateTime? updatedAt;
|
||||||
|
final WalletBalance? balance;
|
||||||
|
final WalletMoney? availableMoney;
|
||||||
|
|
||||||
|
const WalletModel({
|
||||||
|
required this.walletRef,
|
||||||
|
required this.organizationRef,
|
||||||
|
required this.ownerRef,
|
||||||
|
required this.asset,
|
||||||
|
required this.depositAddress,
|
||||||
|
required this.status,
|
||||||
|
this.metadata,
|
||||||
|
this.createdAt,
|
||||||
|
this.updatedAt,
|
||||||
|
this.balance,
|
||||||
|
this.availableMoney,
|
||||||
|
});
|
||||||
|
|
||||||
|
WalletModel copyWith({
|
||||||
|
WalletBalance? balance,
|
||||||
|
WalletMoney? availableMoney,
|
||||||
|
}) {
|
||||||
|
return WalletModel(
|
||||||
|
walletRef: walletRef,
|
||||||
|
organizationRef: organizationRef,
|
||||||
|
ownerRef: ownerRef,
|
||||||
|
asset: asset,
|
||||||
|
depositAddress: depositAddress,
|
||||||
|
status: status,
|
||||||
|
metadata: metadata,
|
||||||
|
createdAt: createdAt,
|
||||||
|
updatedAt: updatedAt,
|
||||||
|
balance: balance ?? this.balance,
|
||||||
|
availableMoney: availableMoney ?? this.availableMoney,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ import 'package:pshared/provider/locale.dart';
|
|||||||
import 'package:pshared/provider/resource.dart';
|
import 'package:pshared/provider/resource.dart';
|
||||||
import 'package:pshared/service/account.dart';
|
import 'package:pshared/service/account.dart';
|
||||||
import 'package:pshared/service/authorization/service.dart';
|
import 'package:pshared/service/authorization/service.dart';
|
||||||
|
import 'package:pshared/service/verification.dart';
|
||||||
import 'package:pshared/utils/exception.dart';
|
import 'package:pshared/utils/exception.dart';
|
||||||
|
|
||||||
|
|
||||||
@@ -77,7 +78,12 @@ class AccountProvider extends ChangeNotifier {
|
|||||||
_setResource(Resource(data: outcome.account, isLoading: false));
|
_setResource(Resource(data: outcome.account, isLoading: false));
|
||||||
_pickupLocale(outcome.account!.locale);
|
_pickupLocale(outcome.account!.locale);
|
||||||
} else {
|
} else {
|
||||||
_pendingLogin = outcome.pending;
|
final pending = outcome.pending;
|
||||||
|
if (pending == null) {
|
||||||
|
throw Exception('Pending login data is missing');
|
||||||
|
}
|
||||||
|
await VerificationService.requestLoginCode(pending);
|
||||||
|
_pendingLogin = pending;
|
||||||
_setResource(_resource.copyWith(isLoading: false));
|
_setResource(_resource.copyWith(isLoading: false));
|
||||||
}
|
}
|
||||||
return outcome;
|
return outcome;
|
||||||
|
|||||||
@@ -8,13 +8,10 @@ import 'package:pshared/api/requests/login_data.dart';
|
|||||||
import 'package:pshared/api/requests/password/change.dart';
|
import 'package:pshared/api/requests/password/change.dart';
|
||||||
import 'package:pshared/api/requests/password/forgot.dart';
|
import 'package:pshared/api/requests/password/forgot.dart';
|
||||||
import 'package:pshared/api/requests/password/reset.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/data/mapper/account/account.dart';
|
||||||
import 'package:pshared/models/account/account.dart';
|
import 'package:pshared/models/account/account.dart';
|
||||||
import 'package:pshared/models/auth/login_outcome.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/service.dart';
|
||||||
import 'package:pshared/service/authorization/storage.dart';
|
|
||||||
import 'package:pshared/service/files.dart';
|
import 'package:pshared/service/files.dart';
|
||||||
import 'package:pshared/service/services.dart';
|
import 'package:pshared/service/services.dart';
|
||||||
import 'package:pshared/utils/http/requests.dart';
|
import 'package:pshared/utils/http/requests.dart';
|
||||||
@@ -29,41 +26,6 @@ class AccountService {
|
|||||||
return AuthorizationService.login(_objectType, login);
|
return AuthorizationService.login(_objectType, login);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> resendLoginCode(PendingLogin pending, {String? destination}) async {
|
|
||||||
await getPOSTResponse(
|
|
||||||
_objectType,
|
|
||||||
'confirmations/resend',
|
|
||||||
{
|
|
||||||
'target': 'login',
|
|
||||||
if (destination != null) 'destination': destination,
|
|
||||||
},
|
|
||||||
authToken: pending.pendingToken.token,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<Account> 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<Account> restore() async {
|
static Future<Account> restore() async {
|
||||||
return AuthorizationService.restore();
|
return AuthorizationService.restore();
|
||||||
}
|
}
|
||||||
@@ -75,7 +37,7 @@ class AccountService {
|
|||||||
|
|
||||||
static Future<void> logout() async {
|
static Future<void> logout() async {
|
||||||
_logger.fine('Logging out');
|
_logger.fine('Logging out');
|
||||||
await AuthorizationService.logout();
|
return AuthorizationService.logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Account> _getAccount(Future<Map<String, dynamic>> future) async {
|
static Future<Account> _getAccount(Future<Map<String, dynamic>> future) async {
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
class Services {
|
class Services {
|
||||||
static const String account = 'accounts';
|
static const String account = 'accounts';
|
||||||
static const String authorization = 'authorization';
|
static const String authorization = 'authorization';
|
||||||
static const String comments = 'comments';
|
static const String confirmations = 'confirmations';
|
||||||
static const String device = 'device';
|
static const String device = 'device';
|
||||||
static const String invitations = 'invitations';
|
static const String invitations = 'invitations';
|
||||||
static const String organization = 'organizations';
|
static const String organization = 'organizations';
|
||||||
static const String permission = 'permissions';
|
static const String permission = 'permissions';
|
||||||
static const String storage = 'storage';
|
static const String storage = 'storage';
|
||||||
|
static const String chainWallets = 'chain_wallets';
|
||||||
|
|
||||||
static const String amplitude = 'amplitude';
|
static const String amplitude = 'amplitude';
|
||||||
static const String clients = 'clients';
|
static const String clients = 'clients';
|
||||||
|
|||||||
60
frontend/pshared/lib/service/verification.dart
Normal file
60
frontend/pshared/lib/service/verification.dart
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/api/requests/confirmations/login_confirmation.dart';
|
||||||
|
import 'package:pshared/api/responses/login.dart';
|
||||||
|
import 'package:pshared/data/mapper/session_identifier.dart';
|
||||||
|
import 'package:pshared/models/account/account.dart';
|
||||||
|
import 'package:pshared/data/mapper/account/account.dart';
|
||||||
|
import 'package:pshared/models/auth/pending_login.dart';
|
||||||
|
import 'package:pshared/service/authorization/storage.dart';
|
||||||
|
import 'package:pshared/service/services.dart';
|
||||||
|
import 'package:pshared/utils/http/requests.dart';
|
||||||
|
|
||||||
|
|
||||||
|
class VerificationService {
|
||||||
|
static final _logger = Logger('service.verification');
|
||||||
|
static const String _objectType = Services.confirmations;
|
||||||
|
|
||||||
|
static Future<void> requestLoginCode(PendingLogin pending, {String? destination}) async {
|
||||||
|
_logger.fine('Requesting login confirmation code');
|
||||||
|
await getPOSTResponse(
|
||||||
|
_objectType,
|
||||||
|
'',
|
||||||
|
LoginConfirmationRequest(destination: destination).toJson(),
|
||||||
|
authToken: pending.pendingToken.token,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> resendLoginCode(PendingLogin pending, {String? destination}) async {
|
||||||
|
_logger.fine('Resending login confirmation code');
|
||||||
|
await getPOSTResponse(
|
||||||
|
_objectType,
|
||||||
|
'/resend',
|
||||||
|
LoginConfirmationRequest(destination: destination).toJson(),
|
||||||
|
authToken: pending.pendingToken.token,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Account> confirmLoginCode({
|
||||||
|
required PendingLogin pending,
|
||||||
|
required String code,
|
||||||
|
String? destination,
|
||||||
|
}) async {
|
||||||
|
_logger.fine('Confirming login code');
|
||||||
|
final response = await getPOSTResponse(
|
||||||
|
_objectType,
|
||||||
|
'/verify',
|
||||||
|
LoginConfirmationVerifyRequest(
|
||||||
|
code: code,
|
||||||
|
destination: destination,
|
||||||
|
sessionIdentifier: pending.session.toDTO(),
|
||||||
|
).toJson(),
|
||||||
|
authToken: pending.pendingToken.token,
|
||||||
|
);
|
||||||
|
|
||||||
|
final loginResponse = LoginResponse.fromJson(response);
|
||||||
|
await AuthorizationStorage.updateToken(loginResponse.accessToken);
|
||||||
|
await AuthorizationStorage.updateRefreshToken(loginResponse.refreshToken);
|
||||||
|
return loginResponse.account.toDomain();
|
||||||
|
}
|
||||||
|
}
|
||||||
31
frontend/pshared/lib/service/wallet.dart
Normal file
31
frontend/pshared/lib/service/wallet.dart
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import 'package:pshared/api/responses/wallet_balance.dart';
|
||||||
|
import 'package:pshared/api/responses/wallets.dart';
|
||||||
|
import 'package:pshared/data/mapper/wallet/response.dart';
|
||||||
|
import 'package:pshared/models/wallet/balance.dart';
|
||||||
|
import 'package:pshared/models/wallet/wallet.dart';
|
||||||
|
import 'package:pshared/service/authorization/service.dart';
|
||||||
|
import 'package:pshared/service/services.dart';
|
||||||
|
|
||||||
|
|
||||||
|
class WalletService {
|
||||||
|
static const String _objectType = Services.chainWallets;
|
||||||
|
|
||||||
|
static Future<List<WalletModel>> list(String organizationRef) async {
|
||||||
|
final json = await AuthorizationService.getGETResponse(
|
||||||
|
_objectType,
|
||||||
|
'/$organizationRef',
|
||||||
|
);
|
||||||
|
return WalletsResponse.fromJson(json).toDomain();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<WalletBalance> getBalance({
|
||||||
|
required String organizationRef,
|
||||||
|
required String walletRef,
|
||||||
|
}) async {
|
||||||
|
final json = await AuthorizationService.getGETResponse(
|
||||||
|
_objectType,
|
||||||
|
'/$organizationRef/$walletRef/balance',
|
||||||
|
);
|
||||||
|
return WalletBalanceResponse.fromJson(json).toDomain();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,14 @@
|
|||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/provider/organizations.dart';
|
||||||
|
|
||||||
import 'package:pweb/app/router/pages.dart';
|
import 'package:pweb/app/router/pages.dart';
|
||||||
|
import 'package:pweb/app/router/page_params.dart';
|
||||||
import 'package:pweb/pages/2fa/page.dart';
|
import 'package:pweb/pages/2fa/page.dart';
|
||||||
import 'package:pweb/pages/signup/page.dart';
|
import 'package:pweb/pages/signup/page.dart';
|
||||||
|
import 'package:pweb/pages/verification/page.dart';
|
||||||
import 'package:pweb/widgets/sidebar/page.dart';
|
import 'package:pweb/widgets/sidebar/page.dart';
|
||||||
import 'package:pweb/pages/login/page.dart';
|
import 'package:pweb/pages/login/page.dart';
|
||||||
import 'package:pweb/pages/errors/not_found.dart';
|
import 'package:pweb/pages/errors/not_found.dart';
|
||||||
@@ -29,28 +35,24 @@ GoRouter createRouter() => GoRouter(
|
|||||||
GoRoute(
|
GoRoute(
|
||||||
name: Pages.sfactor.name,
|
name: Pages.sfactor.name,
|
||||||
path: routerPage(Pages.sfactor),
|
path: routerPage(Pages.sfactor),
|
||||||
builder: (context, state) {
|
builder: (context, _) => TwoFactorCodePage(
|
||||||
// Определяем откуда пришел пользователь
|
|
||||||
final isFromSignup = state.uri.queryParameters['from'] == 'signup';
|
|
||||||
|
|
||||||
return TwoFactorCodePage(
|
|
||||||
onVerificationSuccess: () {
|
onVerificationSuccess: () {
|
||||||
if (isFromSignup) {
|
// trigger organization load
|
||||||
// После регистрации -> на страницу логина
|
context.read<OrganizationsProvider>().load();
|
||||||
context.goNamed(Pages.login.name);
|
|
||||||
} else {
|
|
||||||
// После логина -> на дашборд
|
|
||||||
context.goNamed(Pages.dashboard.name);
|
context.goNamed(Pages.dashboard.name);
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: Pages.signup.name,
|
name: Pages.signup.name,
|
||||||
path: routerPage(Pages.signup),
|
path: routerPage(Pages.signup),
|
||||||
builder: (_, _) => const SignUpPage(),
|
builder: (_, _) => const SignUpPage(),
|
||||||
),
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: Pages.verify.name,
|
||||||
|
path: '${routerPage(Pages.verify)}${routerAddParam(PageParams.token)}',
|
||||||
|
builder: (_, state) => AccountVerificationPage(token: state.pathParameters[PageParams.token.name]!),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
26
frontend/pweb/lib/data/mappers/wallet_ui.dart
Normal file
26
frontend/pweb/lib/data/mappers/wallet_ui.dart
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import 'package:pshared/models/wallet/wallet.dart' as domain;
|
||||||
|
|
||||||
|
import 'package:pweb/models/currency.dart';
|
||||||
|
import 'package:pweb/models/wallet.dart';
|
||||||
|
|
||||||
|
|
||||||
|
extension WalletUiMapper on domain.WalletModel {
|
||||||
|
Wallet toUi() {
|
||||||
|
final amountStr = availableMoney?.amount ?? balance?.available?.amount ?? '0';
|
||||||
|
final currencyStr = availableMoney?.currency ?? balance?.available?.currency ?? Currency.usd.toString().toUpperCase();
|
||||||
|
final parsedAmount = double.tryParse(amountStr) ?? 0;
|
||||||
|
final currency = Currency.values.firstWhere(
|
||||||
|
(c) => c.name.toUpperCase() == currencyStr.toUpperCase(),
|
||||||
|
orElse: () => Currency.usd,
|
||||||
|
);
|
||||||
|
return Wallet(
|
||||||
|
id: walletRef,
|
||||||
|
walletUserID: walletRef,
|
||||||
|
name: metadata?['name'] ?? walletRef,
|
||||||
|
balance: parsedAmount,
|
||||||
|
currency: currency,
|
||||||
|
isHidden: true,
|
||||||
|
calculatedAt: balance?.calculatedAt ?? DateTime.now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
"footerCompanyName": "Sibilla Solutions LTD",
|
"footerCompanyName": "Sibilla Solutions LTD",
|
||||||
"footerAddress": "27, Pindarou Street, Alpha Business Centre, Block B 7th Floor, 1060 Nicosia, Cyprus",
|
"footerAddress": "27, Pindarou Street, Alpha Business Centre, Block B 7th Floor, 1060 Nicosia, Cyprus",
|
||||||
"footerSupport": "Support",
|
"footerSupport": "Support",
|
||||||
"footerEmail": "Email TBD",
|
"footerEmail": "support@sendico.io",
|
||||||
"footerPhoneLabel": "Phone",
|
"footerPhoneLabel": "Phone",
|
||||||
"footerPhone": "+357 22 000 253",
|
"footerPhone": "+357 22 000 253",
|
||||||
"footerTermsOfService": "Terms of Service",
|
"footerTermsOfService": "Terms of Service",
|
||||||
@@ -434,6 +434,14 @@
|
|||||||
"optional": "optional",
|
"optional": "optional",
|
||||||
"ownerRole": "Organization Owner",
|
"ownerRole": "Organization Owner",
|
||||||
"ownerRoleDescription": "This role is granted to the organization’s creator, providing full administrative privileges",
|
"ownerRoleDescription": "This role is granted to the organization’s creator, providing full administrative privileges",
|
||||||
|
"accountVerificationFailed": "Oops! We failed to verify your account. Please, contact support",
|
||||||
|
"verifyAccount": "Account Verification",
|
||||||
|
"verificationFailed": "Verification Failed",
|
||||||
|
"verificationStatusUnknown": "We couldn't determine the status of your verification. Please try again later.",
|
||||||
|
"verificationStatusErrorUnknown": "Unexpected error occurred while verification. Try once again or contact support",
|
||||||
|
"accountVerified": "Account Verified!",
|
||||||
|
"accountVerifiedDescription": "Your account has been successfully verified. You can now log in to access your account.",
|
||||||
|
"retryVerification": "Retry Verification",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"editWallet": "Edit Wallet",
|
"editWallet": "Edit Wallet",
|
||||||
"userNamePlaceholder": "User Name",
|
"userNamePlaceholder": "User Name",
|
||||||
|
|||||||
@@ -66,7 +66,7 @@
|
|||||||
"footerCompanyName": "Sibilla Solutions LTD",
|
"footerCompanyName": "Sibilla Solutions LTD",
|
||||||
"footerAddress": "27, Pindarou Street, Alpha Business Centre, Block B 7th Floor, 1060 Nicosia, Cyprus",
|
"footerAddress": "27, Pindarou Street, Alpha Business Centre, Block B 7th Floor, 1060 Nicosia, Cyprus",
|
||||||
"footerSupport": "Поддержка",
|
"footerSupport": "Поддержка",
|
||||||
"footerEmail": "Email TBD",
|
"footerEmail": "support@sendico.io",
|
||||||
"footerPhoneLabel": "Телефон",
|
"footerPhoneLabel": "Телефон",
|
||||||
"footerPhone": "+357 22 000 253",
|
"footerPhone": "+357 22 000 253",
|
||||||
"footerTermsOfService": "Условия обслуживания",
|
"footerTermsOfService": "Условия обслуживания",
|
||||||
@@ -427,6 +427,14 @@
|
|||||||
|
|
||||||
"ownerRole": "Владелец организации",
|
"ownerRole": "Владелец организации",
|
||||||
"ownerRoleDescription": "Эта роль предоставляется создателю организации и даёт ему полные административные права",
|
"ownerRoleDescription": "Эта роль предоставляется создателю организации и даёт ему полные административные права",
|
||||||
|
"accountVerificationFailed": "Упс! Не удалось подтвердить ваш аккаунт. Пожалуйста, свяжитесь с поддержкой.",
|
||||||
|
"verifyAccount": "Подтвердить аккаунт",
|
||||||
|
"verificationFailed": "Ошибка подтверждения",
|
||||||
|
"verificationStatusUnknown": "Не удалось определить статус подтверждения. Попробуйте позже",
|
||||||
|
"verificationStatusErrorUnknown": "Произошла непредвиденная ошибка при подтверждении. Попробуйте еще раз или обратитесь в службу поддержки",
|
||||||
|
"accountVerified": "Аккаунт подтвержден!",
|
||||||
|
"accountVerifiedDescription": "Ваш аккаунт успешно подтвержден. Теперь вы можете войти, чтобы получить доступ к своему аккаунту",
|
||||||
|
"retryVerification": "Повторить подтверждение",
|
||||||
"save": "Сохранить",
|
"save": "Сохранить",
|
||||||
"editWallet": "Редактировать кошелек",
|
"editWallet": "Редактировать кошелек",
|
||||||
"userNamePlaceholder": "Имя пользователя",
|
"userNamePlaceholder": "Имя пользователя",
|
||||||
|
|||||||
@@ -9,15 +9,15 @@ import 'package:logging/logging.dart';
|
|||||||
|
|
||||||
import 'package:pshared/config/constants.dart';
|
import 'package:pshared/config/constants.dart';
|
||||||
import 'package:pshared/provider/locale.dart';
|
import 'package:pshared/provider/locale.dart';
|
||||||
|
import 'package:pshared/provider/permissions.dart';
|
||||||
|
import 'package:pshared/provider/account.dart';
|
||||||
import 'package:pshared/provider/organizations.dart';
|
import 'package:pshared/provider/organizations.dart';
|
||||||
|
|
||||||
import 'package:pweb/app/app.dart';
|
import 'package:pweb/app/app.dart';
|
||||||
import 'package:pweb/app/timeago.dart';
|
import 'package:pweb/app/timeago.dart';
|
||||||
import 'package:pweb/providers/carousel.dart';
|
import 'package:pweb/providers/carousel.dart';
|
||||||
import 'package:pweb/providers/mock_payment.dart';
|
import 'package:pweb/providers/mock_payment.dart';
|
||||||
import 'package:pweb/providers/permissions.dart';
|
|
||||||
import 'package:pweb/providers/operatioins.dart';
|
import 'package:pweb/providers/operatioins.dart';
|
||||||
import 'package:pweb/providers/account.dart';
|
|
||||||
import 'package:pweb/providers/page_selector.dart';
|
import 'package:pweb/providers/page_selector.dart';
|
||||||
import 'package:pweb/providers/payment_methods.dart';
|
import 'package:pweb/providers/payment_methods.dart';
|
||||||
import 'package:pweb/providers/recipient.dart';
|
import 'package:pweb/providers/recipient.dart';
|
||||||
@@ -32,8 +32,6 @@ import 'package:pweb/services/payments/upload_history.dart';
|
|||||||
import 'package:pweb/services/recipient/recipient.dart';
|
import 'package:pweb/services/recipient/recipient.dart';
|
||||||
import 'package:pweb/services/wallet_transactions.dart';
|
import 'package:pweb/services/wallet_transactions.dart';
|
||||||
import 'package:pweb/services/wallets.dart';
|
import 'package:pweb/services/wallets.dart';
|
||||||
import 'package:pweb/services/accounts.dart';
|
|
||||||
import 'package:pweb/services/permissions.dart';
|
|
||||||
|
|
||||||
|
|
||||||
void _setupLogging() {
|
void _setupLogging() {
|
||||||
@@ -58,12 +56,17 @@ void main() async {
|
|||||||
MultiProvider(
|
MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
ChangeNotifierProvider(create: (_) => LocaleProvider(null)),
|
ChangeNotifierProvider(create: (_) => LocaleProvider(null)),
|
||||||
ChangeNotifierProvider(create: (_) => PermissionsProvider(service: PermissionsService())),
|
ChangeNotifierProxyProvider<LocaleProvider, AccountProvider>(
|
||||||
ChangeNotifierProvider(
|
create: (_) => AccountProvider(),
|
||||||
create: (context) => AccountProvider(
|
update: (context, localeProvider, provider) => provider!..updateProvider(localeProvider),
|
||||||
accountsService: AccountsService(),
|
|
||||||
permissionsProvider: context.read<PermissionsProvider>(),
|
|
||||||
),
|
),
|
||||||
|
ChangeNotifierProxyProvider<AccountProvider, TwoFactorProvider>(
|
||||||
|
create: (_) => TwoFactorProvider(),
|
||||||
|
update: (context, accountProvider, provider) => provider!..update(accountProvider),
|
||||||
|
),
|
||||||
|
ChangeNotifierProxyProvider<OrganizationsProvider, PermissionsProvider>(
|
||||||
|
create: (_) => PermissionsProvider(),
|
||||||
|
update: (context, orgnization, provider) => provider!..update(orgnization),
|
||||||
),
|
),
|
||||||
ChangeNotifierProvider(create: (_) => TwoFactorProvider()),
|
ChangeNotifierProvider(create: (_) => TwoFactorProvider()),
|
||||||
ChangeNotifierProvider(create: (_) => OrganizationsProvider()),
|
ChangeNotifierProvider(create: (_) => OrganizationsProvider()),
|
||||||
@@ -75,8 +78,9 @@ void main() async {
|
|||||||
ChangeNotifierProvider(
|
ChangeNotifierProvider(
|
||||||
create: (_) => PaymentMethodsProvider(service: MockPaymentMethodsService())..loadMethods(),
|
create: (_) => PaymentMethodsProvider(service: MockPaymentMethodsService())..loadMethods(),
|
||||||
),
|
),
|
||||||
ChangeNotifierProvider(
|
ChangeNotifierProxyProvider<OrganizationsProvider, WalletsProvider>(
|
||||||
create: (_) => WalletsProvider(MockWalletsService())..loadData(),
|
create: (_) => WalletsProvider(ApiWalletsService()),
|
||||||
|
update: (context, organizations, provider) => provider!..update(organizations),
|
||||||
),
|
),
|
||||||
ChangeNotifierProvider(
|
ChangeNotifierProvider(
|
||||||
create: (_) => WalletTransactionsProvider(MockWalletTransactionsService())..load(),
|
create: (_) => WalletTransactionsProvider(MockWalletTransactionsService())..load(),
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class Wallet {
|
|||||||
final double balance;
|
final double balance;
|
||||||
final Currency currency;
|
final Currency currency;
|
||||||
final bool isHidden;
|
final bool isHidden;
|
||||||
|
final DateTime calculatedAt;
|
||||||
|
|
||||||
Wallet({
|
Wallet({
|
||||||
required this.id,
|
required this.id,
|
||||||
@@ -15,6 +16,7 @@ class Wallet {
|
|||||||
required this.name,
|
required this.name,
|
||||||
required this.balance,
|
required this.balance,
|
||||||
required this.currency,
|
required this.currency,
|
||||||
|
required this.calculatedAt,
|
||||||
this.isHidden = true,
|
this.isHidden = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -25,14 +27,13 @@ class Wallet {
|
|||||||
Currency? currency,
|
Currency? currency,
|
||||||
String? walletUserID,
|
String? walletUserID,
|
||||||
bool? isHidden,
|
bool? isHidden,
|
||||||
}) {
|
}) => Wallet(
|
||||||
return Wallet(
|
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
name: name ?? this.name,
|
name: name ?? this.name,
|
||||||
balance: balance ?? this.balance,
|
balance: balance ?? this.balance,
|
||||||
currency: currency ?? this.currency,
|
currency: currency ?? this.currency,
|
||||||
walletUserID: walletUserID ?? this.walletUserID,
|
walletUserID: walletUserID ?? this.walletUserID,
|
||||||
isHidden: isHidden ?? this.isHidden,
|
isHidden: isHidden ?? this.isHidden,
|
||||||
|
calculatedAt: calculatedAt,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:pweb/pages/2fa/error_message.dart';
|
import 'package:pweb/pages/2fa/error_message.dart';
|
||||||
import 'package:pweb/pages/2fa/input.dart';
|
import 'package:pweb/pages/2fa/input.dart';
|
||||||
import 'package:pweb/pages/2fa/prompt.dart';
|
import 'package:pweb/pages/2fa/prompt.dart';
|
||||||
import 'package:pweb/pages/2fa/resend.dart';
|
import 'package:pweb/pages/2fa/resend.dart';
|
||||||
|
|
||||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
|
||||||
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:pweb/providers/two_factor.dart';
|
import 'package:pweb/providers/two_factor.dart';
|
||||||
|
|
||||||
|
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||||
|
|
||||||
|
|
||||||
class TwoFactorCodePage extends StatelessWidget {
|
class TwoFactorCodePage extends StatelessWidget {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:pweb/providers/account.dart';
|
import 'package:pshared/provider/account.dart';
|
||||||
|
|
||||||
import 'package:pweb/app/router/pages.dart';
|
import 'package:pweb/app/router/pages.dart';
|
||||||
import 'package:pweb/widgets/error/snackbar.dart';
|
import 'package:pweb/widgets/error/snackbar.dart';
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:pweb/providers/account.dart';
|
import 'package:pshared/provider/account.dart';
|
||||||
import 'package:pweb/providers/permissions.dart';
|
import 'package:pshared/provider/permissions.dart';
|
||||||
|
|
||||||
import 'package:pweb/app/router/pages.dart';
|
import 'package:pweb/app/router/pages.dart';
|
||||||
import 'package:pweb/widgets/error/snackbar.dart';
|
import 'package:pweb/widgets/error/snackbar.dart';
|
||||||
@@ -29,9 +29,9 @@ class PermissionsLoader extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
navigateAndReplace(context, Pages.login);
|
navigateAndReplace(context, Pages.login);
|
||||||
}
|
}
|
||||||
if (provider.error == null && !provider.hasLoaded && accountProvider.account != null) {
|
if (provider.error == null && !provider.isReady && accountProvider.account != null) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
provider.loadForAccount(accountProvider.account!.id);
|
provider.load();
|
||||||
});
|
});
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/provider/account.dart';
|
||||||
import 'package:pshared/provider/locale.dart';
|
import 'package:pshared/provider/locale.dart';
|
||||||
import 'package:pweb/providers/account.dart';
|
|
||||||
|
|
||||||
import 'package:pweb/app/router/pages.dart';
|
import 'package:pweb/app/router/pages.dart';
|
||||||
import 'package:pweb/pages/login/buttons.dart';
|
import 'package:pweb/pages/login/buttons.dart';
|
||||||
@@ -44,6 +44,7 @@ class _LoginFormState extends State<LoginForm> {
|
|||||||
locale: context.read<LocaleProvider>().locale.languageCode,
|
locale: context.read<LocaleProvider>().locale.languageCode,
|
||||||
);
|
);
|
||||||
if (outcome.isPending) {
|
if (outcome.isPending) {
|
||||||
|
// TODO: fix context usage
|
||||||
navigateAndReplace(context, Pages.sfactor);
|
navigateAndReplace(context, Pages.sfactor);
|
||||||
} else {
|
} else {
|
||||||
onLogin();
|
onLogin();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:pweb/providers/account.dart';
|
import 'package:pshared/provider/account.dart';
|
||||||
|
|
||||||
import 'package:pweb/widgets/vspacer.dart';
|
import 'package:pweb/widgets/vspacer.dart';
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user