+signup +login
Some checks failed
ci/woodpecker/push/bff Pipeline was successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/billing_fees Pipeline was successful
ci/woodpecker/push/chain_gateway Pipeline was successful
ci/woodpecker/push/fx_ingestor Pipeline was successful
ci/woodpecker/push/frontend Pipeline was successful
ci/woodpecker/push/fx_oracle Pipeline was successful
ci/woodpecker/push/nats Pipeline was successful
ci/woodpecker/push/ledger Pipeline was successful
ci/woodpecker/push/notification Pipeline was successful
ci/woodpecker/push/payments_orchestrator Pipeline was successful
ci/woodpecker/push/bump_version Pipeline failed
Some checks failed
ci/woodpecker/push/bff Pipeline was successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/billing_fees Pipeline was successful
ci/woodpecker/push/chain_gateway Pipeline was successful
ci/woodpecker/push/fx_ingestor Pipeline was successful
ci/woodpecker/push/frontend Pipeline was successful
ci/woodpecker/push/fx_oracle Pipeline was successful
ci/woodpecker/push/nats Pipeline was successful
ci/woodpecker/push/ledger Pipeline was successful
ci/woodpecker/push/notification Pipeline was successful
ci/woodpecker/push/payments_orchestrator Pipeline was successful
ci/woodpecker/push/bump_version Pipeline failed
This commit is contained in:
@@ -22,9 +22,9 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251110112254-48a6e677648f // indirect
|
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251117160429-c598d23eddcf // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bits-and-blooms/bitset v1.24.3 // indirect
|
github.com/bits-and-blooms/bitset v1.24.4 // indirect
|
||||||
github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect
|
github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect
|
||||||
github.com/casbin/casbin/v2 v2.134.0 // indirect
|
github.com/casbin/casbin/v2 v2.134.0 // indirect
|
||||||
github.com/casbin/govaluate v1.10.0 // indirect
|
github.com/casbin/govaluate v1.10.0 // indirect
|
||||||
|
|||||||
@@ -8,12 +8,16 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo
|
|||||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251110112254-48a6e677648f h1:B/TfTw73mVqWKDzJZhU9Qi9wQyYfmiCz9FnmpQsyv5M=
|
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251110112254-48a6e677648f h1:B/TfTw73mVqWKDzJZhU9Qi9wQyYfmiCz9FnmpQsyv5M=
|
||||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251110112254-48a6e677648f/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
|
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251110112254-48a6e677648f/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
|
||||||
|
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251117160429-c598d23eddcf h1:aZI2VRIP0LAI6Rw934WEAxxL0SNYSVt9vR9h/cP5Pbo=
|
||||||
|
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251117160429-c598d23eddcf/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
|
||||||
github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0=
|
github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0=
|
||||||
github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
|
github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bits-and-blooms/bitset v1.24.3 h1:Bte86SlO3lwPQqww+7BE9ZuUCKIjfqnG5jtEyqA9y9Y=
|
github.com/bits-and-blooms/bitset v1.24.3 h1:Bte86SlO3lwPQqww+7BE9ZuUCKIjfqnG5jtEyqA9y9Y=
|
||||||
github.com/bits-and-blooms/bitset v1.24.3/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
github.com/bits-and-blooms/bitset v1.24.3/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||||
|
github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE=
|
||||||
|
github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||||
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||||
github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE=
|
github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE=
|
||||||
github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ type AccountBase struct {
|
|||||||
storable.Base `bson:",inline" json:",inline"`
|
storable.Base `bson:",inline" json:",inline"`
|
||||||
ArchivableBase `bson:",inline" json:",inline"`
|
ArchivableBase `bson:",inline" json:",inline"`
|
||||||
Describable `bson:",inline" json:",inline"`
|
Describable `bson:",inline" json:",inline"`
|
||||||
|
LastName string `bson:"lastName" json:"lastName"`
|
||||||
AvatarURL *string `bson:"avatarUrl,omitempty" json:"avatarUrl,omitempty"`
|
AvatarURL *string `bson:"avatarUrl,omitempty" json:"avatarUrl,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ type LoginData struct {
|
|||||||
type AccountData struct {
|
type AccountData struct {
|
||||||
LoginData `bson:",inline" json:",inline"`
|
LoginData `bson:",inline" json:",inline"`
|
||||||
Describable `bson:",inline" json:",inline"`
|
Describable `bson:",inline" json:",inline"`
|
||||||
|
LastName string `bson:"lastName" json:"lastName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ad *AccountData) ToAccount() *Account {
|
func (ad *AccountData) ToAccount() *Account {
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ replace github.com/tech/sendico/chain/gateway => ../chain/gateway
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aws/aws-sdk-go-v2 v1.39.6
|
github.com/aws/aws-sdk-go-v2 v1.39.6
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.31.18
|
github.com/aws/aws-sdk-go-v2/config v1.31.20
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.22
|
github.com/aws/aws-sdk-go-v2/credentials v1.18.24
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.0
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2
|
||||||
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
|
||||||
@@ -49,9 +49,9 @@ require (
|
|||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.1 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.3 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5 // indirect
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.40.0 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sts v1.40.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
|
||||||
|
|||||||
@@ -12,8 +12,12 @@ github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 h1:DHctwEM8P8iTXFxC
|
|||||||
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.31.18 h1:RouG3AcF2fLFhw+Z0qbnuIl9HZ0Kh4E/U9sKwTMRpMI=
|
github.com/aws/aws-sdk-go-v2/config v1.31.18 h1:RouG3AcF2fLFhw+Z0qbnuIl9HZ0Kh4E/U9sKwTMRpMI=
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.31.18/go.mod h1:aXZ13mSQC8S2VEHwGfL1COMuJ1Zty6pX5xU7hyqjvCg=
|
github.com/aws/aws-sdk-go-v2/config v1.31.18/go.mod h1:aXZ13mSQC8S2VEHwGfL1COMuJ1Zty6pX5xU7hyqjvCg=
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.31.20 h1:/jWF4Wu90EhKCgjTdy1DGxcbcbNrjfBHvksEL79tfQc=
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.31.20/go.mod h1:95Hh1Tc5VYKL9NJ7tAkDcqeKt+MCXQB1hQZaRdJIZE0=
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.22 h1:hyIVGBHhQPaNP9D4BaVRwpjLMCwMMdAkHqB3gGMiykU=
|
github.com/aws/aws-sdk-go-v2/credentials v1.18.22 h1:hyIVGBHhQPaNP9D4BaVRwpjLMCwMMdAkHqB3gGMiykU=
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.22/go.mod h1:B9E2qHs3/YGfeQZ4jrIE/nPvqxtyafZrJ5EQiZBG6pk=
|
github.com/aws/aws-sdk-go-v2/credentials v1.18.22/go.mod h1:B9E2qHs3/YGfeQZ4jrIE/nPvqxtyafZrJ5EQiZBG6pk=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.18.24 h1:iJ2FmPT35EaIB0+kMa6TnQ+PwG5A1prEdAw+PsMzfHg=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.18.24/go.mod h1:U91+DrfjAiXPDEGYhh/x29o4p0qHX5HDqG7y5VViv64=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 h1:T1brd5dR3/fzNFAQch/iBKeX07/ffu/cLu+q+RuzEWk=
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 h1:T1brd5dR3/fzNFAQch/iBKeX07/ffu/cLu+q+RuzEWk=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13/go.mod h1:Peg/GBAQ6JDt+RoBf4meB1wylmAipb7Kg2ZFakZTlwk=
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13/go.mod h1:Peg/GBAQ6JDt+RoBf4meB1wylmAipb7Kg2ZFakZTlwk=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 h1:a+8/MLcWlIxo1lF9xaGt3J/u3yOZx+CdSveSNwjhD40=
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 h1:a+8/MLcWlIxo1lF9xaGt3J/u3yOZx+CdSveSNwjhD40=
|
||||||
@@ -34,12 +38,20 @@ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 h1:zhBJXdhWIFZ1a
|
|||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13/go.mod h1:JaaOeCE368qn2Hzi3sEzY6FgAZVCIYcC2nwbro2QCh8=
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13/go.mod h1:JaaOeCE368qn2Hzi3sEzY6FgAZVCIYcC2nwbro2QCh8=
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.0 h1:ef6gIJR+xv/JQWwpa5FYirzoQctfSJm7tuDe3SZsUf8=
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.0 h1:ef6gIJR+xv/JQWwpa5FYirzoQctfSJm7tuDe3SZsUf8=
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.0/go.mod h1:+wArOOrcHUevqdto9k1tKOF5++YTe9JEcPSc9Tx2ZSw=
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.0/go.mod h1:+wArOOrcHUevqdto9k1tKOF5++YTe9JEcPSc9Tx2ZSw=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2 h1:DhdbtDl4FdNlj31+xiRXANxEE+eC7n8JQz+/ilwQ8Uc=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2/go.mod h1:+wArOOrcHUevqdto9k1tKOF5++YTe9JEcPSc9Tx2ZSw=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.1 h1:0JPwLz1J+5lEOfy/g0SURC9cxhbQ1lIMHMa+AHZSzz0=
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.1 h1:0JPwLz1J+5lEOfy/g0SURC9cxhbQ1lIMHMa+AHZSzz0=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.1/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k=
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.1/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.3 h1:NjShtS1t8r5LUfFVtFeI8xLAHQNTa7UI0VawXlrBMFQ=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.3/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k=
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5 h1:OWs0/j2UYR5LOGi88sD5/lhN6TDLG6SfA7CqsQO9zF0=
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5 h1:OWs0/j2UYR5LOGi88sD5/lhN6TDLG6SfA7CqsQO9zF0=
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo=
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7 h1:gTsnx0xXNQ6SBbymoDvcoRHL+q4l/dAFsQuKfDWSaGc=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.40.0 h1:ZGDJVmlpPFiNFCb/I42nYVKUanJAdFUiSmUo/32AqPQ=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.40.0 h1:ZGDJVmlpPFiNFCb/I42nYVKUanJAdFUiSmUo/32AqPQ=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.40.0/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.40.0/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.40.2 h1:HK5ON3KmQV2HcAunnx4sKLB9aPf3gKGwVAf7xnx0QT0=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.40.2/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk=
|
||||||
github.com/aws/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=
|
||||||
|
|||||||
@@ -6,7 +6,5 @@ type Signup struct {
|
|||||||
Account model.AccountData `json:"account"`
|
Account model.AccountData `json:"account"`
|
||||||
Organization model.Describable `json:"organization"`
|
Organization model.Describable `json:"organization"`
|
||||||
OrganizationTimeZone string `json:"organizationTimeZone"`
|
OrganizationTimeZone string `json:"organizationTimeZone"`
|
||||||
AnonymousUser model.Describable `json:"anonymousUser"`
|
|
||||||
OwnerRole model.Describable `json:"ownerRole"`
|
OwnerRole model.Describable `json:"ownerRole"`
|
||||||
AnonymousRole model.Describable `json:"anonymousRole"`
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,15 +28,9 @@ func TestSignupRequest_JSONSerialization(t *testing.T) {
|
|||||||
Name: "Test Organization",
|
Name: "Test Organization",
|
||||||
},
|
},
|
||||||
OrganizationTimeZone: "UTC",
|
OrganizationTimeZone: "UTC",
|
||||||
AnonymousUser: model.Describable{
|
|
||||||
Name: "Anonymous User",
|
|
||||||
},
|
|
||||||
OwnerRole: model.Describable{
|
OwnerRole: model.Describable{
|
||||||
Name: "Owner",
|
Name: "Owner",
|
||||||
},
|
},
|
||||||
AnonymousRole: model.Describable{
|
|
||||||
Name: "Anonymous",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test JSON marshaling
|
// Test JSON marshaling
|
||||||
@@ -55,9 +49,7 @@ func TestSignupRequest_JSONSerialization(t *testing.T) {
|
|||||||
assert.Equal(t, signup.Account.Password, unmarshaled.Account.Password)
|
assert.Equal(t, signup.Account.Password, unmarshaled.Account.Password)
|
||||||
assert.Equal(t, signup.Organization.Name, unmarshaled.Organization.Name)
|
assert.Equal(t, signup.Organization.Name, unmarshaled.Organization.Name)
|
||||||
assert.Equal(t, signup.OrganizationTimeZone, unmarshaled.OrganizationTimeZone)
|
assert.Equal(t, signup.OrganizationTimeZone, unmarshaled.OrganizationTimeZone)
|
||||||
assert.Equal(t, signup.AnonymousUser.Name, unmarshaled.AnonymousUser.Name)
|
|
||||||
assert.Equal(t, signup.OwnerRole.Name, unmarshaled.OwnerRole.Name)
|
assert.Equal(t, signup.OwnerRole.Name, unmarshaled.OwnerRole.Name)
|
||||||
assert.Equal(t, signup.AnonymousRole.Name, unmarshaled.AnonymousRole.Name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSignupRequest_MinimalValidRequest(t *testing.T) {
|
func TestSignupRequest_MinimalValidRequest(t *testing.T) {
|
||||||
@@ -77,15 +69,9 @@ func TestSignupRequest_MinimalValidRequest(t *testing.T) {
|
|||||||
Name: "Test Organization",
|
Name: "Test Organization",
|
||||||
},
|
},
|
||||||
OrganizationTimeZone: "UTC",
|
OrganizationTimeZone: "UTC",
|
||||||
AnonymousUser: model.Describable{
|
|
||||||
Name: "Anonymous",
|
|
||||||
},
|
|
||||||
OwnerRole: model.Describable{
|
OwnerRole: model.Describable{
|
||||||
Name: "Owner",
|
Name: "Owner",
|
||||||
},
|
},
|
||||||
AnonymousRole: model.Describable{
|
|
||||||
Name: "Anonymous",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test JSON marshaling
|
// Test JSON marshaling
|
||||||
@@ -141,15 +127,9 @@ func TestSignupRequest_UnicodeCharacters(t *testing.T) {
|
|||||||
Name: "测试 Organization",
|
Name: "测试 Organization",
|
||||||
},
|
},
|
||||||
OrganizationTimeZone: "UTC",
|
OrganizationTimeZone: "UTC",
|
||||||
AnonymousUser: model.Describable{
|
|
||||||
Name: "匿名 User",
|
|
||||||
},
|
|
||||||
OwnerRole: model.Describable{
|
OwnerRole: model.Describable{
|
||||||
Name: "所有者",
|
Name: "所有者",
|
||||||
},
|
},
|
||||||
AnonymousRole: model.Describable{
|
|
||||||
Name: "匿名",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test JSON marshaling
|
// Test JSON marshaling
|
||||||
@@ -166,7 +146,5 @@ func TestSignupRequest_UnicodeCharacters(t *testing.T) {
|
|||||||
assert.Equal(t, "测试@example.com", unmarshaled.Account.Login)
|
assert.Equal(t, "测试@example.com", unmarshaled.Account.Login)
|
||||||
assert.Equal(t, "Test 用户 Üser", unmarshaled.Account.Name)
|
assert.Equal(t, "Test 用户 Üser", unmarshaled.Account.Name)
|
||||||
assert.Equal(t, "测试 Organization", unmarshaled.Organization.Name)
|
assert.Equal(t, "测试 Organization", unmarshaled.Organization.Name)
|
||||||
assert.Equal(t, "匿名 User", unmarshaled.AnonymousUser.Name)
|
|
||||||
assert.Equal(t, "所有者", unmarshaled.OwnerRole.Name)
|
assert.Equal(t, "所有者", unmarshaled.OwnerRole.Name)
|
||||||
assert.Equal(t, "匿名", unmarshaled.AnonymousRole.Name)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,27 +22,6 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *AccountAPI) createAnonymousAccount(ctx context.Context, org *model.Organization, sr *srequest.Signup) error {
|
|
||||||
anonymousUser := &model.Account{
|
|
||||||
AccountPublic: model.AccountPublic{
|
|
||||||
AccountBase: model.AccountBase{
|
|
||||||
Describable: sr.AnonymousUser,
|
|
||||||
},
|
|
||||||
UserDataBase: sr.Account.UserDataBase,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
r, err := a.pmanager.Role().Create(ctx, org.ID, &sr.AnonymousRole)
|
|
||||||
if err != nil {
|
|
||||||
a.logger.Warn("Failed to create anonymous role", zap.Error(err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := a.accService.CreateAccount(ctx, org, anonymousUser, r.ID); err != nil {
|
|
||||||
a.logger.Warn("Failed to create account", zap.Error(err), zap.String("login", anonymousUser.Login))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AccountAPI) createOrg(ctx context.Context, sr *srequest.Signup, permissionRef primitive.ObjectID) (*model.Organization, error) {
|
func (a *AccountAPI) createOrg(ctx context.Context, sr *srequest.Signup, permissionRef primitive.ObjectID) (*model.Organization, error) {
|
||||||
name := strings.TrimSpace(sr.Organization.Name)
|
name := strings.TrimSpace(sr.Organization.Name)
|
||||||
if name == "" {
|
if name == "" {
|
||||||
@@ -175,10 +154,6 @@ func (a *AccountAPI) signupTransactionBody(ctx context.Context, sr *srequest.Sig
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := a.createAnonymousAccount(ctx, org, sr); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,15 +73,9 @@ func TestSignupRequestSerialization(t *testing.T) {
|
|||||||
Name: "Test Organization",
|
Name: "Test Organization",
|
||||||
},
|
},
|
||||||
OrganizationTimeZone: "UTC",
|
OrganizationTimeZone: "UTC",
|
||||||
AnonymousUser: model.Describable{
|
|
||||||
Name: "Anonymous User",
|
|
||||||
},
|
|
||||||
OwnerRole: model.Describable{
|
OwnerRole: model.Describable{
|
||||||
Name: "Owner",
|
Name: "Owner",
|
||||||
},
|
},
|
||||||
AnonymousRole: model.Describable{
|
|
||||||
Name: "Anonymous",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store in MongoDB
|
// Store in MongoDB
|
||||||
@@ -121,15 +115,9 @@ func TestSignupHTTPSerialization(t *testing.T) {
|
|||||||
Name: "Test Organization",
|
Name: "Test Organization",
|
||||||
},
|
},
|
||||||
OrganizationTimeZone: "UTC",
|
OrganizationTimeZone: "UTC",
|
||||||
AnonymousUser: model.Describable{
|
|
||||||
Name: "Anonymous User",
|
|
||||||
},
|
|
||||||
OwnerRole: model.Describable{
|
OwnerRole: model.Describable{
|
||||||
Name: "Owner",
|
Name: "Owner",
|
||||||
},
|
},
|
||||||
AnonymousRole: model.Describable{
|
|
||||||
Name: "Anonymous",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("ValidJSONRequest", func(t *testing.T) {
|
t.Run("ValidJSONRequest", func(t *testing.T) {
|
||||||
|
|||||||
@@ -74,15 +74,9 @@ func TestCreateValidSignupRequest(t *testing.T) {
|
|||||||
Name: "Test Organization",
|
Name: "Test Organization",
|
||||||
},
|
},
|
||||||
OrganizationTimeZone: "UTC",
|
OrganizationTimeZone: "UTC",
|
||||||
AnonymousUser: model.Describable{
|
|
||||||
Name: "Anonymous User",
|
|
||||||
},
|
|
||||||
OwnerRole: model.Describable{
|
OwnerRole: model.Describable{
|
||||||
Name: "Owner",
|
Name: "Owner",
|
||||||
},
|
},
|
||||||
AnonymousRole: model.Describable{
|
|
||||||
Name: "Anonymous",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the request structure
|
// Validate the request structure
|
||||||
|
|||||||
@@ -21,9 +21,21 @@ fi
|
|||||||
|
|
||||||
CURRENT_VERSION="$(cat "${VERSION_FILE}")"
|
CURRENT_VERSION="$(cat "${VERSION_FILE}")"
|
||||||
NEXT_VERSION="$(printf '%s' "${CURRENT_VERSION}" | awk -F. -v OFS=. '
|
NEXT_VERSION="$(printf '%s' "${CURRENT_VERSION}" | awk -F. -v OFS=. '
|
||||||
|
function pad(value, width, result, i) {
|
||||||
|
result=value ""
|
||||||
|
if (length(result) >= width) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
i = width - length(result)
|
||||||
|
while (i-- > 0) {
|
||||||
|
result = "0" result
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
NF==1 { print ++$NF; next }
|
NF==1 { print ++$NF; next }
|
||||||
{
|
{
|
||||||
$NF=sprintf("%0*d", length($NF), ($NF+1))
|
last = $NF + 1
|
||||||
|
$NF = pad(last, length($NF))
|
||||||
print
|
print
|
||||||
}')"
|
}')"
|
||||||
|
|
||||||
|
|||||||
BIN
frontend/.DS_Store
vendored
Normal file
BIN
frontend/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
frontend/pshared/lib/.DS_Store
vendored
Normal file
BIN
frontend/pshared/lib/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
frontend/pshared/lib/api/.DS_Store
vendored
Normal file
BIN
frontend/pshared/lib/api/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -1,2 +1,22 @@
|
|||||||
class AuthorizationFailed implements Exception {
|
class AuthenticationFailedException implements Exception {
|
||||||
|
final String message;
|
||||||
|
final Exception? originalError;
|
||||||
|
|
||||||
|
const AuthenticationFailedException(this.message, [this.originalError]);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'AuthenticationFailedException: $message${originalError != null ? ' (caused by: $originalError)' : ''}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CircuitBreakerOpenException implements Exception {
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
const CircuitBreakerOpenException(this.message);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'CircuitBreakerOpenException: $message';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
BIN
frontend/pshared/lib/api/requests/.DS_Store
vendored
Normal file
BIN
frontend/pshared/lib/api/requests/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -1,20 +1,18 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/api/requests/login_data.dart';
|
||||||
|
|
||||||
part 'login.g.dart';
|
part 'login.g.dart';
|
||||||
|
|
||||||
|
|
||||||
@JsonSerializable(explicitToJson: true)
|
@JsonSerializable(explicitToJson: true)
|
||||||
class LoginRequest {
|
class LoginRequest {
|
||||||
final String login;
|
final LoginData login;
|
||||||
final String password;
|
|
||||||
final String locale;
|
|
||||||
final String clientId;
|
final String clientId;
|
||||||
final String deviceId;
|
final String deviceId;
|
||||||
|
|
||||||
const LoginRequest({
|
const LoginRequest({
|
||||||
required this.login,
|
required this.login,
|
||||||
required this.password,
|
|
||||||
required this.locale,
|
|
||||||
required this.clientId,
|
required this.clientId,
|
||||||
required this.deviceId,
|
required this.deviceId,
|
||||||
});
|
});
|
||||||
|
|||||||
76
frontend/pshared/lib/api/requests/login_data.dart
Normal file
76
frontend/pshared/lib/api/requests/login_data.dart
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'login_data.g.dart';
|
||||||
|
|
||||||
|
|
||||||
|
@JsonSerializable(explicitToJson: true, constructor: 'build')
|
||||||
|
class LoginData {
|
||||||
|
final String login;
|
||||||
|
final String password;
|
||||||
|
final String locale;
|
||||||
|
|
||||||
|
const LoginData._({
|
||||||
|
required this.login,
|
||||||
|
required this.password,
|
||||||
|
required this.locale,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory LoginData.build({
|
||||||
|
required String login,
|
||||||
|
required String password,
|
||||||
|
required String locale,
|
||||||
|
}) => LoginData._(
|
||||||
|
login: login.trim().toLowerCase(),
|
||||||
|
password: password,
|
||||||
|
locale: locale,
|
||||||
|
);
|
||||||
|
|
||||||
|
factory LoginData.fromJson(Map<String, dynamic> json) => _$LoginDataFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() => _$LoginDataToJson(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable(explicitToJson: true, constructor: 'buildIstance')
|
||||||
|
class AccountData extends LoginData {
|
||||||
|
final String name;
|
||||||
|
final String lastName;
|
||||||
|
|
||||||
|
const AccountData._({
|
||||||
|
required super.login,
|
||||||
|
required super.password,
|
||||||
|
required super.locale,
|
||||||
|
required this.name,
|
||||||
|
required this.lastName,
|
||||||
|
}) : super._();
|
||||||
|
|
||||||
|
factory AccountData.buildIstance({
|
||||||
|
required String login,
|
||||||
|
required String password,
|
||||||
|
required String locale,
|
||||||
|
required String name,
|
||||||
|
required String lastName,
|
||||||
|
}) => AccountData._(
|
||||||
|
login: login,
|
||||||
|
password: password,
|
||||||
|
locale: locale,
|
||||||
|
name: name.trim(),
|
||||||
|
lastName: lastName.trim(),
|
||||||
|
);
|
||||||
|
|
||||||
|
factory AccountData.build({
|
||||||
|
required LoginData login,
|
||||||
|
required String name,
|
||||||
|
required String lastName,
|
||||||
|
}) => AccountData.buildIstance(
|
||||||
|
login: login.login,
|
||||||
|
password: login.password,
|
||||||
|
locale: login.locale,
|
||||||
|
name: name,
|
||||||
|
lastName: lastName,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
factory AccountData.fromJson(Map<String, dynamic> json) => _$AccountDataFromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() => _$AccountDataToJson(this);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
part 'change_password.g.dart';
|
part 'change.g.dart';
|
||||||
|
|
||||||
|
|
||||||
@JsonSerializable(explicitToJson: true)
|
@JsonSerializable(explicitToJson: true)
|
||||||
20
frontend/pshared/lib/api/requests/password/forgot.dart
Normal file
20
frontend/pshared/lib/api/requests/password/forgot.dart
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'forgot.g.dart';
|
||||||
|
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class ForgotPasswordRequest {
|
||||||
|
final String login;
|
||||||
|
|
||||||
|
const ForgotPasswordRequest({
|
||||||
|
required this.login,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory ForgotPasswordRequest.fromJson(Map<String, dynamic> json) => _$ForgotPasswordRequestFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() => _$ForgotPasswordRequestToJson(this);
|
||||||
|
|
||||||
|
static ForgotPasswordRequest build({
|
||||||
|
required String login,
|
||||||
|
}) => ForgotPasswordRequest(login: login);
|
||||||
|
}
|
||||||
20
frontend/pshared/lib/api/requests/password/reset.dart
Normal file
20
frontend/pshared/lib/api/requests/password/reset.dart
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'reset.g.dart';
|
||||||
|
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class ResetPasswordRequest {
|
||||||
|
final String password;
|
||||||
|
|
||||||
|
const ResetPasswordRequest({
|
||||||
|
required this.password,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory ResetPasswordRequest.fromJson(Map<String, dynamic> json) => _$ResetPasswordRequestFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() => _$ResetPasswordRequestToJson(this);
|
||||||
|
|
||||||
|
static ResetPasswordRequest build({
|
||||||
|
required String password,
|
||||||
|
}) => ResetPasswordRequest(password: password);
|
||||||
|
}
|
||||||
@@ -1,82 +1,39 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/api/requests/login_data.dart';
|
||||||
|
import 'package:pshared/data/dto/describable.dart';
|
||||||
|
import 'package:pshared/data/mapper/describable.dart';
|
||||||
|
import 'package:pshared/models/describable.dart';
|
||||||
|
|
||||||
part 'signup.g.dart';
|
part 'signup.g.dart';
|
||||||
|
|
||||||
|
|
||||||
@JsonSerializable(explicitToJson: true)
|
@JsonSerializable(explicitToJson: true)
|
||||||
class SignupRequest {
|
class SignupRequest {
|
||||||
final SignupAccount account;
|
final AccountData account;
|
||||||
final DescribableRequest organization;
|
final DescribableDTO organization;
|
||||||
final String organizationTimeZone;
|
final String organizationTimeZone;
|
||||||
final DescribableRequest anonymousUser;
|
final DescribableDTO ownerRole;
|
||||||
final DescribableRequest ownerRole;
|
|
||||||
final DescribableRequest anonymousRole;
|
|
||||||
|
|
||||||
const SignupRequest({
|
const SignupRequest({
|
||||||
required this.account,
|
required this.account,
|
||||||
required this.organization,
|
required this.organization,
|
||||||
required this.organizationTimeZone,
|
required this.organizationTimeZone,
|
||||||
required this.anonymousUser,
|
|
||||||
required this.ownerRole,
|
required this.ownerRole,
|
||||||
required this.anonymousRole,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
factory SignupRequest.build({
|
factory SignupRequest.build({
|
||||||
required String name,
|
required AccountData account,
|
||||||
required String login,
|
required Describable organization,
|
||||||
required String password,
|
|
||||||
required String locale,
|
|
||||||
required String organizationName,
|
|
||||||
required String organizationTimeZone,
|
required String organizationTimeZone,
|
||||||
}) =>
|
required Describable ownerRole,
|
||||||
SignupRequest(
|
}) => SignupRequest(
|
||||||
account: SignupAccount(
|
account: account,
|
||||||
name: name,
|
organization: organization.toDTO(),
|
||||||
login: login,
|
organizationTimeZone: organizationTimeZone,
|
||||||
password: password,
|
ownerRole: ownerRole.toDTO(),
|
||||||
locale: locale,
|
);
|
||||||
),
|
|
||||||
organization: DescribableRequest(name: organizationName),
|
|
||||||
organizationTimeZone: organizationTimeZone,
|
|
||||||
anonymousUser: const DescribableRequest(name: 'Anonymous'),
|
|
||||||
ownerRole: const DescribableRequest(name: 'Owner'),
|
|
||||||
anonymousRole: const DescribableRequest(name: 'Anonymous'),
|
|
||||||
);
|
|
||||||
|
|
||||||
factory SignupRequest.fromJson(Map<String, dynamic> json) =>
|
factory SignupRequest.fromJson(Map<String, dynamic> json) => _$SignupRequestFromJson(json);
|
||||||
_$SignupRequestFromJson(json);
|
|
||||||
Map<String, dynamic> toJson() => _$SignupRequestToJson(this);
|
Map<String, dynamic> toJson() => _$SignupRequestToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonSerializable()
|
|
||||||
class SignupAccount {
|
|
||||||
final String name;
|
|
||||||
final String login;
|
|
||||||
final String password;
|
|
||||||
final String locale;
|
|
||||||
final String? description;
|
|
||||||
|
|
||||||
const SignupAccount({
|
|
||||||
required this.name,
|
|
||||||
required this.login,
|
|
||||||
required this.password,
|
|
||||||
required this.locale,
|
|
||||||
this.description,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory SignupAccount.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$SignupAccountFromJson(json);
|
|
||||||
Map<String, dynamic> toJson() => _$SignupAccountToJson(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonSerializable()
|
|
||||||
class DescribableRequest {
|
|
||||||
final String name;
|
|
||||||
final String? description;
|
|
||||||
|
|
||||||
const DescribableRequest({required this.name, this.description});
|
|
||||||
|
|
||||||
factory DescribableRequest.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$DescribableRequestFromJson(json);
|
|
||||||
Map<String, dynamic> toJson() => _$DescribableRequestToJson(this);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
import 'package:pshared/data/dto/account/base.dart';
|
import 'package:pshared/data/dto/account/base.dart';
|
||||||
|
import 'package:pshared/data/dto/date_time.dart';
|
||||||
|
|
||||||
part 'account.g.dart';
|
part 'account.g.dart';
|
||||||
|
|
||||||
@@ -8,14 +9,17 @@ part 'account.g.dart';
|
|||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class AccountDTO extends AccountBaseDTO {
|
class AccountDTO extends AccountBaseDTO {
|
||||||
final String login;
|
final String login;
|
||||||
|
final String locale;
|
||||||
|
|
||||||
const AccountDTO({
|
const AccountDTO({
|
||||||
required super.id,
|
required super.id,
|
||||||
required super.createdAt,
|
required super.createdAt,
|
||||||
required super.updatedAt,
|
required super.updatedAt,
|
||||||
required super.name,
|
required super.name,
|
||||||
|
required super.lastName,
|
||||||
|
required super.description,
|
||||||
required super.avatarUrl,
|
required super.avatarUrl,
|
||||||
required super.locale,
|
required this.locale,
|
||||||
required this.login,
|
required this.login,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
import 'package:pshared/data/dto/storable.dart';
|
import 'package:pshared/data/dto/storable.dart';
|
||||||
|
import 'package:pshared/data/dto/date_time.dart';
|
||||||
|
|
||||||
part 'base.g.dart';
|
part 'base.g.dart';
|
||||||
|
|
||||||
@@ -8,7 +9,8 @@ part 'base.g.dart';
|
|||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class AccountBaseDTO extends StorableDTO {
|
class AccountBaseDTO extends StorableDTO {
|
||||||
final String name;
|
final String name;
|
||||||
final String locale;
|
final String lastName;
|
||||||
|
final String? description;
|
||||||
final String? avatarUrl;
|
final String? avatarUrl;
|
||||||
|
|
||||||
const AccountBaseDTO({
|
const AccountBaseDTO({
|
||||||
@@ -16,8 +18,9 @@ class AccountBaseDTO extends StorableDTO {
|
|||||||
required super.createdAt,
|
required super.createdAt,
|
||||||
required super.updatedAt,
|
required super.updatedAt,
|
||||||
required this.name,
|
required this.name,
|
||||||
|
required this.description,
|
||||||
required this.avatarUrl,
|
required this.avatarUrl,
|
||||||
required this.locale,
|
required this.lastName,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory AccountBaseDTO.fromJson(Map<String, dynamic> json) => _$AccountBaseDTOFromJson(json);
|
factory AccountBaseDTO.fromJson(Map<String, dynamic> json) => _$AccountBaseDTOFromJson(json);
|
||||||
|
|||||||
12
frontend/pshared/lib/data/dto/date_time.dart
Normal file
12
frontend/pshared/lib/data/dto/date_time.dart
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
|
||||||
|
class UtcIso8601Converter implements JsonConverter<DateTime, String> {
|
||||||
|
const UtcIso8601Converter();
|
||||||
|
|
||||||
|
@override
|
||||||
|
DateTime fromJson(String json) => DateTime.parse(json).toUtc();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toJson(DateTime value) => value.toUtc().toIso8601String();
|
||||||
|
}
|
||||||
18
frontend/pshared/lib/data/dto/describable.dart
Normal file
18
frontend/pshared/lib/data/dto/describable.dart
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'describable.g.dart';
|
||||||
|
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class DescribableDTO {
|
||||||
|
final String name;
|
||||||
|
final String? description;
|
||||||
|
|
||||||
|
const DescribableDTO({
|
||||||
|
required this.name,
|
||||||
|
this.description,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory DescribableDTO.fromJson(Map<String, dynamic> json) => _$DescribableDTOFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() => _$DescribableDTOToJson(this);
|
||||||
|
}
|
||||||
@@ -1,18 +1,28 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:pshared/data/dto/storable.dart';
|
|
||||||
|
import 'package:pshared/data/dto/date_time.dart';
|
||||||
|
import 'package:pshared/data/dto/permissions/bound.dart';
|
||||||
|
|
||||||
part 'organization.g.dart';
|
part 'organization.g.dart';
|
||||||
|
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class OrganizationDTO extends StorableDTO {
|
class OrganizationDTO extends PermissionBoundDTO {
|
||||||
|
final String name;
|
||||||
|
final String? description;
|
||||||
final String timeZone;
|
final String timeZone;
|
||||||
final String? logoUrl;
|
final String? logoUrl;
|
||||||
|
final String tenantRef;
|
||||||
|
|
||||||
const OrganizationDTO({
|
const OrganizationDTO({
|
||||||
required super.id,
|
required super.id,
|
||||||
required super.createdAt,
|
required super.createdAt,
|
||||||
required super.updatedAt,
|
required super.updatedAt,
|
||||||
|
required super.permissionRef,
|
||||||
|
required super.organizationRef,
|
||||||
|
required this.name,
|
||||||
|
required this.tenantRef,
|
||||||
|
this.description,
|
||||||
required this.timeZone,
|
required this.timeZone,
|
||||||
this.logoUrl,
|
this.logoUrl,
|
||||||
});
|
});
|
||||||
|
|||||||
23
frontend/pshared/lib/data/dto/organization/bound.dart
Normal file
23
frontend/pshared/lib/data/dto/organization/bound.dart
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/data/dto/storable.dart';
|
||||||
|
import 'package:pshared/data/dto/date_time.dart';
|
||||||
|
|
||||||
|
part 'bound.g.dart';
|
||||||
|
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class OrganizationBoundDTO extends StorableDTO {
|
||||||
|
final String organizationRef;
|
||||||
|
|
||||||
|
const OrganizationBoundDTO({
|
||||||
|
required super.id,
|
||||||
|
required super.createdAt,
|
||||||
|
required super.updatedAt,
|
||||||
|
required this.organizationRef,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory OrganizationBoundDTO.fromJson(Map<String, dynamic> json) => _$OrganizationBoundDTOFromJson(json);
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() => _$OrganizationBoundDTOToJson(this);
|
||||||
|
}
|
||||||
@@ -1,13 +1,17 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/data/dto/describable.dart';
|
||||||
|
|
||||||
part 'description.g.dart';
|
part 'description.g.dart';
|
||||||
|
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class OrganizationDescriptionDTO {
|
class OrganizationDescriptionDTO {
|
||||||
|
final DescribableDTO description;
|
||||||
final String? logoUrl;
|
final String? logoUrl;
|
||||||
|
|
||||||
const OrganizationDescriptionDTO({
|
const OrganizationDescriptionDTO({
|
||||||
|
required this.description,
|
||||||
this.logoUrl,
|
this.logoUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
26
frontend/pshared/lib/data/dto/permissions/bound.dart
Normal file
26
frontend/pshared/lib/data/dto/permissions/bound.dart
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/data/dto/date_time.dart';
|
||||||
|
import 'package:pshared/data/dto/storable.dart';
|
||||||
|
|
||||||
|
part 'bound.g.dart';
|
||||||
|
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class PermissionBoundDTO extends StorableDTO {
|
||||||
|
final String permissionRef;
|
||||||
|
final String organizationRef;
|
||||||
|
|
||||||
|
const PermissionBoundDTO({
|
||||||
|
required super.id,
|
||||||
|
required super.createdAt,
|
||||||
|
required super.updatedAt,
|
||||||
|
required this.permissionRef,
|
||||||
|
required this.organizationRef,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory PermissionBoundDTO.fromJson(Map<String, dynamic> json) => _$PermissionBoundDTOFromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() => _$PermissionBoundDTOToJson(this);
|
||||||
|
}
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:pshared/data/dto/storable.dart';
|
|
||||||
|
import 'package:pshared/data/dto/storable/describable.dart';
|
||||||
|
import 'package:pshared/data/dto/date_time.dart';
|
||||||
import 'package:pshared/models/resources.dart';
|
import 'package:pshared/models/resources.dart';
|
||||||
|
|
||||||
part 'policy.g.dart';
|
part 'policy.g.dart';
|
||||||
|
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class PolicyDescriptionDTO extends StorableDTO {
|
class PolicyDescriptionDTO extends StorableDescribabaleDTO {
|
||||||
final List<ResourceType>? resourceTypes;
|
final List<ResourceType>? resourceTypes;
|
||||||
final String? organizationRef;
|
final String? organizationRef;
|
||||||
|
|
||||||
@@ -14,6 +16,8 @@ class PolicyDescriptionDTO extends StorableDTO {
|
|||||||
required super.id,
|
required super.id,
|
||||||
required super.createdAt,
|
required super.createdAt,
|
||||||
required super.updatedAt,
|
required super.updatedAt,
|
||||||
|
required super.name,
|
||||||
|
required super.description,
|
||||||
required this.resourceTypes,
|
required this.resourceTypes,
|
||||||
required this.organizationRef,
|
required this.organizationRef,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:pshared/data/dto/storable.dart';
|
|
||||||
|
import 'package:pshared/data/dto/date_time.dart';
|
||||||
|
import 'package:pshared/data/dto/storable/describable.dart';
|
||||||
|
|
||||||
part 'role.g.dart';
|
part 'role.g.dart';
|
||||||
|
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class RoleDescriptionDTO extends StorableDTO {
|
class RoleDescriptionDTO extends StorableDescribabaleDTO {
|
||||||
final String organizationRef;
|
final String organizationRef;
|
||||||
|
|
||||||
const RoleDescriptionDTO({
|
const RoleDescriptionDTO({
|
||||||
required super.id,
|
required super.id,
|
||||||
required super.createdAt,
|
required super.createdAt,
|
||||||
required super.updatedAt,
|
required super.updatedAt,
|
||||||
|
required super.name,
|
||||||
|
required super.description,
|
||||||
required this.organizationRef,
|
required this.organizationRef,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
16
frontend/pshared/lib/data/dto/reference.dart
Normal file
16
frontend/pshared/lib/data/dto/reference.dart
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'reference.g.dart';
|
||||||
|
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class ReferenceDTO {
|
||||||
|
final String ref;
|
||||||
|
|
||||||
|
const ReferenceDTO({
|
||||||
|
required this.ref,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory ReferenceDTO.fromJson(Map<String, dynamic> json) => _$ReferenceDTOFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() => _$ReferenceDTOToJson(this);
|
||||||
|
}
|
||||||
@@ -1,12 +1,18 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/data/dto/date_time.dart';
|
||||||
|
|
||||||
part 'storable.g.dart';
|
part 'storable.g.dart';
|
||||||
|
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class StorableDTO {
|
class StorableDTO {
|
||||||
final String id;
|
final String id;
|
||||||
|
|
||||||
|
@UtcIso8601Converter()
|
||||||
final DateTime createdAt;
|
final DateTime createdAt;
|
||||||
|
|
||||||
|
@UtcIso8601Converter()
|
||||||
final DateTime updatedAt;
|
final DateTime updatedAt;
|
||||||
|
|
||||||
const StorableDTO({
|
const StorableDTO({
|
||||||
|
|||||||
26
frontend/pshared/lib/data/dto/storable/describable.dart
Normal file
26
frontend/pshared/lib/data/dto/storable/describable.dart
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/data/dto/date_time.dart';
|
||||||
|
import 'package:pshared/data/dto/storable.dart';
|
||||||
|
|
||||||
|
part 'describable.g.dart';
|
||||||
|
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class StorableDescribabaleDTO extends StorableDTO {
|
||||||
|
final String name;
|
||||||
|
final String? description;
|
||||||
|
|
||||||
|
const StorableDescribabaleDTO({
|
||||||
|
required super.id,
|
||||||
|
required super.createdAt,
|
||||||
|
required super.updatedAt,
|
||||||
|
required this.name,
|
||||||
|
this.description,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory StorableDescribabaleDTO.fromJson(Map<String, dynamic> json) => _$StorableDescribabaleDTOFromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() => _$StorableDescribabaleDTOToJson(this);
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:pshared/data/dto/account/account.dart';
|
import 'package:pshared/data/dto/account/account.dart';
|
||||||
import 'package:pshared/models/account/account.dart';
|
import 'package:pshared/models/account/account.dart';
|
||||||
|
import 'package:pshared/models/describable.dart';
|
||||||
import 'package:pshared/models/storable.dart';
|
import 'package:pshared/models/storable.dart';
|
||||||
|
|
||||||
|
|
||||||
@@ -9,6 +10,8 @@ extension AccountMapper on Account {
|
|||||||
createdAt: createdAt,
|
createdAt: createdAt,
|
||||||
updatedAt: updatedAt,
|
updatedAt: updatedAt,
|
||||||
name: name,
|
name: name,
|
||||||
|
lastName: lastName,
|
||||||
|
description: description,
|
||||||
avatarUrl: avatarUrl,
|
avatarUrl: avatarUrl,
|
||||||
locale: locale,
|
locale: locale,
|
||||||
login: login,
|
login: login,
|
||||||
@@ -19,8 +22,9 @@ extension AccountDTOMapper on AccountDTO {
|
|||||||
Account toDomain() => Account(
|
Account toDomain() => Account(
|
||||||
storable: newStorable(id: id, createdAt: createdAt, updatedAt: updatedAt),
|
storable: newStorable(id: id, createdAt: createdAt, updatedAt: updatedAt),
|
||||||
avatarUrl: avatarUrl,
|
avatarUrl: avatarUrl,
|
||||||
|
describable: newDescribable(name: name, description: description),
|
||||||
|
lastName: lastName,
|
||||||
locale: locale,
|
locale: locale,
|
||||||
login: login,
|
login: login,
|
||||||
name: name,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:pshared/data/dto/account/base.dart';
|
import 'package:pshared/data/dto/account/base.dart';
|
||||||
import 'package:pshared/models/account/base.dart';
|
import 'package:pshared/models/account/base.dart';
|
||||||
|
import 'package:pshared/models/describable.dart';
|
||||||
import 'package:pshared/models/storable.dart';
|
import 'package:pshared/models/storable.dart';
|
||||||
|
|
||||||
|
|
||||||
@@ -8,17 +9,18 @@ extension AccountBaseMapper on AccountBase {
|
|||||||
id: storable.id,
|
id: storable.id,
|
||||||
createdAt: storable.createdAt,
|
createdAt: storable.createdAt,
|
||||||
updatedAt: storable.updatedAt,
|
updatedAt: storable.updatedAt,
|
||||||
|
name: describable.name,
|
||||||
|
description: describable.description,
|
||||||
|
lastName: lastName,
|
||||||
avatarUrl: avatarUrl,
|
avatarUrl: avatarUrl,
|
||||||
name: name,
|
|
||||||
locale: locale,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AccountDTOMapper on AccountBaseDTO {
|
extension AccountDTOMapper on AccountBaseDTO {
|
||||||
AccountBase toDomain() => AccountBase(
|
AccountBase toDomain() => AccountBase(
|
||||||
storable: newStorable(id: id, createdAt: createdAt, updatedAt: updatedAt),
|
storable: newStorable(id: id, createdAt: createdAt, updatedAt: updatedAt),
|
||||||
|
describable: newDescribable(name: name, description: description),
|
||||||
|
lastName: lastName,
|
||||||
avatarUrl: avatarUrl,
|
avatarUrl: avatarUrl,
|
||||||
name: name,
|
|
||||||
locale: locale,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
17
frontend/pshared/lib/data/mapper/describable.dart
Normal file
17
frontend/pshared/lib/data/mapper/describable.dart
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import 'package:pshared/data/dto/describable.dart';
|
||||||
|
import 'package:pshared/models/describable.dart';
|
||||||
|
|
||||||
|
|
||||||
|
extension DescribableMapper on Describable {
|
||||||
|
DescribableDTO toDTO() => DescribableDTO(
|
||||||
|
name: name,
|
||||||
|
description: description,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DescribableDTOMapper on DescribableDTO {
|
||||||
|
Describable toDomain() => newDescribable(
|
||||||
|
name: name,
|
||||||
|
description: description,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
import 'package:pshared/data/dto/organization.dart';
|
import 'package:pshared/data/dto/organization.dart';
|
||||||
|
import 'package:pshared/models/describable.dart';
|
||||||
|
import 'package:pshared/models/organization/bound.dart';
|
||||||
import 'package:pshared/models/organization/organization.dart';
|
import 'package:pshared/models/organization/organization.dart';
|
||||||
|
import 'package:pshared/models/permissions/bound.dart';
|
||||||
import 'package:pshared/models/storable.dart';
|
import 'package:pshared/models/storable.dart';
|
||||||
|
|
||||||
|
|
||||||
@@ -8,15 +11,26 @@ extension OrganizationMapper on Organization {
|
|||||||
id: storable.id,
|
id: storable.id,
|
||||||
createdAt: storable.createdAt,
|
createdAt: storable.createdAt,
|
||||||
updatedAt: storable.updatedAt,
|
updatedAt: storable.updatedAt,
|
||||||
|
name: describable.name,
|
||||||
|
description: describable.description,
|
||||||
timeZone: timeZone,
|
timeZone: timeZone,
|
||||||
logoUrl: logoUrl,
|
logoUrl: logoUrl,
|
||||||
|
organizationRef: permissionBound.organizationRef,
|
||||||
|
permissionRef: permissionBound.permissionRef,
|
||||||
|
tenantRef: tenantRef,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
extension OrganizationDTOMapper on OrganizationDTO {
|
extension OrganizationDTOMapper on OrganizationDTO {
|
||||||
Organization toDomain() => Organization(
|
Organization toDomain() => Organization(
|
||||||
storable: newStorable(id: id, createdAt: createdAt, updatedAt: updatedAt),
|
storable: newStorable(id: id, createdAt: createdAt, updatedAt: updatedAt),
|
||||||
|
describable: newDescribable(name: name, description: description),
|
||||||
timeZone: timeZone,
|
timeZone: timeZone,
|
||||||
logoUrl: logoUrl,
|
logoUrl: logoUrl,
|
||||||
|
permissionBound: newPermissionBound(
|
||||||
|
organizationBound: newOrganizationBound(organizationRef: organizationRef),
|
||||||
|
permissionRef: permissionRef,
|
||||||
|
),
|
||||||
|
tenantRef: tenantRef,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
18
frontend/pshared/lib/data/mapper/organization/bound.dart
Normal file
18
frontend/pshared/lib/data/mapper/organization/bound.dart
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import 'package:pshared/data/dto/organization/bound.dart';
|
||||||
|
import 'package:pshared/models/organization/bound.dart';
|
||||||
|
|
||||||
|
|
||||||
|
extension OrganizationBoundMapper on OrganizationBound {
|
||||||
|
OrganizationBoundDTO toDTO() => OrganizationBoundDTO(
|
||||||
|
id: '', // OrganizationBound doesn't have storable fields, so we need to provide defaults
|
||||||
|
createdAt: DateTime.now(),
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
organizationRef: organizationRef,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
extension OrganizationBoundDTOMapper on OrganizationBoundDTO {
|
||||||
|
OrganizationBound toDomain() => newOrganizationBound(
|
||||||
|
organizationRef: organizationRef,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import 'package:pshared/data/dto/organization/description.dart';
|
import 'package:pshared/data/dto/organization/description.dart';
|
||||||
|
import 'package:pshared/data/mapper/describable.dart';
|
||||||
import 'package:pshared/models/organization/description.dart';
|
import 'package:pshared/models/organization/description.dart';
|
||||||
|
|
||||||
|
|
||||||
extension OrganizationDescriptionMapper on OrganizationDescription {
|
extension OrganizationDescriptionMapper on OrganizationDescription {
|
||||||
OrganizationDescriptionDTO toDTO() => OrganizationDescriptionDTO(
|
OrganizationDescriptionDTO toDTO() => OrganizationDescriptionDTO(
|
||||||
|
description: description.toDTO(),
|
||||||
logoUrl: logoUrl,
|
logoUrl: logoUrl,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -11,5 +13,6 @@ extension OrganizationDescriptionMapper on OrganizationDescription {
|
|||||||
extension AccountDescriptionDTOMapper on OrganizationDescriptionDTO {
|
extension AccountDescriptionDTOMapper on OrganizationDescriptionDTO {
|
||||||
OrganizationDescription toDomain() => OrganizationDescription(
|
OrganizationDescription toDomain() => OrganizationDescription(
|
||||||
logoUrl: logoUrl,
|
logoUrl: logoUrl,
|
||||||
|
description: description.toDomain(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:pshared/data/dto/permissions/description/policy.dart';
|
import 'package:pshared/data/dto/permissions/description/policy.dart';
|
||||||
|
import 'package:pshared/models/describable.dart';
|
||||||
import 'package:pshared/models/permissions/descriptions/policy.dart';
|
import 'package:pshared/models/permissions/descriptions/policy.dart';
|
||||||
import 'package:pshared/models/storable.dart';
|
import 'package:pshared/models/storable.dart';
|
||||||
|
|
||||||
@@ -8,6 +9,8 @@ extension PolicyDescriptionMapper on PolicyDescription {
|
|||||||
id: storable.id,
|
id: storable.id,
|
||||||
createdAt: storable.createdAt,
|
createdAt: storable.createdAt,
|
||||||
updatedAt: storable.updatedAt,
|
updatedAt: storable.updatedAt,
|
||||||
|
name: describable.name,
|
||||||
|
description: describable.description,
|
||||||
resourceTypes: resourceTypes,
|
resourceTypes: resourceTypes,
|
||||||
organizationRef: organizationRef,
|
organizationRef: organizationRef,
|
||||||
);
|
);
|
||||||
@@ -16,6 +19,7 @@ extension PolicyDescriptionMapper on PolicyDescription {
|
|||||||
extension PolicyDescriptionDTOMapper on PolicyDescriptionDTO {
|
extension PolicyDescriptionDTOMapper on PolicyDescriptionDTO {
|
||||||
PolicyDescription toDomain() => PolicyDescription(
|
PolicyDescription toDomain() => PolicyDescription(
|
||||||
storable: newStorable(id: id, createdAt: createdAt, updatedAt: createdAt),
|
storable: newStorable(id: id, createdAt: createdAt, updatedAt: createdAt),
|
||||||
|
describable: newDescribable(name: name, description: description),
|
||||||
resourceTypes: resourceTypes,
|
resourceTypes: resourceTypes,
|
||||||
organizationRef: organizationRef,
|
organizationRef: organizationRef,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:pshared/data/dto/permissions/description/role.dart';
|
import 'package:pshared/data/dto/permissions/description/role.dart';
|
||||||
|
import 'package:pshared/models/describable.dart';
|
||||||
import 'package:pshared/models/permissions/descriptions/role.dart';
|
import 'package:pshared/models/permissions/descriptions/role.dart';
|
||||||
import 'package:pshared/models/storable.dart';
|
import 'package:pshared/models/storable.dart';
|
||||||
|
|
||||||
@@ -8,6 +9,8 @@ extension RoleDescriptionMapper on RoleDescription {
|
|||||||
id: storable.id,
|
id: storable.id,
|
||||||
createdAt: storable.createdAt,
|
createdAt: storable.createdAt,
|
||||||
updatedAt: storable.updatedAt,
|
updatedAt: storable.updatedAt,
|
||||||
|
name: describable.name,
|
||||||
|
description: describable.description,
|
||||||
organizationRef: organizationRef,
|
organizationRef: organizationRef,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -15,6 +18,7 @@ extension RoleDescriptionMapper on RoleDescription {
|
|||||||
extension RoleDescriptionDTOMapper on RoleDescriptionDTO {
|
extension RoleDescriptionDTOMapper on RoleDescriptionDTO {
|
||||||
RoleDescription toDomain() => RoleDescription(
|
RoleDescription toDomain() => RoleDescription(
|
||||||
storable: newStorable(id: id, createdAt: createdAt, updatedAt: updatedAt),
|
storable: newStorable(id: id, createdAt: createdAt, updatedAt: updatedAt),
|
||||||
|
describable: newDescribable(name: name, description: description),
|
||||||
organizationRef: organizationRef,
|
organizationRef: organizationRef,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
11
frontend/pshared/lib/data/mapper/reference.dart
Normal file
11
frontend/pshared/lib/data/mapper/reference.dart
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import 'package:pshared/data/dto/reference.dart';
|
||||||
|
import 'package:pshared/models/reference.dart';
|
||||||
|
|
||||||
|
|
||||||
|
extension ReferenceMapper on Reference {
|
||||||
|
ReferenceDTO toDTO() => ReferenceDTO(ref: ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ReferenceDTOMapper on ReferenceDTO {
|
||||||
|
Reference toDomain() => newReference(ref: ref);
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ import 'package:pshared/models/storable.dart';
|
|||||||
|
|
||||||
|
|
||||||
extension StorableMapper on Storable {
|
extension StorableMapper on Storable {
|
||||||
StorableDTO toDTO() => StorableDTO(id: id, createdAt: createdAt, updatedAt: updatedAt);
|
StorableDTO toDTO() => StorableDTO(id: id, createdAt: createdAt.toUtc(), updatedAt: updatedAt.toUtc());
|
||||||
}
|
}
|
||||||
|
|
||||||
extension StorableDTOMapper on StorableDTO {
|
extension StorableDTOMapper on StorableDTO {
|
||||||
|
|||||||
BIN
frontend/pshared/lib/models/.DS_Store
vendored
Normal file
BIN
frontend/pshared/lib/models/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -1,36 +1,35 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
import 'package:pshared/models/account/base.dart';
|
import 'package:pshared/models/account/base.dart';
|
||||||
|
import 'package:pshared/models/describable.dart';
|
||||||
|
|
||||||
|
|
||||||
|
@immutable
|
||||||
class Account extends AccountBase {
|
class Account extends AccountBase {
|
||||||
final String login;
|
final String login;
|
||||||
|
final String locale;
|
||||||
|
|
||||||
const Account({
|
const Account({
|
||||||
required super.storable,
|
required super.storable,
|
||||||
|
required super.describable,
|
||||||
required super.avatarUrl,
|
required super.avatarUrl,
|
||||||
|
required super.lastName,
|
||||||
required this.login,
|
required this.login,
|
||||||
required super.locale,
|
required this.locale,
|
||||||
required super.name,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Account.fromBase(AccountBase accountBase, String login) => Account(
|
|
||||||
storable: accountBase.storable,
|
|
||||||
avatarUrl: accountBase.avatarUrl,
|
|
||||||
locale: accountBase.locale,
|
|
||||||
name: accountBase.name,
|
|
||||||
login: login,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Account copyWith({
|
Account copyWith({
|
||||||
|
Describable? describable,
|
||||||
|
String? lastName,
|
||||||
String? Function()? avatarUrl,
|
String? Function()? avatarUrl,
|
||||||
String? name,
|
|
||||||
String? locale,
|
String? locale,
|
||||||
}) {
|
}) => Account(
|
||||||
final updatedBase = super.copyWith(
|
storable: storable,
|
||||||
avatarUrl: avatarUrl,
|
describable: describableCopyWithOther(this.describable, describable),
|
||||||
name: name,
|
lastName: lastName ?? this.lastName,
|
||||||
locale: locale,
|
avatarUrl: avatarUrl != null ? avatarUrl() : this.avatarUrl,
|
||||||
);
|
login: login,
|
||||||
return Account.fromBase(updatedBase, login);
|
locale: locale ?? this.locale,
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,16 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/models/describable.dart';
|
||||||
import 'package:pshared/models/storable.dart';
|
import 'package:pshared/models/storable.dart';
|
||||||
|
import 'package:pshared/models/storable/describable.dart';
|
||||||
|
import 'package:pshared/utils/name_initials.dart';
|
||||||
|
|
||||||
|
|
||||||
class AccountBase implements Storable {
|
@immutable
|
||||||
|
class AccountBase implements StorableDescribable {
|
||||||
final Storable storable;
|
final Storable storable;
|
||||||
|
final Describable describable;
|
||||||
|
final String lastName;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get id => storable.id;
|
String get id => storable.id;
|
||||||
@@ -10,26 +18,30 @@ class AccountBase implements Storable {
|
|||||||
DateTime get createdAt => storable.createdAt;
|
DateTime get createdAt => storable.createdAt;
|
||||||
@override
|
@override
|
||||||
DateTime get updatedAt => storable.updatedAt;
|
DateTime get updatedAt => storable.updatedAt;
|
||||||
|
@override
|
||||||
|
String get name => describable.name;
|
||||||
|
@override
|
||||||
|
String? get description => describable.description;
|
||||||
|
|
||||||
final String? avatarUrl;
|
final String? avatarUrl;
|
||||||
final String name;
|
|
||||||
final String locale;
|
|
||||||
|
|
||||||
const AccountBase({
|
const AccountBase({
|
||||||
required this.storable,
|
required this.storable,
|
||||||
required this.name,
|
required this.describable,
|
||||||
required this.locale,
|
|
||||||
required this.avatarUrl,
|
required this.avatarUrl,
|
||||||
|
required this.lastName,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
String get nameInitials => getNameInitials(describable.name);
|
||||||
|
|
||||||
AccountBase copyWith({
|
AccountBase copyWith({
|
||||||
|
Describable? describable,
|
||||||
|
String? lastName,
|
||||||
String? Function()? avatarUrl,
|
String? Function()? avatarUrl,
|
||||||
String? name,
|
|
||||||
String? locale,
|
|
||||||
}) => AccountBase(
|
}) => AccountBase(
|
||||||
storable: storable,
|
storable: storable,
|
||||||
avatarUrl: avatarUrl != null ? avatarUrl() : this.avatarUrl,
|
avatarUrl: avatarUrl != null ? avatarUrl() : this.avatarUrl,
|
||||||
locale: locale ?? this.locale,
|
describable: describable ?? this.describable,
|
||||||
name: name ?? this.name,
|
lastName: lastName ?? this.lastName,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
39
frontend/pshared/lib/models/describable.dart
Normal file
39
frontend/pshared/lib/models/describable.dart
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
|
||||||
|
abstract class Describable {
|
||||||
|
String get name;
|
||||||
|
String? get description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class _DescribableImp implements Describable {
|
||||||
|
@override
|
||||||
|
final String name;
|
||||||
|
@override
|
||||||
|
final String? description;
|
||||||
|
|
||||||
|
const _DescribableImp({
|
||||||
|
required this.name,
|
||||||
|
required this.description,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Describable newDescribable({required String name, String? description}) =>
|
||||||
|
_DescribableImp(name: name, description: description);
|
||||||
|
|
||||||
|
|
||||||
|
extension DescribableCopier on Describable {
|
||||||
|
Describable copyWith({
|
||||||
|
String? name,
|
||||||
|
String? Function()? description,
|
||||||
|
}) => newDescribable(
|
||||||
|
name: name ?? this.name,
|
||||||
|
description: description != null ? description() : this.description,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Describable describableCopyWithOther(Describable current, Describable? other) => current.copyWith(
|
||||||
|
name: other?.name,
|
||||||
|
description: () => other?.description,
|
||||||
|
);
|
||||||
18
frontend/pshared/lib/models/organization/bound.dart
Normal file
18
frontend/pshared/lib/models/organization/bound.dart
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
|
||||||
|
abstract class OrganizationBound {
|
||||||
|
String get organizationRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class _OrganizationBoundImp implements OrganizationBound {
|
||||||
|
@override
|
||||||
|
final String organizationRef;
|
||||||
|
|
||||||
|
const _OrganizationBoundImp({
|
||||||
|
required this.organizationRef,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
OrganizationBound newOrganizationBound({ required String organizationRef }) => _OrganizationBoundImp(organizationRef: organizationRef);
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
|
import 'package:pshared/models/describable.dart';
|
||||||
|
|
||||||
|
|
||||||
class OrganizationDescription {
|
class OrganizationDescription {
|
||||||
|
final Describable description;
|
||||||
final String? logoUrl;
|
final String? logoUrl;
|
||||||
|
|
||||||
const OrganizationDescription({
|
const OrganizationDescription({
|
||||||
|
required this.description,
|
||||||
this.logoUrl,
|
this.logoUrl,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
|
import 'package:pshared/models/describable.dart';
|
||||||
|
import 'package:pshared/models/permissions/bound.dart';
|
||||||
|
import 'package:pshared/models/permissions/bound/describable.dart';
|
||||||
import 'package:pshared/models/storable.dart';
|
import 'package:pshared/models/storable.dart';
|
||||||
|
|
||||||
|
|
||||||
class Organization implements Storable {
|
class Organization implements PermissionBoundStorableDescribable {
|
||||||
final Storable storable;
|
final Storable storable;
|
||||||
|
final PermissionBound permissionBound;
|
||||||
|
final Describable describable;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get id => storable.id;
|
String get id => storable.id;
|
||||||
@@ -10,25 +15,39 @@ class Organization implements Storable {
|
|||||||
DateTime get createdAt => storable.createdAt;
|
DateTime get createdAt => storable.createdAt;
|
||||||
@override
|
@override
|
||||||
DateTime get updatedAt => storable.updatedAt;
|
DateTime get updatedAt => storable.updatedAt;
|
||||||
|
@override
|
||||||
|
String get organizationRef => permissionBound.organizationRef;
|
||||||
|
@override
|
||||||
|
String get permissionRef => permissionBound.permissionRef;
|
||||||
|
@override
|
||||||
|
String get name => describable.name;
|
||||||
|
@override
|
||||||
|
String? get description => describable.description;
|
||||||
|
|
||||||
final String timeZone;
|
final String timeZone;
|
||||||
final String? logoUrl;
|
final String? logoUrl;
|
||||||
|
final String tenantRef;
|
||||||
|
|
||||||
const Organization({
|
const Organization({
|
||||||
required this.storable,
|
required this.storable,
|
||||||
|
required this.describable,
|
||||||
required this.timeZone,
|
required this.timeZone,
|
||||||
|
required this.permissionBound,
|
||||||
|
required this.tenantRef,
|
||||||
this.logoUrl,
|
this.logoUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
Organization copyWith({
|
Organization copyWith({
|
||||||
String? name,
|
Describable? describable,
|
||||||
String? Function()? description,
|
|
||||||
String? timeZone,
|
String? timeZone,
|
||||||
String? Function()? logoUrl,
|
String? Function()? logoUrl,
|
||||||
}) => Organization(
|
}) => Organization(
|
||||||
storable: storable, // Same Storable, same id
|
storable: storable, // Same Storable, same id
|
||||||
|
describable: describableCopyWithOther(this.describable, describable),
|
||||||
timeZone: timeZone ?? this.timeZone,
|
timeZone: timeZone ?? this.timeZone,
|
||||||
logoUrl: logoUrl != null ? logoUrl() : this.logoUrl,
|
logoUrl: logoUrl != null ? logoUrl() : this.logoUrl,
|
||||||
|
permissionBound: permissionBound,
|
||||||
|
tenantRef: tenantRef,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
import 'package:pshared/config/constants.dart';
|
|
||||||
|
|
||||||
|
|
||||||
abstract class PermissionBound {
|
|
||||||
String get permissionRef;
|
|
||||||
String get organizationRef;
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PermissionBoundImp implements PermissionBound {
|
|
||||||
@override
|
|
||||||
final String permissionRef;
|
|
||||||
@override
|
|
||||||
final String organizationRef;
|
|
||||||
|
|
||||||
const _PermissionBoundImp({
|
|
||||||
required this.permissionRef,
|
|
||||||
required this.organizationRef,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
PermissionBound newPermissionBound({ required String organizationRef, String? permissionRef}) =>
|
|
||||||
_PermissionBoundImp(permissionRef: permissionRef ?? Constants.nilObjectRef, organizationRef: organizationRef);
|
|
||||||
32
frontend/pshared/lib/models/permissions/bound.dart
Normal file
32
frontend/pshared/lib/models/permissions/bound.dart
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/config/constants.dart';
|
||||||
|
import 'package:pshared/models/organization/bound.dart';
|
||||||
|
|
||||||
|
|
||||||
|
abstract class PermissionBound extends OrganizationBound {
|
||||||
|
String get permissionRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class _PermissionBoundImp implements PermissionBound {
|
||||||
|
@override
|
||||||
|
final String permissionRef;
|
||||||
|
final OrganizationBound organizationBound;
|
||||||
|
|
||||||
|
@override
|
||||||
|
get organizationRef => organizationBound.organizationRef;
|
||||||
|
|
||||||
|
const _PermissionBoundImp({
|
||||||
|
required this.permissionRef,
|
||||||
|
required this.organizationBound,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
PermissionBound newPermissionBound({
|
||||||
|
required OrganizationBound organizationBound,
|
||||||
|
String? permissionRef,
|
||||||
|
}) => _PermissionBoundImp(
|
||||||
|
permissionRef: permissionRef ?? Constants.nilObjectRef,
|
||||||
|
organizationBound: organizationBound,
|
||||||
|
);
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import 'package:pshared/models/permissions/bound/storable.dart';
|
||||||
|
import 'package:pshared/models/storable/describable.dart';
|
||||||
|
|
||||||
|
|
||||||
|
abstract class PermissionBoundStorableDescribable implements PermissionBoundStorable, StorableDescribable {
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:pshared/models/permission_bound.dart';
|
import 'package:pshared/models/permissions/bound.dart';
|
||||||
import 'package:pshared/models/storable.dart';
|
import 'package:pshared/models/storable.dart';
|
||||||
|
|
||||||
|
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
|
import 'package:pshared/models/describable.dart';
|
||||||
import 'package:pshared/models/resources.dart';
|
import 'package:pshared/models/resources.dart';
|
||||||
import 'package:pshared/models/storable.dart';
|
import 'package:pshared/models/storable.dart';
|
||||||
|
import 'package:pshared/models/storable/describable.dart';
|
||||||
|
|
||||||
|
|
||||||
class PolicyDescription implements Storable {
|
class PolicyDescription implements StorableDescribable {
|
||||||
final Storable storable;
|
final Storable storable;
|
||||||
|
final Describable describable;
|
||||||
final List<ResourceType>? resourceTypes;
|
final List<ResourceType>? resourceTypes;
|
||||||
final String? organizationRef;
|
final String? organizationRef;
|
||||||
|
|
||||||
@@ -13,9 +16,14 @@ class PolicyDescription implements Storable {
|
|||||||
DateTime get createdAt => storable.createdAt;
|
DateTime get createdAt => storable.createdAt;
|
||||||
@override
|
@override
|
||||||
DateTime get updatedAt => storable.updatedAt;
|
DateTime get updatedAt => storable.updatedAt;
|
||||||
|
@override
|
||||||
|
String get name => describable.name;
|
||||||
|
@override
|
||||||
|
String? get description => describable.description;
|
||||||
|
|
||||||
const PolicyDescription({
|
const PolicyDescription({
|
||||||
required this.storable,
|
required this.storable,
|
||||||
|
required this.describable,
|
||||||
required this.resourceTypes,
|
required this.resourceTypes,
|
||||||
required this.organizationRef,
|
required this.organizationRef,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
|
import 'package:pshared/models/describable.dart';
|
||||||
import 'package:pshared/models/storable.dart';
|
import 'package:pshared/models/storable.dart';
|
||||||
|
import 'package:pshared/models/storable/describable.dart';
|
||||||
|
|
||||||
|
|
||||||
class RoleDescription implements Storable {
|
class RoleDescription implements StorableDescribable {
|
||||||
final Storable storable;
|
final Storable storable;
|
||||||
|
final Describable describable;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get id => storable.id;
|
String get id => storable.id;
|
||||||
@@ -10,18 +13,25 @@ class RoleDescription implements Storable {
|
|||||||
DateTime get createdAt => storable.createdAt;
|
DateTime get createdAt => storable.createdAt;
|
||||||
@override
|
@override
|
||||||
DateTime get updatedAt => storable.updatedAt;
|
DateTime get updatedAt => storable.updatedAt;
|
||||||
|
@override
|
||||||
|
String get name => describable.name;
|
||||||
|
@override
|
||||||
|
String? get description => describable.description;
|
||||||
|
|
||||||
final String organizationRef;
|
final String organizationRef;
|
||||||
|
|
||||||
const RoleDescription({
|
const RoleDescription({
|
||||||
required this.storable,
|
required this.storable,
|
||||||
|
required this.describable,
|
||||||
required this.organizationRef,
|
required this.organizationRef,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory RoleDescription.build({
|
factory RoleDescription.build({
|
||||||
|
required Describable roleDescription,
|
||||||
required String organizationRef,
|
required String organizationRef,
|
||||||
}) => RoleDescription(
|
}) => RoleDescription(
|
||||||
storable: newStorable(),
|
storable: newStorable(),
|
||||||
|
describable: roleDescription,
|
||||||
organizationRef: organizationRef
|
organizationRef: organizationRef
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
22
frontend/pshared/lib/models/reference.dart
Normal file
22
frontend/pshared/lib/models/reference.dart
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
abstract class Reference {
|
||||||
|
String get ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ReferenceImp implements Reference {
|
||||||
|
@override
|
||||||
|
final String ref;
|
||||||
|
|
||||||
|
const _ReferenceImp({
|
||||||
|
required this.ref,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Reference newReference({required String ref}) => _ReferenceImp(ref: ref);
|
||||||
|
|
||||||
|
extension ReferenceCopier on Reference {
|
||||||
|
Reference copyWith({
|
||||||
|
String? ref,
|
||||||
|
}) => newReference(
|
||||||
|
ref: ref ?? this.ref,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import 'package:pshared/config/constants.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/config/web.dart';
|
||||||
|
|
||||||
|
|
||||||
abstract class Storable {
|
abstract class Storable {
|
||||||
@@ -7,6 +9,7 @@ abstract class Storable {
|
|||||||
DateTime get updatedAt;
|
DateTime get updatedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@immutable
|
||||||
class _StorableImp implements Storable {
|
class _StorableImp implements Storable {
|
||||||
@override
|
@override
|
||||||
final String id;
|
final String id;
|
||||||
|
|||||||
BIN
frontend/pshared/lib/models/storable/.DS_Store
vendored
Normal file
BIN
frontend/pshared/lib/models/storable/.DS_Store
vendored
Normal file
Binary file not shown.
6
frontend/pshared/lib/models/storable/describable.dart
Normal file
6
frontend/pshared/lib/models/storable/describable.dart
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import 'package:pshared/models/describable.dart';
|
||||||
|
import 'package:pshared/models/storable.dart';
|
||||||
|
|
||||||
|
|
||||||
|
abstract class StorableDescribable implements Storable, Describable {
|
||||||
|
}
|
||||||
@@ -4,21 +4,48 @@ import 'package:share_plus/share_plus.dart';
|
|||||||
|
|
||||||
import 'package:pshared/api/errors/unauthorized.dart';
|
import 'package:pshared/api/errors/unauthorized.dart';
|
||||||
import 'package:pshared/api/requests/signup.dart';
|
import 'package:pshared/api/requests/signup.dart';
|
||||||
|
import 'package:pshared/api/requests/login_data.dart';
|
||||||
|
import 'package:pshared/config/constants.dart';
|
||||||
import 'package:pshared/models/account/account.dart';
|
import 'package:pshared/models/account/account.dart';
|
||||||
import 'package:pshared/provider/exception.dart';
|
import 'package:pshared/models/describable.dart';
|
||||||
|
import 'package:pshared/models/storable.dart';
|
||||||
|
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/utils/exception.dart';
|
||||||
|
|
||||||
|
|
||||||
class AccountProvider extends ChangeNotifier {
|
class AccountProvider extends ChangeNotifier {
|
||||||
|
static String get currentUserRef => Constants.nilObjectRef;
|
||||||
|
|
||||||
// The resource now wraps our Account? state along with its loading/error state.
|
// The resource now wraps our Account? state along with its loading/error state.
|
||||||
Resource<Account?> _resource = Resource(data: null);
|
Resource<Account?> _resource = Resource(data: null);
|
||||||
Resource<Account?> get resource => _resource;
|
Resource<Account?> get resource => _resource;
|
||||||
|
late LocaleProvider _localeProvider;
|
||||||
|
|
||||||
Account? get account => _resource.data;
|
Account? get account => _resource.data;
|
||||||
bool get isLoggedIn => account != null;
|
bool get isLoggedIn => account != null;
|
||||||
bool get isLoading => _resource.isLoading;
|
bool get isLoading => _resource.isLoading;
|
||||||
Object? get error => _resource.error;
|
Object? get error => _resource.error;
|
||||||
|
bool get isReady => (!isLoading) && (account != null);
|
||||||
|
|
||||||
|
Account? currentUser() {
|
||||||
|
final acc = account;
|
||||||
|
if (acc == null) return null;
|
||||||
|
return Account(
|
||||||
|
storable: newStorable(
|
||||||
|
id: currentUserRef,
|
||||||
|
createdAt: acc.createdAt,
|
||||||
|
updatedAt: acc.updatedAt,
|
||||||
|
),
|
||||||
|
describable: acc.describable,
|
||||||
|
lastName: acc.lastName,
|
||||||
|
avatarUrl: acc.avatarUrl,
|
||||||
|
login: acc.login,
|
||||||
|
locale: acc.locale,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Private helper to update the resource and notify listeners.
|
// Private helper to update the resource and notify listeners.
|
||||||
void _setResource(Resource<Account?> newResource) {
|
void _setResource(Resource<Account?> newResource) {
|
||||||
@@ -26,16 +53,24 @@ class AccountProvider extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateProvider(LocaleProvider localeProvider) => _localeProvider = localeProvider;
|
||||||
|
|
||||||
Future<Account?> login({
|
void _pickupLocale(String locale) => _localeProvider.setLocale(Locale(locale));
|
||||||
|
|
||||||
|
Future<Account> login({
|
||||||
required String email,
|
required String email,
|
||||||
required String password,
|
required String password,
|
||||||
required String locale,
|
required String locale,
|
||||||
}) async {
|
}) async {
|
||||||
_setResource(_resource.copyWith(isLoading: true, error: null));
|
_setResource(_resource.copyWith(isLoading: true, error: null));
|
||||||
try {
|
try {
|
||||||
final acc = await AccountService.login(email, password, locale);
|
final acc = await AccountService.login(LoginData.build(
|
||||||
|
login: email,
|
||||||
|
password: password,
|
||||||
|
locale: locale,
|
||||||
|
));
|
||||||
_setResource(Resource(data: acc, isLoading: false));
|
_setResource(Resource(data: acc, isLoading: false));
|
||||||
|
_pickupLocale(acc.locale);
|
||||||
return acc;
|
return acc;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_setResource(_resource.copyWith(isLoading: false, error: toException(e)));
|
_setResource(_resource.copyWith(isLoading: false, error: toException(e)));
|
||||||
@@ -43,11 +78,14 @@ class AccountProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> isAuthorizationStored() async => AuthorizationService.isAuthorizationStored();
|
||||||
|
|
||||||
Future<Account?> restore() async {
|
Future<Account?> restore() async {
|
||||||
_setResource(_resource.copyWith(isLoading: true, error: null));
|
_setResource(_resource.copyWith(isLoading: true, error: null));
|
||||||
try {
|
try {
|
||||||
final acc = await AccountService.restore();
|
final acc = await AccountService.restore();
|
||||||
_setResource(Resource(data: acc, isLoading: false));
|
_setResource(Resource(data: acc, isLoading: false));
|
||||||
|
_pickupLocale(acc.locale);
|
||||||
return acc;
|
return acc;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_setResource(_resource.copyWith(isLoading: false, error: toException(e)));
|
_setResource(_resource.copyWith(isLoading: false, error: toException(e)));
|
||||||
@@ -55,24 +93,20 @@ class AccountProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> signup(
|
Future<void> signup({
|
||||||
String name,
|
required AccountData account,
|
||||||
String login,
|
required Describable organization,
|
||||||
String password,
|
required String timezone,
|
||||||
String locale,
|
required Describable ownerRole,
|
||||||
String organizationName,
|
}) async {
|
||||||
String timezone,
|
|
||||||
) async {
|
|
||||||
_setResource(_resource.copyWith(isLoading: true, error: null));
|
_setResource(_resource.copyWith(isLoading: true, error: null));
|
||||||
try {
|
try {
|
||||||
await AccountService.signup(
|
await AccountService.signup(
|
||||||
SignupRequest.build(
|
SignupRequest.build(
|
||||||
name: name,
|
account: account,
|
||||||
login: login.trim().toLowerCase(),
|
organization: organization,
|
||||||
password: password,
|
|
||||||
locale: locale,
|
|
||||||
organizationName: organizationName,
|
|
||||||
organizationTimeZone: timezone,
|
organizationTimeZone: timezone,
|
||||||
|
ownerRole: ownerRole,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
// Signup might not automatically log in the user,
|
// Signup might not automatically log in the user,
|
||||||
@@ -96,6 +130,7 @@ class AccountProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Account?> update({
|
Future<Account?> update({
|
||||||
|
Describable? describable,
|
||||||
String? locale,
|
String? locale,
|
||||||
String? avatarUrl,
|
String? avatarUrl,
|
||||||
String? notificationFrequency,
|
String? notificationFrequency,
|
||||||
@@ -105,6 +140,7 @@ class AccountProvider extends ChangeNotifier {
|
|||||||
try {
|
try {
|
||||||
final updated = await AccountService.update(
|
final updated = await AccountService.update(
|
||||||
account!.copyWith(
|
account!.copyWith(
|
||||||
|
describable: describable,
|
||||||
avatarUrl: () => avatarUrl ?? account!.avatarUrl,
|
avatarUrl: () => avatarUrl ?? account!.avatarUrl,
|
||||||
locale: locale ?? account!.locale,
|
locale: locale ?? account!.locale,
|
||||||
),
|
),
|
||||||
@@ -141,4 +177,26 @@ class AccountProvider extends ChangeNotifier {
|
|||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> forgotPassword(String email) async {
|
||||||
|
_setResource(_resource.copyWith(isLoading: true, error: null));
|
||||||
|
try {
|
||||||
|
await AccountService.forgotPassword(email);
|
||||||
|
_setResource(_resource.copyWith(isLoading: false));
|
||||||
|
} catch (e) {
|
||||||
|
_setResource(_resource.copyWith(isLoading: false, error: toException(e)));
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> resetPassword(String accountId, String token, String newPassword) async {
|
||||||
|
_setResource(_resource.copyWith(isLoading: true, error: null));
|
||||||
|
try {
|
||||||
|
await AccountService.resetPassword(accountId, token, newPassword);
|
||||||
|
_setResource(_resource.copyWith(isLoading: false));
|
||||||
|
} catch (e) {
|
||||||
|
_setResource(_resource.copyWith(isLoading: false, error: toException(e)));
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import 'package:collection/collection.dart';
|
|||||||
import 'package:pshared/config/constants.dart';
|
import 'package:pshared/config/constants.dart';
|
||||||
import 'package:pshared/models/organization/organization.dart';
|
import 'package:pshared/models/organization/organization.dart';
|
||||||
import 'package:pshared/provider/resource.dart';
|
import 'package:pshared/provider/resource.dart';
|
||||||
import 'package:pshared/provider/exception.dart';
|
|
||||||
import 'package:pshared/service/organization.dart';
|
import 'package:pshared/service/organization.dart';
|
||||||
import 'package:pshared/service/secure_storage.dart';
|
import 'package:pshared/service/secure_storage.dart';
|
||||||
|
import 'package:pshared/utils/exception.dart';
|
||||||
|
|
||||||
|
|
||||||
class OrganizationsProvider extends ChangeNotifier {
|
class OrganizationsProvider extends ChangeNotifier {
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import 'package:pshared/service/pfe/service.dart';
|
|
||||||
import 'package:pshared/provider/exception.dart';
|
|
||||||
import 'package:pshared/provider/resource.dart';
|
|
||||||
import 'package:pshared/service/account.dart';
|
|
||||||
|
|
||||||
|
|
||||||
class PfeProvider extends ChangeNotifier {
|
|
||||||
// The resource now wraps our Account? state along with its loading/error state.
|
|
||||||
Resource<String?> _resource = Resource(data: null);
|
|
||||||
Resource<String?> get resource => _resource;
|
|
||||||
|
|
||||||
String? get session => _resource.data;
|
|
||||||
bool get isLoggedIn => session != null;
|
|
||||||
bool get isLoading => _resource.isLoading;
|
|
||||||
Object? get error => _resource.error;
|
|
||||||
|
|
||||||
// Private helper to update the resource and notify listeners.
|
|
||||||
void _setResource(Resource<String?> newResource) {
|
|
||||||
_resource = newResource;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Future<String?> login({
|
|
||||||
required String email,
|
|
||||||
required String password,
|
|
||||||
}) async {
|
|
||||||
_setResource(_resource.copyWith(isLoading: true, error: null));
|
|
||||||
try {
|
|
||||||
final acc = await PfeService.login(email, password);
|
|
||||||
_setResource(Resource(data: acc, isLoading: false));
|
|
||||||
return acc;
|
|
||||||
} catch (e) {
|
|
||||||
_setResource(_resource.copyWith(isLoading: false, error: toException(e)));
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> logout() async {
|
|
||||||
_setResource(_resource.copyWith(isLoading: true, error: null));
|
|
||||||
try {
|
|
||||||
await AccountService.logout();
|
|
||||||
_setResource(Resource(data: null, isLoading: false));
|
|
||||||
} catch (e) {
|
|
||||||
_setResource(_resource.copyWith(isLoading: false, error: toException(e)));
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,10 +2,10 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
import 'package:pshared/models/permission_bound_storable.dart';
|
import 'package:pshared/models/permissions/bound/storable.dart';
|
||||||
import 'package:pshared/provider/exception.dart';
|
|
||||||
import 'package:pshared/provider/resource.dart';
|
import 'package:pshared/provider/resource.dart';
|
||||||
import 'package:pshared/service/template.dart';
|
import 'package:pshared/service/template.dart';
|
||||||
|
import 'package:pshared/utils/exception.dart';
|
||||||
|
|
||||||
|
|
||||||
List<T> mergeLists<T>({
|
List<T> mergeLists<T>({
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import 'package:share_plus/share_plus.dart';
|
|||||||
|
|
||||||
import 'package:pshared/api/requests/signup.dart';
|
import 'package:pshared/api/requests/signup.dart';
|
||||||
import 'package:pshared/api/responses/account.dart';
|
import 'package:pshared/api/responses/account.dart';
|
||||||
import 'package:pshared/api/requests/change_password.dart';
|
import 'package:pshared/api/requests/login_data.dart';
|
||||||
|
import 'package:pshared/api/requests/password/change.dart';
|
||||||
|
import 'package:pshared/api/requests/password/forgot.dart';
|
||||||
|
import 'package:pshared/api/requests/password/reset.dart';
|
||||||
import 'package:pshared/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/service/authorization/service.dart';
|
import 'package:pshared/service/authorization/service.dart';
|
||||||
@@ -17,9 +20,9 @@ class AccountService {
|
|||||||
static final _logger = Logger('service.account');
|
static final _logger = Logger('service.account');
|
||||||
static const String _objectType = Services.account;
|
static const String _objectType = Services.account;
|
||||||
|
|
||||||
static Future<Account> login(String email, String password, String locale) async {
|
static Future<Account> login(LoginData login) async {
|
||||||
_logger.fine('Logging in');
|
_logger.fine('Logging in');
|
||||||
return AuthorizationService.login(_objectType, email, password, locale);
|
return AuthorizationService.login(_objectType, login);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Account> restore() async {
|
static Future<Account> restore() async {
|
||||||
@@ -27,6 +30,7 @@ class AccountService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> signup(SignupRequest request) async {
|
static Future<void> signup(SignupRequest request) async {
|
||||||
|
// Use regular HTTP for public signup endpoint (no auth needed)
|
||||||
await getPOSTResponse(_objectType, 'signup', request.toJson());
|
await getPOSTResponse(_objectType, 'signup', request.toJson());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,9 +46,20 @@ class AccountService {
|
|||||||
|
|
||||||
static Future<Account> update(Account account) async {
|
static Future<Account> update(Account account) async {
|
||||||
_logger.fine('Patching account ${account.id}');
|
_logger.fine('Patching account ${account.id}');
|
||||||
|
// Use AuthorizationService for authenticated operations
|
||||||
return _getAccount(AuthorizationService.getPUTResponse(_objectType, '', account.toDTO().toJson()));
|
return _getAccount(AuthorizationService.getPUTResponse(_objectType, '', account.toDTO().toJson()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<void> forgotPassword(String email) async {
|
||||||
|
_logger.fine('Requesting password reset for email: $email');
|
||||||
|
await getPUTResponse(_objectType, 'password', ForgotPasswordRequest.build(login: email).toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> resetPassword(String accountRef, String token, String newPassword) async {
|
||||||
|
_logger.fine('Resetting password for account: $accountRef');
|
||||||
|
await getPOSTResponse(_objectType, 'password/reset/$accountRef/$token', ResetPasswordRequest.build(password: newPassword).toJson());
|
||||||
|
}
|
||||||
|
|
||||||
static Future<Account> changePassword(String oldPassword, String newPassword) async {
|
static Future<Account> changePassword(String oldPassword, String newPassword) async {
|
||||||
_logger.fine('Changing password');
|
_logger.fine('Changing password');
|
||||||
return _getAccount(AuthorizationService.getPATCHResponse(
|
return _getAccount(AuthorizationService.getPATCHResponse(
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
|
/// Circuit breaker pattern implementation for authentication service failures
|
||||||
|
class AuthCircuitBreaker {
|
||||||
|
static final _logger = Logger('service.auth_circuit_breaker');
|
||||||
|
|
||||||
|
static int _failureCount = 0;
|
||||||
|
static DateTime? _lastFailure;
|
||||||
|
static const int _failureThreshold = 3;
|
||||||
|
static const Duration _recoveryTime = Duration(minutes: 5);
|
||||||
|
|
||||||
|
/// Returns true if the circuit breaker is open (blocking operations)
|
||||||
|
static bool get isOpen {
|
||||||
|
if (_failureCount < _failureThreshold) return false;
|
||||||
|
if (_lastFailure == null) return false;
|
||||||
|
|
||||||
|
final isOpen = DateTime.now().difference(_lastFailure!) < _recoveryTime;
|
||||||
|
if (isOpen) {
|
||||||
|
_logger.warning('Circuit breaker is OPEN. Failure count: $_failureCount, last failure: $_lastFailure');
|
||||||
|
}
|
||||||
|
return isOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the circuit breaker is in half-open state (allowing test requests)
|
||||||
|
static bool get isHalfOpen {
|
||||||
|
if (_failureCount < _failureThreshold) return false;
|
||||||
|
if (_lastFailure == null) return false;
|
||||||
|
|
||||||
|
return DateTime.now().difference(_lastFailure!) >= _recoveryTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Executes an operation with circuit breaker protection
|
||||||
|
static Future<T> execute<T>(Future<T> Function() operation) async {
|
||||||
|
if (isOpen) {
|
||||||
|
final timeSinceFailure = _lastFailure != null
|
||||||
|
? DateTime.now().difference(_lastFailure!)
|
||||||
|
: Duration.zero;
|
||||||
|
final timeUntilRecovery = _recoveryTime - timeSinceFailure;
|
||||||
|
|
||||||
|
_logger.warning('Circuit breaker blocking operation. Recovery in: ${timeUntilRecovery.inSeconds}s');
|
||||||
|
throw Exception('Auth service temporarily unavailable. Try again in ${timeUntilRecovery.inMinutes} minutes.');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
_logger.fine('Executing operation through circuit breaker');
|
||||||
|
final result = await operation();
|
||||||
|
_reset();
|
||||||
|
return result;
|
||||||
|
} catch (e) {
|
||||||
|
_recordFailure();
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Records a failure and updates the circuit breaker state
|
||||||
|
static void _recordFailure() {
|
||||||
|
_failureCount++;
|
||||||
|
_lastFailure = DateTime.now();
|
||||||
|
_logger.warning('Auth circuit breaker recorded failure #$_failureCount at $_lastFailure');
|
||||||
|
|
||||||
|
if (_failureCount >= _failureThreshold) {
|
||||||
|
_logger.severe('Auth circuit breaker OPENED after $_failureCount failures');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resets the circuit breaker to closed state
|
||||||
|
static void _reset() {
|
||||||
|
if (_failureCount > 0) {
|
||||||
|
_logger.info('Auth circuit breaker CLOSED - resetting failure count from $_failureCount to 0');
|
||||||
|
}
|
||||||
|
_failureCount = 0;
|
||||||
|
_lastFailure = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Manual reset (for testing or administrative purposes)
|
||||||
|
static void manualReset() {
|
||||||
|
_logger.info('Auth circuit breaker manually reset');
|
||||||
|
_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get current status for debugging
|
||||||
|
static Map<String, dynamic> getStatus() {
|
||||||
|
return {
|
||||||
|
'failureCount': _failureCount,
|
||||||
|
'lastFailure': _lastFailure?.toIso8601String(),
|
||||||
|
'isOpen': isOpen,
|
||||||
|
'isHalfOpen': isHalfOpen,
|
||||||
|
'threshold': _failureThreshold,
|
||||||
|
'recoveryTime': _recoveryTime.inSeconds,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
87
frontend/pshared/lib/service/authorization/retry_helper.dart
Normal file
87
frontend/pshared/lib/service/authorization/retry_helper.dart
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/utils/exception.dart';
|
||||||
|
|
||||||
|
|
||||||
|
class RetryHelper {
|
||||||
|
static final _logger = Logger('auth.retry');
|
||||||
|
|
||||||
|
/// Executes an operation with exponential backoff retry logic
|
||||||
|
static Future<T> withExponentialBackoff<T>(
|
||||||
|
Future<T> Function() operation, {
|
||||||
|
int maxRetries = 3,
|
||||||
|
Duration initialDelay = const Duration(milliseconds: 500),
|
||||||
|
double backoffMultiplier = 2.0,
|
||||||
|
Duration maxDelay = const Duration(seconds: 30),
|
||||||
|
bool Function(Exception)? shouldRetry,
|
||||||
|
}) async {
|
||||||
|
Exception? lastException;
|
||||||
|
|
||||||
|
// Total attempts = initial attempt + maxRetries
|
||||||
|
final totalAttempts = maxRetries + 1;
|
||||||
|
|
||||||
|
for (int attempt = 1; attempt <= totalAttempts; attempt++) {
|
||||||
|
try {
|
||||||
|
_logger.fine('Attempting operation (attempt $attempt/$totalAttempts)');
|
||||||
|
return await operation();
|
||||||
|
} catch (e) {
|
||||||
|
lastException = toException(e);
|
||||||
|
|
||||||
|
// Don't retry if we've reached max attempts
|
||||||
|
if (attempt == totalAttempts) {
|
||||||
|
_logger.warning('Operation failed after $totalAttempts attempts: $lastException');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we should retry this specific error
|
||||||
|
if (shouldRetry != null && !shouldRetry(lastException)) {
|
||||||
|
_logger.fine('Operation failed with non-retryable error: $lastException');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate delay with exponential backoff
|
||||||
|
final delayMs = min(
|
||||||
|
initialDelay.inMilliseconds * pow(backoffMultiplier, attempt - 1).toInt(),
|
||||||
|
maxDelay.inMilliseconds,
|
||||||
|
);
|
||||||
|
final delay = Duration(milliseconds: delayMs);
|
||||||
|
|
||||||
|
_logger.fine('Operation failed (attempt $attempt), retrying in ${delay.inMilliseconds}ms: $lastException');
|
||||||
|
await Future.delayed(delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should never be reached due to rethrow above, but just in case
|
||||||
|
throw lastException ?? Exception('Retry logic error');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines if an error is retryable (network/temporary errors)
|
||||||
|
static bool isRetryableError(Exception error) {
|
||||||
|
final errorString = error.toString().toLowerCase();
|
||||||
|
|
||||||
|
// Network connectivity issues
|
||||||
|
if (errorString.contains('socket') ||
|
||||||
|
errorString.contains('connection') ||
|
||||||
|
errorString.contains('timeout') ||
|
||||||
|
errorString.contains('network')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server temporary errors (5xx)
|
||||||
|
if (errorString.contains('500') ||
|
||||||
|
errorString.contains('502') ||
|
||||||
|
errorString.contains('503') ||
|
||||||
|
errorString.contains('504')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rate limiting
|
||||||
|
if (errorString.contains('429')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +1,38 @@
|
|||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
import 'package:pshared/api/errors/upload_failed.dart';
|
import 'package:pshared/api/errors/authorization_failed.dart';
|
||||||
import 'package:pshared/api/requests/login.dart';
|
import 'package:pshared/api/requests/login.dart';
|
||||||
|
import 'package:pshared/api/requests/login_data.dart';
|
||||||
import 'package:pshared/api/responses/account.dart';
|
import 'package:pshared/api/responses/account.dart';
|
||||||
import 'package:pshared/api/responses/login.dart';
|
import 'package:pshared/api/responses/login.dart';
|
||||||
import 'package:pshared/config/constants.dart';
|
import 'package:pshared/config/web.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/service/authorization/circuit_breaker.dart';
|
||||||
|
import 'package:pshared/service/authorization/retry_helper.dart';
|
||||||
import 'package:pshared/service/authorization/storage.dart';
|
import 'package:pshared/service/authorization/storage.dart';
|
||||||
import 'package:pshared/service/authorization/token.dart';
|
import 'package:pshared/service/authorization/token.dart';
|
||||||
import 'package:pshared/service/device_id.dart';
|
import 'package:pshared/service/device_id.dart';
|
||||||
|
import 'package:pshared/utils/exception.dart';
|
||||||
import 'package:pshared/utils/http/requests.dart' as httpr;
|
import 'package:pshared/utils/http/requests.dart' as httpr;
|
||||||
|
|
||||||
|
|
||||||
|
/// AuthorizationService provides centralized authorization management
|
||||||
|
/// with token refresh, retry logic, and circuit breaker patterns
|
||||||
class AuthorizationService {
|
class AuthorizationService {
|
||||||
static final _logger = Logger('service.authorization');
|
static final _logger = Logger('service.authorization.auth_service');
|
||||||
|
|
||||||
|
static Future<Account> login(String service, LoginData login) async {
|
||||||
|
_logger.fine('Logging in ${login.login} with ${login.locale} locale');
|
||||||
|
final deviceId = await DeviceIdManager.getDeviceId();
|
||||||
|
final response = await httpr.getPOSTResponse(
|
||||||
|
service,
|
||||||
|
'/login',
|
||||||
|
LoginRequest(login: login, deviceId: deviceId, clientId: Constants.clientId).toJson(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (await _completeLogin(response)).account.toDomain();
|
||||||
|
}
|
||||||
|
|
||||||
static Future<void> _updateAccessToken(AccountResponse response) async {
|
static Future<void> _updateAccessToken(AccountResponse response) async {
|
||||||
await AuthorizationStorage.updateToken(response.accessToken);
|
await AuthorizationStorage.updateToken(response.accessToken);
|
||||||
@@ -31,59 +49,86 @@ class AuthorizationService {
|
|||||||
return lr;
|
return lr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Account> login(String service, String email, String password, String locale) async {
|
|
||||||
_logger.fine('Logging in $email with $locale locale');
|
|
||||||
final deviceId = await DeviceIdManager.getDeviceId();
|
|
||||||
final response = await httpr.getPOSTResponse(
|
|
||||||
service,
|
|
||||||
'/login',
|
|
||||||
LoginRequest(
|
|
||||||
login: email.toLowerCase(),
|
|
||||||
password: password,
|
|
||||||
locale: locale,
|
|
||||||
deviceId: deviceId,
|
|
||||||
clientId: Constants.clientId,
|
|
||||||
).toJson());
|
|
||||||
|
|
||||||
return (await _completeLogin(response)).account.toDomain();
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<Account> restore() async {
|
static Future<Account> restore() async {
|
||||||
return (await TokenService.rotateRefreshToken()).account.toDomain();
|
return (await TokenService.refreshAccessToken()).account.toDomain();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> logout() async {
|
static Future<void> logout() async {
|
||||||
return AuthorizationStorage.removeTokens();
|
return AuthorizationStorage.removeTokens();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Map<String, dynamic>> _authenticatedRequest(
|
// Original AuthorizationService methods - keeping the interface unchanged
|
||||||
String service,
|
|
||||||
String url,
|
|
||||||
Future<Map<String, dynamic>> Function(String, String, Map<String, dynamic>, {String? authToken}) requestType,
|
|
||||||
{Map<String, dynamic>? body}) async {
|
|
||||||
final accessToken = await TokenService.getAccessToken();
|
|
||||||
return requestType(service, url, body ?? {}, authToken: accessToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<Map<String, dynamic>> getPOSTResponse(String service, String url, Map<String, dynamic> body) async => _authenticatedRequest(service, url, httpr.getPOSTResponse, body: body);
|
|
||||||
|
|
||||||
static Future<Map<String, dynamic>> getGETResponse(String service, String url) async {
|
static Future<Map<String, dynamic>> getGETResponse(String service, String url) async {
|
||||||
final accessToken = await TokenService.getAccessToken();
|
final token = await TokenService.getAccessTokenSafe();
|
||||||
return httpr.getGETResponse(service, url, authToken: accessToken);
|
return httpr.getGETResponse(service, url, authToken: token);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Map<String, dynamic>> getPUTResponse(String service, String url, Map<String, dynamic> body) async => _authenticatedRequest(service, url, httpr.getPUTResponse, body: body);
|
static Future<Map<String, dynamic>> getPOSTResponse(String service, String url, Map<String, dynamic> body) async {
|
||||||
|
final token = await TokenService.getAccessTokenSafe();
|
||||||
|
return httpr.getPOSTResponse(service, url, body, authToken: token);
|
||||||
|
}
|
||||||
|
|
||||||
static Future<Map<String, dynamic>> getPATCHResponse(String service, String url, Map<String, dynamic> body) async => _authenticatedRequest(service, url, httpr.getPATCHResponse, body: body);
|
static Future<Map<String, dynamic>> getPUTResponse(String service, String url, Map<String, dynamic> body) async {
|
||||||
|
final token = await TokenService.getAccessTokenSafe();
|
||||||
|
return httpr.getPUTResponse(service, url, body, authToken: token);
|
||||||
|
}
|
||||||
|
|
||||||
static Future<Map<String, dynamic>> getDELETEResponse(String service, String url, Map<String, dynamic> body) async => _authenticatedRequest(service, url, httpr.getDELETEResponse, body: body);
|
static Future<Map<String, dynamic>> getPATCHResponse(String service, String url, Map<String, dynamic> body) async {
|
||||||
|
final token = await TokenService.getAccessTokenSafe();
|
||||||
|
return httpr.getPATCHResponse(service, url, body, authToken: token);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Map<String, dynamic>> getDELETEResponse(String service, String url, Map<String, dynamic> body) async {
|
||||||
|
final token = await TokenService.getAccessTokenSafe();
|
||||||
|
return httpr.getDELETEResponse(service, url, body, authToken: token);
|
||||||
|
}
|
||||||
|
|
||||||
static Future<String> getFileUploadResponseAuth(String service, String url, String fileName, String fileType, String mediaType, List<int> bytes) async {
|
static Future<String> getFileUploadResponseAuth(String service, String url, String fileName, String fileType, String mediaType, List<int> bytes) async {
|
||||||
final accessToken = await TokenService.getAccessToken();
|
final token = await TokenService.getAccessTokenSafe();
|
||||||
final res = await httpr.getFileUploadResponse(service, url, fileName, fileType, mediaType, bytes, authToken: accessToken);
|
final res = await httpr.getFileUploadResponse(service, url, fileName, fileType, mediaType, bytes, authToken: token);
|
||||||
if (res == null) {
|
if (res == null) {
|
||||||
throw ErrorUploadFailed();
|
throw Exception('Upload failed');
|
||||||
}
|
}
|
||||||
return res.url;
|
return res.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<bool> isAuthorizationStored() async => AuthorizationStorage.isAuthorizationStored();
|
||||||
|
|
||||||
|
/// Execute an operation with automatic token management and retry logic
|
||||||
|
static Future<T> executeWithAuth<T>(
|
||||||
|
Future<T> Function() operation,
|
||||||
|
String description, {
|
||||||
|
int? maxRetries,
|
||||||
|
}) async => AuthCircuitBreaker.execute(() async => RetryHelper.withExponentialBackoff(
|
||||||
|
operation,
|
||||||
|
maxRetries: maxRetries ?? 3,
|
||||||
|
initialDelay: Duration(milliseconds: 100),
|
||||||
|
maxDelay: Duration(seconds: 5),
|
||||||
|
shouldRetry: (error) => RetryHelper.isRetryableError(error),
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
/// Handle 401 unauthorized errors with automatic token recovery
|
||||||
|
static Future<T> handleUnauthorized<T>(
|
||||||
|
Future<T> Function() operation,
|
||||||
|
String description,
|
||||||
|
) async {
|
||||||
|
_logger.warning('Handling unauthorized error with token recovery: $description');
|
||||||
|
|
||||||
|
return executeWithAuth(
|
||||||
|
() async {
|
||||||
|
try {
|
||||||
|
// Attempt token recovery first
|
||||||
|
await TokenService.handleUnauthorized();
|
||||||
|
|
||||||
|
// Retry the original operation
|
||||||
|
return await operation();
|
||||||
|
} catch (e) {
|
||||||
|
_logger.severe('Token recovery failed', e);
|
||||||
|
throw AuthenticationFailedException('Token recovery failed', toException(e));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'unauthorized recovery: $description',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,34 @@ class AuthorizationStorage {
|
|||||||
return TokenData.fromJson(jsonDecode(tokenJson));
|
return TokenData.fromJson(jsonDecode(tokenJson));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Future<bool> _checkTokenUsable(String keyName) async {
|
||||||
|
final hasKey = await SecureStorageService.containsKey(keyName);
|
||||||
|
if (!hasKey) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final tokenData = await _getTokenData(keyName);
|
||||||
|
return tokenData.expiration.isAfter(DateTime.now());
|
||||||
|
} catch (e, st) {
|
||||||
|
_logger.warning('Error reading token from $keyName: $e', e, st);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Future<bool> isAuthorizationStored() async {
|
||||||
|
_logger.fine('Checking if authorization is stored');
|
||||||
|
|
||||||
|
final accessUsable = await _checkTokenUsable(Constants.accessTokenStorageKey);
|
||||||
|
if (accessUsable) return true;
|
||||||
|
|
||||||
|
final refreshUsable = await _checkTokenUsable(Constants.refreshTokenStorageKey);
|
||||||
|
if (refreshUsable) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static Future<TokenData> getAccessToken() async {
|
static Future<TokenData> getAccessToken() async {
|
||||||
_logger.fine('Getting access token');
|
_logger.fine('Getting access token');
|
||||||
return _getTokenData(Constants.accessTokenStorageKey);
|
return _getTokenData(Constants.accessTokenStorageKey);
|
||||||
|
|||||||
@@ -1,20 +1,25 @@
|
|||||||
|
|
||||||
|
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/api/errors/authorization_failed.dart';
|
||||||
import 'package:pshared/api/errors/unauthorized.dart';
|
import 'package:pshared/api/errors/unauthorized.dart';
|
||||||
import 'package:pshared/api/requests/tokens/access_refresh.dart';
|
import 'package:pshared/api/requests/tokens/access_refresh.dart';
|
||||||
import 'package:pshared/api/requests/tokens/refresh_rotate.dart';
|
import 'package:pshared/api/requests/tokens/refresh_rotate.dart';
|
||||||
import 'package:pshared/api/responses/account.dart';
|
import 'package:pshared/api/responses/account.dart';
|
||||||
|
import 'package:pshared/api/responses/error/server.dart';
|
||||||
import 'package:pshared/api/responses/login.dart';
|
import 'package:pshared/api/responses/login.dart';
|
||||||
import 'package:pshared/api/responses/token.dart';
|
import 'package:pshared/api/responses/token.dart';
|
||||||
import 'package:pshared/config/constants.dart';
|
import 'package:pshared/config/constants.dart';
|
||||||
|
import 'package:pshared/service/authorization/circuit_breaker.dart';
|
||||||
|
import 'package:pshared/service/authorization/retry_helper.dart';
|
||||||
import 'package:pshared/service/authorization/storage.dart';
|
import 'package:pshared/service/authorization/storage.dart';
|
||||||
|
import 'package:pshared/service/authorization/token_mutex.dart';
|
||||||
import 'package:pshared/service/device_id.dart';
|
import 'package:pshared/service/device_id.dart';
|
||||||
import 'package:pshared/service/services.dart';
|
import 'package:pshared/service/services.dart';
|
||||||
|
import 'package:pshared/utils/exception.dart';
|
||||||
import 'package:pshared/utils/http/requests.dart';
|
import 'package:pshared/utils/http/requests.dart';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TokenService {
|
class TokenService {
|
||||||
static final _logger = Logger('service.authorization.token');
|
static final _logger = Logger('service.authorization.token');
|
||||||
static const String _objectType = Services.account;
|
static const String _objectType = Services.account;
|
||||||
@@ -26,7 +31,11 @@ class TokenService {
|
|||||||
static Future<String> getAccessToken() async {
|
static Future<String> getAccessToken() async {
|
||||||
TokenData token = await AuthorizationStorage.getAccessToken();
|
TokenData token = await AuthorizationStorage.getAccessToken();
|
||||||
if (_isTokenExpiringSoon(token, const Duration(hours: 4))) {
|
if (_isTokenExpiringSoon(token, const Duration(hours: 4))) {
|
||||||
token = (await _refreshAccessToken()).accessToken;
|
// Use mutex to prevent concurrent refresh operations
|
||||||
|
final refreshedToken = await TokenRefreshMutex().executeRefresh(() async {
|
||||||
|
return (await refreshAccessToken()).accessToken.token;
|
||||||
|
});
|
||||||
|
return refreshedToken;
|
||||||
}
|
}
|
||||||
return token.token;
|
return token.token;
|
||||||
}
|
}
|
||||||
@@ -36,13 +45,13 @@ class TokenService {
|
|||||||
await AuthorizationStorage.updateRefreshToken(response.refreshToken);
|
await AuthorizationStorage.updateRefreshToken(response.refreshToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<AccountResponse> _refreshAccessToken() async {
|
static Future<AccountResponse> refreshAccessToken() async {
|
||||||
_logger.fine('Refreshing access token...');
|
_logger.fine('Refreshing access token...');
|
||||||
final deviceId = await DeviceIdManager.getDeviceId();
|
final deviceId = await DeviceIdManager.getDeviceId();
|
||||||
final refresh = await AuthorizationStorage.getRefreshToken();
|
final refresh = await AuthorizationStorage.getRefreshToken();
|
||||||
|
|
||||||
if (_isTokenExpiringSoon(refresh, const Duration(days: 7))) {
|
if (_isTokenExpiringSoon(refresh, const Duration(days: 7))) {
|
||||||
return await rotateRefreshToken();
|
return await _rotateRefreshToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
final response = await getPOSTResponse(
|
final response = await getPOSTResponse(
|
||||||
@@ -60,7 +69,7 @@ class TokenService {
|
|||||||
return accountResp;
|
return accountResp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<LoginResponse> rotateRefreshToken() async {
|
static Future<LoginResponse> _rotateRefreshToken() async {
|
||||||
_logger.fine('Rotating refresh token...');
|
_logger.fine('Rotating refresh token...');
|
||||||
final refresh = await AuthorizationStorage.getRefreshToken();
|
final refresh = await AuthorizationStorage.getRefreshToken();
|
||||||
|
|
||||||
@@ -82,4 +91,89 @@ class TokenService {
|
|||||||
return loginResponse;
|
return loginResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enhanced method to handle unexpected 401 errors with fallback logic
|
||||||
|
static Future<void> handleUnauthorized() async {
|
||||||
|
_logger.warning('Handling unexpected 401 unauthorized error');
|
||||||
|
|
||||||
|
return AuthCircuitBreaker.execute(() async {
|
||||||
|
return RetryHelper.withExponentialBackoff(
|
||||||
|
() async {
|
||||||
|
try {
|
||||||
|
// Try refresh first (faster)
|
||||||
|
final currentRefresh = await AuthorizationStorage.getRefreshToken();
|
||||||
|
if (!_isTokenExpiringSoon(currentRefresh, const Duration(days: 1))) {
|
||||||
|
_logger.fine('Attempting access token refresh for 401 recovery');
|
||||||
|
await TokenRefreshMutex().executeRefresh(() async {
|
||||||
|
await refreshAccessToken();
|
||||||
|
return 'refreshed';
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to rotation if refresh token expiring soon
|
||||||
|
_logger.fine('Attempting refresh token rotation for 401 recovery');
|
||||||
|
await TokenRefreshMutex().executeRotation(() async {
|
||||||
|
await _rotateRefreshToken();
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
_logger.severe('Token recovery failed: $e');
|
||||||
|
throw AuthenticationFailedException('Token recovery failed', toException(e));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
maxRetries: 2,
|
||||||
|
shouldRetry: (error) {
|
||||||
|
// Only retry on network errors, not auth errors
|
||||||
|
return RetryHelper.isRetryableError(error) && !_isAuthError(error);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enhanced getAccessToken with better error handling
|
||||||
|
static Future<String> getAccessTokenSafe() async {
|
||||||
|
return AuthCircuitBreaker.execute(() async {
|
||||||
|
return RetryHelper.withExponentialBackoff(
|
||||||
|
() async {
|
||||||
|
TokenData token = await AuthorizationStorage.getAccessToken();
|
||||||
|
if (_isTokenExpiringSoon(token, const Duration(hours: 4))) {
|
||||||
|
// Use mutex to prevent concurrent refresh operations
|
||||||
|
final refreshedToken = await TokenRefreshMutex().executeRefresh(() async {
|
||||||
|
return (await refreshAccessToken()).accessToken.token;
|
||||||
|
});
|
||||||
|
return refreshedToken;
|
||||||
|
}
|
||||||
|
return token.token;
|
||||||
|
},
|
||||||
|
maxRetries: 2,
|
||||||
|
shouldRetry: (error) => RetryHelper.isRetryableError(error),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if error is authentication-related (non-retryable)
|
||||||
|
static bool _isAuthError(Exception error) {
|
||||||
|
if (error is ErrorUnauthorized || error is AuthenticationFailedException) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error is ErrorResponse && error.code == 401) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final errorString = error.toString().toLowerCase();
|
||||||
|
return errorString.contains('unauthorized') ||
|
||||||
|
errorString.contains('401') ||
|
||||||
|
errorString.contains('authentication') ||
|
||||||
|
errorString.contains('token');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get circuit breaker status for debugging
|
||||||
|
static Map<String, dynamic> getAuthStatus() {
|
||||||
|
return {
|
||||||
|
'circuitBreaker': AuthCircuitBreaker.getStatus(),
|
||||||
|
'tokenMutex': TokenRefreshMutex().getStatus(),
|
||||||
|
'timestamp': DateTime.now().toIso8601String(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
103
frontend/pshared/lib/service/authorization/token_mutex.dart
Normal file
103
frontend/pshared/lib/service/authorization/token_mutex.dart
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
|
/// Mutex to prevent concurrent token refresh operations
|
||||||
|
/// This ensures only one refresh operation happens at a time,
|
||||||
|
/// preventing race conditions during app startup when multiple
|
||||||
|
/// providers try to refresh tokens simultaneously.
|
||||||
|
class TokenRefreshMutex {
|
||||||
|
static final _instance = TokenRefreshMutex._();
|
||||||
|
factory TokenRefreshMutex() => _instance;
|
||||||
|
TokenRefreshMutex._();
|
||||||
|
|
||||||
|
static final _logger = Logger('service.authorization.token_mutex');
|
||||||
|
|
||||||
|
Completer<String>? _currentRefresh;
|
||||||
|
Completer<void>? _currentRotation;
|
||||||
|
|
||||||
|
/// Execute a token refresh operation with mutex protection
|
||||||
|
/// If another refresh is in progress, wait for it to complete
|
||||||
|
Future<String> executeRefresh(Future<String> Function() refreshOperation) async {
|
||||||
|
if (_currentRefresh != null) {
|
||||||
|
_logger.fine('Token refresh already in progress, waiting for completion');
|
||||||
|
return await _currentRefresh!.future;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.fine('Starting new token refresh operation');
|
||||||
|
_currentRefresh = Completer<String>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final result = await refreshOperation();
|
||||||
|
if (_currentRefresh != null) {
|
||||||
|
_currentRefresh!.complete(result);
|
||||||
|
_logger.fine('Token refresh completed successfully');
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (e, st) {
|
||||||
|
_logger.warning('Token refresh failed', e, st);
|
||||||
|
if (_currentRefresh != null) {
|
||||||
|
_currentRefresh!.completeError(e, st);
|
||||||
|
}
|
||||||
|
rethrow;
|
||||||
|
} finally {
|
||||||
|
_currentRefresh = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute a token rotation operation with mutex protection
|
||||||
|
/// If another rotation is in progress, wait for it to complete
|
||||||
|
Future<void> executeRotation(Future<void> Function() rotationOperation) async {
|
||||||
|
if (_currentRotation != null) {
|
||||||
|
_logger.fine('Token rotation already in progress, waiting for completion');
|
||||||
|
return await _currentRotation!.future;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.fine('Starting new token rotation operation');
|
||||||
|
_currentRotation = Completer<void>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await rotationOperation();
|
||||||
|
if (_currentRotation != null) {
|
||||||
|
_currentRotation!.complete();
|
||||||
|
_logger.fine('Token rotation completed successfully');
|
||||||
|
}
|
||||||
|
} catch (e, st) {
|
||||||
|
_logger.warning('Token rotation failed', e, st);
|
||||||
|
if (_currentRotation != null) {
|
||||||
|
_currentRotation!.completeError(e, st);
|
||||||
|
}
|
||||||
|
rethrow;
|
||||||
|
} finally {
|
||||||
|
_currentRotation = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a refresh operation is currently in progress
|
||||||
|
bool get isRefreshInProgress => _currentRefresh != null;
|
||||||
|
|
||||||
|
/// Check if a rotation operation is currently in progress
|
||||||
|
bool get isRotationInProgress => _currentRotation != null;
|
||||||
|
|
||||||
|
/// Get current status for debugging
|
||||||
|
Map<String, dynamic> getStatus() {
|
||||||
|
return {
|
||||||
|
'refreshInProgress': isRefreshInProgress,
|
||||||
|
'rotationInProgress': isRotationInProgress,
|
||||||
|
'timestamp': DateTime.now().toIso8601String(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Force reset the mutex (for testing or emergency situations)
|
||||||
|
void forceReset() {
|
||||||
|
_logger.warning('Force resetting token refresh mutex');
|
||||||
|
if (_currentRefresh != null && !_currentRefresh!.isCompleted) {
|
||||||
|
_currentRefresh!.completeError(Exception('Mutex force reset'));
|
||||||
|
}
|
||||||
|
if (_currentRotation != null && !_currentRotation!.isCompleted) {
|
||||||
|
_currentRotation!.completeError(Exception('Mutex force reset'));
|
||||||
|
}
|
||||||
|
_currentRefresh = null;
|
||||||
|
_currentRotation = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
|
||||||
class SecureStorageService {
|
class SecureStorageService {
|
||||||
static Future<String?> get(String key) async {
|
static Future<String?> get(String key) async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
@@ -18,6 +19,11 @@ class SecureStorageService {
|
|||||||
return _setImp(prefs, key, value);
|
return _setImp(prefs, key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<bool> containsKey(String key) async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
return prefs.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
static Future<void> delete(String key) async {
|
static Future<void> delete(String key) async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
await prefs.remove(key);
|
await prefs.remove(key);
|
||||||
|
|||||||
@@ -6,27 +6,13 @@ class Services {
|
|||||||
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 project = 'projects';
|
|
||||||
static const String pgroup = 'priority_groups';
|
|
||||||
static const String priorities = 'priorities';
|
|
||||||
static const String reactions = 'reactions';
|
|
||||||
static const String storage = 'storage';
|
static const String storage = 'storage';
|
||||||
static const String taskStatus = 'statuses';
|
|
||||||
static const String tasks = 'tasks';
|
|
||||||
|
|
||||||
static const String amplitude = 'amplitude';
|
static const String amplitude = 'amplitude';
|
||||||
static const String automations = 'automation';
|
|
||||||
static const String changes = 'changes';
|
|
||||||
static const String clients = 'clients';
|
static const String clients = 'clients';
|
||||||
static const String invoices = 'invoices';
|
|
||||||
static const String logo = 'logo';
|
static const String logo = 'logo';
|
||||||
static const String notifications = 'notifications';
|
static const String notifications = 'notifications';
|
||||||
static const String policies = 'policies';
|
static const String policies = 'policies';
|
||||||
static const String properties = 'properties';
|
|
||||||
static const String refreshTokens = 'refresh_tokens';
|
static const String refreshTokens = 'refresh_tokens';
|
||||||
static const String roles = 'roles';
|
static const String roles = 'roles';
|
||||||
static const String steps = 'steps';
|
|
||||||
static const String teams = 'teams';
|
|
||||||
static const String workflows = 'workflows';
|
|
||||||
static const String workspaces = 'workspaces';
|
|
||||||
}
|
}
|
||||||
|
|||||||
1
frontend/pweb/lib/.gitignore
vendored
Normal file
1
frontend/pweb/lib/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
generated
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,779 +0,0 @@
|
|||||||
// ignore: unused_import
|
|
||||||
import 'package:intl/intl.dart' as intl;
|
|
||||||
import 'app_localizations.dart';
|
|
||||||
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
|
|
||||||
/// The translations for English (`en`).
|
|
||||||
class AppLocalizationsEn extends AppLocalizations {
|
|
||||||
AppLocalizationsEn([String locale = 'en']) : super(locale);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get login => 'Login';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get logout => 'Logout';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get profile => 'Profile';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get signup => 'Sign up';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get username => 'Email';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get usernameHint => 'email@example.com';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get usernameErrorInvalid => 'Provide a valid email address';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String usernameUnknownTLD(Object domain) {
|
|
||||||
return 'Domain .$domain is not known, please, check it';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get password => 'Password';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get confirmPassword => 'Confirm password';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get passwordValidationRuleDigit => 'has digit';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get passwordValidationRuleUpperCase => 'has uppercase letter';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get passwordValidationRuleLowerCase => 'has lowercase letter';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get passwordValidationRuleSpecialCharacter =>
|
|
||||||
'has special character letter';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String passwordValidationRuleMinCharacters(Object charNum) {
|
|
||||||
return 'is $charNum characters long at least';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get passwordsDoNotMatch => 'Passwords do not match';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String passwordValidationError(Object matchesCriteria) {
|
|
||||||
return 'Check that your password $matchesCriteria';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String notificationError(Object error) {
|
|
||||||
return 'Error occurred: $error';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String loginUserNotFound(Object account) {
|
|
||||||
return 'Account $account has not been registered in the system';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get loginPasswordIncorrect =>
|
|
||||||
'Authorization failed, please check your password';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String internalErrorOccurred(Object error) {
|
|
||||||
return 'An internal server error occurred: $error, we already know about it and working hard to fix it';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get noErrorInformation =>
|
|
||||||
'Some error occurred, but we have not error information. We are already investigating the issue';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get yourName => 'Your name';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get nameHint => 'John Doe';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorPageNotFoundTitle => 'Page Not Found';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorPageNotFoundMessage => 'Oops! We couldn\'t find that page.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorPageNotFoundHint =>
|
|
||||||
'The page you\'re looking for doesn\'t exist or has been moved. Please check the URL or return to the home page.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorUnknown => 'Unknown error occurred';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get unknown => 'unknown';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get goToLogin => 'Go to Login';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get goBack => 'Go Back';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get goToMainPage => 'Go to Main Page';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get goToSignUp => 'Go to Sign Up';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String signupError(Object error) {
|
|
||||||
return 'Failed to signup: $error';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String signupSuccess(Object email) {
|
|
||||||
return 'Email confirmation message has been sent to $email. Please, open it and click link to activate your account.';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String connectivityError(Object serverAddress) {
|
|
||||||
return 'Cannot reach the server at $serverAddress. Check your network and try again.';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorAccountExists => 'Account already exists';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorAccountNotVerified =>
|
|
||||||
'Your account hasn\'t been verified yet. Please check your email to complete the verification';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorLoginUnauthorized =>
|
|
||||||
'Login or password is incorrect. Please try again';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorInternalError =>
|
|
||||||
'An internal error occurred. We\'re aware of the issue and working to resolve it. Please try again later';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorVerificationTokenNotFound =>
|
|
||||||
'Account for verification not found. Sign up again';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get created => 'Created';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get edited => 'Edited';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorDataConflict =>
|
|
||||||
'We can’t process your data because it has conflicting or contradictory information.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorAccessDenied =>
|
|
||||||
'You do not have permission to access this resource. If you need access, please contact an administrator.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorBrokenPayload =>
|
|
||||||
'The data you sent is invalid or incomplete. Please check your submission and try again.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorInvalidArgument =>
|
|
||||||
'One or more arguments are invalid. Verify your input and try again.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorBrokenReference =>
|
|
||||||
'The resource you\'re trying to access could not be referenced. It may have been moved or deleted.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorInvalidQueryParameter =>
|
|
||||||
'One or more query parameters are missing or incorrect. Check them and try again.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorNotImplemented =>
|
|
||||||
'This feature is not yet available. Please try again later or contact support.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorLicenseRequired =>
|
|
||||||
'A valid license is required to perform this action. Please contact your administrator.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorNotFound =>
|
|
||||||
'We couldn\'t find the resource you requested. It may have been removed or is temporarily unavailable.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorNameMissing => 'Please provide a name before continuing.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorEmailMissing =>
|
|
||||||
'Please provide an email address before continuing.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorPasswordMissing =>
|
|
||||||
'Please provide a password before continuing.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorEmailNotRegistered =>
|
|
||||||
'We could not find an account associated with that email address.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorDuplicateEmail =>
|
|
||||||
'This email address is already in use. Try another one or reset your password.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get showDetailsAction => 'Show Details';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorLogin => 'Error logging in';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorCreatingInvitation => 'Failed to create invitaiton';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get footerCompanyName => 'Sibilla Solutions LTD';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get footerAddress =>
|
|
||||||
'27, Pindarou Street, Alpha Business Centre, Block B 7th Floor, 1060 Nicosia, Cyprus';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get footerSupport => 'Support';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get footerEmail => 'Email TBD';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get footerPhoneLabel => 'Phone';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get footerPhone => '+357 22 000 253';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get footerTermsOfService => 'Terms of Service';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get footerPrivacyPolicy => 'Privacy Policy';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get footerCookiePolicy => 'Cookie Policy';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get navigationLogout => 'Logout';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get dashboard => 'Dashboard';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get navigationUsersSettings => 'Users';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get navigationRolesSettings => 'Roles';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get navigationPermissionsSettings => 'Permissions';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get usersManagement => 'User Management';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get navigationOrganizationSettings => 'Organization settings';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get navigationAccountSettings => 'Profile settings';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get twoFactorPrompt => 'Enter the 6-digit code we sent to your device';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get twoFactorResend => 'Didn’t receive a code? Resend';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get twoFactorTitle => 'Two-Factor Authentication';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get twoFactorError => 'Invalid code. Please try again.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get payoutNavDashboard => 'Dashboard';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get payoutNavSendPayout => 'Send payout';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get payoutNavRecipients => 'Recipients';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get payoutNavReports => 'Reports';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get payoutNavSettings => 'Settings';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get payoutNavLogout => 'Logout';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get payoutNavMethods => 'Payouts';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get expand => 'Expand';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get collapse => 'Collapse';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get pageTitleRecipients => 'Recipient address book';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get actionAddNew => 'Add new';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get colDataOwner => 'Data owner';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get colAvatar => 'Avatar';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get colName => 'Name';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get colEmail => 'Email';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get colStatus => 'Status';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get statusReady => 'Ready';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get statusRegistered => 'Registered';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get statusNotRegistered => 'Not registered';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get typeInternal => 'Managed by me';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get typeExternal => 'Self‑managed';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get searchHint => 'Search recipients';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get colActions => 'Actions';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get menuEdit => 'Edit';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get menuSendPayout => 'Send payout';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get tooltipRowActions => 'More actions';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get accountSettings => 'Account Settings';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get accountNameUpdateError => 'Failed to update account name';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get settingsSuccessfullyUpdated => 'Settings successfully updated';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get language => 'Language';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get failedToUpdateLanguage => 'Failed to update language';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get settingsImageUpdateError => 'Couldn\'t update the image';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get settingsImageTitle => 'Image';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get settingsImageHint => 'Tap to change the image';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get accountName => 'Name';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get accountNameHint => 'Specify your name';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get avatar => 'Profile photo';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get avatarHint => 'Tap to update';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get avatarUpdateError => 'Failed to update profile photo';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get settings => 'Settings';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get notSet => 'not set';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get search => 'Search...';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get ok => 'Ok';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cancel => 'Cancel';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get confirm => 'Confirm';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get back => 'Back';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get operationfryTitle => 'Operation history';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get filters => 'Filters';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get period => 'Period';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get selectPeriod => 'Select period';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get apply => 'Apply';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String status(String status) {
|
|
||||||
return '$status';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get operationStatusSuccessful => 'Successful';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get operationStatusPending => 'Pending';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get operationStatusUnsuccessful => 'Unsuccessful';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get statusColumn => 'Status';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get fileNameColumn => 'File name';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get amountColumn => 'Amount';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get toAmountColumn => 'To amount';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get payIdColumn => 'Pay ID';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cardNumberColumn => 'Card number';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get nameColumn => 'Name';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get dateColumn => 'Date';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get commentColumn => 'Comment';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get paymentConfigTitle => 'Where to receive money';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get paymentConfigSubtitle =>
|
|
||||||
'Add multiple methods and choose your primary one.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get addPaymentMethod => 'Add payment method';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get makeMain => 'Make primary';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get advanced => 'Advanced';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get fallbackExplanation =>
|
|
||||||
'If the primary method is unavailable, we will try the next enabled one in the list.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get delete => 'Delete';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get deletePaymentConfirmation =>
|
|
||||||
'Are you sure you want to delete this payment method?';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get edit => 'Edit';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get moreActions => 'More actions';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get noPayouts => 'No Payouts';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterBankName => 'Enter bank name';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get paymentType => 'Payment Method Type';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get selectPaymentType => 'Please select a payment method type';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get paymentTypeCard => 'Credit Card';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get paymentTypeBankAccount => 'Russian Bank Account';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get paymentTypeIban => 'IBAN';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get paymentTypeWallet => 'Wallet';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cardNumber => 'Card Number';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterCardNumber => 'Enter the card number';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cardholderName => 'Cardholder Name';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get iban => 'IBAN';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterIban => 'Enter IBAN';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get bic => 'BIC';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get bankName => 'Bank Name';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get accountHolder => 'Account Holder';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterAccountHolder => 'Enter account holder';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterBic => 'Enter BIC';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get walletId => 'Wallet ID';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterWalletId => 'Enter wallet ID';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get recipients => 'Recipients';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get recipientName => 'Recipient Name';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterRecipientName => 'Enter recipient name';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get inn => 'INN';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterInn => 'Enter INN';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get kpp => 'KPP';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterKpp => 'Enter KPP';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get accountNumber => 'Account Number';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterAccountNumber => 'Enter account number';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get correspondentAccount => 'Correspondent Account';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterCorrespondentAccount => 'Enter correspondent account';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get bik => 'BIK';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterBik => 'Enter BIK';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get add => 'Add';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get expiryDate => 'Expiry (MM/YY)';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get firstName => 'First Name';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterFirstName => 'Enter First Name';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get lastName => 'Last Name';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterLastName => 'Enter Last Name';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get sendSingle => 'Send single transaction';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get sendMultiple => 'Send multiple transactions';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get addFunds => 'Add Funds';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get close => 'Close';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get multiplePayout => 'Multiple Payout';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get howItWorks => 'How it works?';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get exampleTitle => 'File Format & Sample';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get downloadSampleCSV => 'Download sample.csv';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get tokenColumn => 'Token (required)';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get currency => 'Currency';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get amount => 'Amount';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get comment => 'Comment';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get uploadCSV => 'Upload your CSV';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get upload => 'Upload';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get hintUpload => 'Supported format: .CSV · Max size 1 MB';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get uploadHistory => 'Upload History';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get payout => 'Payout';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get sendTo => 'Send Payout To';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get send => 'Send Payout';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get recipientPaysFee => 'Recipient pays the fee';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String sentAmount(String amount) {
|
|
||||||
return 'Sent amount: \$$amount';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String fee(String fee) {
|
|
||||||
return 'Fee: \$$fee';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String recipientWillReceive(String amount) {
|
|
||||||
return 'Recipient will receive: \$$amount';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String total(String total) {
|
|
||||||
return 'Total: \$$total';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get hideDetails => 'Hide Details';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get showDetails => 'Show Details';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get whereGetMoney => 'Source of funds for debit';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get details => 'Details';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get addRecipient => 'Add Recipient';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get editRecipient => 'Edit Recipient';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get saveRecipient => 'Save Recipient';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get choosePaymentMethod => 'Payment Methods (choose at least 1)';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get recipientFormRule =>
|
|
||||||
'Recipient must have at least one payment method';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get allStatus => 'All';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get readyStatus => 'Ready';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get registeredStatus => 'Registered';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get notRegisteredStatus => 'Not registered';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get noRecipientSelected => 'No recipient selected';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get companyName => 'Name of your company';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get companynameRequired => 'Company name required';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorSignUp => 'Error occured while signing up, try again later';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get companyDescription => 'Company Description';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get companyDescriptionHint =>
|
|
||||||
'Describe any of the fields of the Company\'s business';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get optional => 'optional';
|
|
||||||
}
|
|
||||||
@@ -1,782 +0,0 @@
|
|||||||
// ignore: unused_import
|
|
||||||
import 'package:intl/intl.dart' as intl;
|
|
||||||
import 'app_localizations.dart';
|
|
||||||
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
|
|
||||||
/// The translations for Russian (`ru`).
|
|
||||||
class AppLocalizationsRu extends AppLocalizations {
|
|
||||||
AppLocalizationsRu([String locale = 'ru']) : super(locale);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get login => 'Войти';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get logout => 'Выйти';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get profile => 'Профиль';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get signup => 'Регистрация';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get username => 'Email';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get usernameHint => 'email@example.com';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get usernameErrorInvalid =>
|
|
||||||
'Укажите действительный адрес электронной почты';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String usernameUnknownTLD(Object domain) {
|
|
||||||
return 'Домен .$domain неизвестен, пожалуйста, проверьте его';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get password => 'Пароль';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get confirmPassword => 'Подтвердите пароль';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get passwordValidationRuleDigit => 'содержит цифру';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get passwordValidationRuleUpperCase => 'содержит заглавную букву';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get passwordValidationRuleLowerCase => 'содержит строчную букву';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get passwordValidationRuleSpecialCharacter =>
|
|
||||||
'содержит специальный символ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String passwordValidationRuleMinCharacters(Object charNum) {
|
|
||||||
return 'длина не менее $charNum символов';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get passwordsDoNotMatch => 'Пароли не совпадают';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String passwordValidationError(Object matchesCriteria) {
|
|
||||||
return 'Убедитесь, что ваш пароль $matchesCriteria';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String notificationError(Object error) {
|
|
||||||
return 'Произошла ошибка: $error';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String loginUserNotFound(Object account) {
|
|
||||||
return 'Аккаунт $account не зарегистрирован в системе';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get loginPasswordIncorrect =>
|
|
||||||
'Ошибка авторизации, пожалуйста, проверьте пароль';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String internalErrorOccurred(Object error) {
|
|
||||||
return 'Произошла внутренняя ошибка сервера: $error, мы уже знаем о ней и усердно работаем над исправлением';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get noErrorInformation =>
|
|
||||||
'Произошла ошибка, но у нас нет информации о ней. Мы уже расследуем этот вопрос';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get yourName => 'Ваше имя';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get nameHint => 'Иван Иванов';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorPageNotFoundTitle => 'Страница не найдена';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorPageNotFoundMessage =>
|
|
||||||
'Упс! Мы не смогли найти эту страницу.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorPageNotFoundHint =>
|
|
||||||
'Запрашиваемая страница не существует или была перемещена. Пожалуйста, проверьте URL или вернитесь на главную страницу.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorUnknown => 'Произошла неизвестная ошибка';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get unknown => 'неизвестно';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get goToLogin => 'Перейти к входу';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get goBack => 'Назад';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get goToMainPage => 'На главную';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get goToSignUp => 'Перейти к регистрации';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String signupError(Object error) {
|
|
||||||
return 'Не удалось зарегистрироваться: $error';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String signupSuccess(Object email) {
|
|
||||||
return 'Письмо с подтверждением email отправлено на $email. Пожалуйста, откройте его и перейдите по ссылке для активации вашего аккаунта.';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String connectivityError(Object serverAddress) {
|
|
||||||
return 'Не удается связаться с сервером $serverAddress. Проверьте ваше интернет-соединение и попробуйте снова.';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorAccountExists => 'Account already exists';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorAccountNotVerified =>
|
|
||||||
'Ваш аккаунт еще не подтвержден. Пожалуйста, проверьте вашу электронную почту для завершения верификации';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorLoginUnauthorized =>
|
|
||||||
'Неверный логин или пароль. Пожалуйста, попробуйте снова';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorInternalError =>
|
|
||||||
'Произошла внутренняя ошибка. Мы в курсе проблемы и работаем над ее решением. Пожалуйста, попробуйте позже';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorVerificationTokenNotFound =>
|
|
||||||
'Аккаунт для верификации не найден. Зарегистрируйтесь снова';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get created => 'Создано';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get edited => 'Изменено';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorDataConflict =>
|
|
||||||
'Мы не можем обработать ваши данные, так как они содержат конфликтующую или противоречивую информацию.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorAccessDenied =>
|
|
||||||
'У вас нет разрешения на доступ к этому ресурсу. Если вам нужен доступ, пожалуйста, обратитесь к администратору.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorBrokenPayload =>
|
|
||||||
'Отправленные данные недействительны или неполны. Пожалуйста, проверьте введенные данные и попробуйте снова.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorInvalidArgument =>
|
|
||||||
'Один или несколько аргументов недействительны. Проверьте введенные данные и попробуйте снова.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorBrokenReference =>
|
|
||||||
'Ресурс, к которому вы пытаетесь получить доступ, не может быть найден. Возможно, он был перемещен или удален.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorInvalidQueryParameter =>
|
|
||||||
'Один или несколько параметров запроса отсутствуют или указаны неверно. Проверьте их и попробуйте снова.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorNotImplemented =>
|
|
||||||
'Эта функция еще недоступна. Пожалуйста, попробуйте позже или обратитесь в службу поддержки.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorLicenseRequired =>
|
|
||||||
'Для выполнения этого действия требуется действующая лицензия. Пожалуйста, обратитесь к вашему администратору.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorNotFound =>
|
|
||||||
'Мы не смогли найти запрошенный ресурс. Возможно, он был удален или временно недоступен.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorNameMissing => 'Пожалуйста, укажите имя для продолжения.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorEmailMissing =>
|
|
||||||
'Пожалуйста, укажите адрес электронной почты для продолжения.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorPasswordMissing =>
|
|
||||||
'Пожалуйста, укажите пароль для продолжения.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorEmailNotRegistered =>
|
|
||||||
'Мы не нашли аккаунт, связанный с этим адресом электронной почты.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorDuplicateEmail =>
|
|
||||||
'Этот адрес электронной почты уже используется. Попробуйте другой или восстановите пароль.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get showDetailsAction => 'Показать детали';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorLogin => 'Ошибка входа';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorCreatingInvitation => 'Не удалось создать приглашение';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get footerCompanyName => 'Sibilla Solutions LTD';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get footerAddress =>
|
|
||||||
'27, Pindarou Street, Alpha Business Centre, Block B 7th Floor, 1060 Nicosia, Cyprus';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get footerSupport => 'Поддержка';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get footerEmail => 'Email TBD';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get footerPhoneLabel => 'Телефон';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get footerPhone => '+357 22 000 253';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get footerTermsOfService => 'Условия обслуживания';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get footerPrivacyPolicy => 'Политика конфиденциальности';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get footerCookiePolicy => 'Политика использования файлов cookie';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get navigationLogout => 'Выйти';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get dashboard => 'Дашборд';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get navigationUsersSettings => 'Пользователи';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get navigationRolesSettings => 'Роли';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get navigationPermissionsSettings => 'Разрешения';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get usersManagement => 'Управление пользователями';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get navigationOrganizationSettings => 'Настройки организации';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get navigationAccountSettings => 'Настройки профиля';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get twoFactorPrompt =>
|
|
||||||
'Введите 6-значный код, отправленный на ваше устройство';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get twoFactorResend => 'Не получили код? Отправить снова';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get twoFactorTitle => 'Двухфакторная аутентификация';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get twoFactorError => 'Неверный код. Пожалуйста, попробуйте снова.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get payoutNavDashboard => 'Дашборд';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get payoutNavSendPayout => 'Отправить выплату';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get payoutNavRecipients => 'Получатели';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get payoutNavReports => 'Отчеты';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get payoutNavSettings => 'Настройки';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get payoutNavLogout => 'Выйти';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get payoutNavMethods => 'Выплаты';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get expand => 'Развернуть';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get collapse => 'Свернуть';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get pageTitleRecipients => 'Адресная книга получателей';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get actionAddNew => 'Добавить';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get colDataOwner => 'Владелец данных';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get colAvatar => 'Аватар';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get colName => 'Имя';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get colEmail => 'Email';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get colStatus => 'Статус';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get statusReady => 'Готов';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get statusRegistered => 'Зарегистрирован';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get statusNotRegistered => 'Не зарегистрирован';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get typeInternal => 'Управляется мной';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get typeExternal => 'Самоуправляемый';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get searchHint => 'Поиск получателей';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get colActions => 'Действия';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get menuEdit => 'Редактировать';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get menuSendPayout => 'Отправить выплату';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get tooltipRowActions => 'Другие действия';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get accountSettings => 'Настройки аккаунта';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get accountNameUpdateError => 'Не удалось обновить имя аккаунта';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get settingsSuccessfullyUpdated => 'Настройки успешно обновлены';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get language => 'Язык';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get failedToUpdateLanguage => 'Не удалось обновить язык';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get settingsImageUpdateError => 'Не удалось обновить изображение';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get settingsImageTitle => 'Изображение';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get settingsImageHint => 'Нажмите, чтобы изменить изображение';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get accountName => 'Имя';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get accountNameHint => 'Укажите ваше имя';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get avatar => 'Фото профиля';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get avatarHint => 'Нажмите для обновления';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get avatarUpdateError => 'Не удалось обновить фото профиля';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get settings => 'Настройки';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get notSet => 'не задано';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get search => 'Поиск...';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get ok => 'Ок';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cancel => 'Отмена';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get confirm => 'Подтвердить';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get back => 'Назад';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get operationfryTitle => 'История операций';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get filters => 'Фильтры';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get period => 'Период';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get selectPeriod => 'Выберите период';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get apply => 'Применить';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String status(String status) {
|
|
||||||
return '$status';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get operationStatusSuccessful => 'Успешно';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get operationStatusPending => 'В ожидании';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get operationStatusUnsuccessful => 'Неуспешно';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get statusColumn => 'Статус';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get fileNameColumn => 'Имя файла';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get amountColumn => 'Сумма';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get toAmountColumn => 'На сумму';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get payIdColumn => 'Pay ID';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cardNumberColumn => 'Номер карты';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get nameColumn => 'Имя';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get dateColumn => 'Дата';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get commentColumn => 'Комментарий';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get paymentConfigTitle => 'Куда получать деньги';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get paymentConfigSubtitle =>
|
|
||||||
'Добавьте несколько методов и выберите основной.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get addPaymentMethod => 'Добавить способ оплаты';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get makeMain => 'Сделать основным';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get advanced => 'Дополнительно';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get fallbackExplanation =>
|
|
||||||
'Если основной метод недоступен, мы попробуем следующий включенный метод в списке.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get delete => 'Удалить';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get deletePaymentConfirmation =>
|
|
||||||
'Вы уверены, что хотите удалить этот способ оплаты?';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get edit => 'Редактировать';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get moreActions => 'Еще действия';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get noPayouts => 'Нет выплат';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterBankName => 'Введите название банка';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get paymentType => 'Тип способа оплаты';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get selectPaymentType => 'Пожалуйста, выберите тип способа оплаты';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get paymentTypeCard => 'Кредитная карта';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get paymentTypeBankAccount => 'Российский банковский счет';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get paymentTypeIban => 'IBAN';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get paymentTypeWallet => 'Кошелек';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cardNumber => 'Номер карты';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterCardNumber => 'Введите номер карты';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cardholderName => 'Имя держателя карты';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get iban => 'IBAN';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterIban => 'Введите IBAN';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get bic => 'BIC';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get bankName => 'Название банка';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get accountHolder => 'Владелец счета';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterAccountHolder => 'Введите владельца счета';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterBic => 'Введите BIC';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get walletId => 'ID кошелька';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterWalletId => 'Введите ID кошелька';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get recipients => 'Получатели';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get recipientName => 'Имя получателя';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterRecipientName => 'Введите имя получателя';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get inn => 'ИНН';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterInn => 'Введите ИНН';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get kpp => 'КПП';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterKpp => 'Введите КПП';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get accountNumber => 'Номер счета';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterAccountNumber => 'Введите номер счета';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get correspondentAccount => 'Корреспондентский счет';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterCorrespondentAccount => 'Введите корреспондентский счет';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get bik => 'БИК';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterBik => 'Введите БИК';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get add => 'Добавить';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get expiryDate => 'Срок действия (ММ/ГГ)';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get firstName => 'Имя';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterFirstName => 'Введите имя';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get lastName => 'Фамилия';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get enterLastName => 'Введите фамилию';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get sendSingle => 'Отправить одну транзакцию';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get sendMultiple => 'Отправить несколько транзакций';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get addFunds => 'Пополнить счет';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get close => 'Закрыть';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get multiplePayout => 'Множественная выплата';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get howItWorks => 'Как это работает?';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get exampleTitle => 'Формат файла и образец';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get downloadSampleCSV => 'Скачать sample.csv';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get tokenColumn => 'Токен (обязательно)';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get currency => 'Валюта';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get amount => 'Сумма';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get comment => 'Комментарий';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get uploadCSV => 'Загрузите ваш CSV';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get upload => 'Загрузить';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get hintUpload => 'Поддерживаемый формат: .CSV · Макс. размер 1 МБ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get uploadHistory => 'История загрузок';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get payout => 'Выплата';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get sendTo => 'Отправить выплату';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get send => 'Отправить выплату';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get recipientPaysFee => 'Получатель оплачивает комиссию';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String sentAmount(String amount) {
|
|
||||||
return 'Отправленная сумма: \$$amount';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String fee(String fee) {
|
|
||||||
return 'Комиссия: \$$fee';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String recipientWillReceive(String amount) {
|
|
||||||
return 'Получатель получит: \$$amount';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String total(String total) {
|
|
||||||
return 'Итого: \$$total';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get hideDetails => 'Скрыть детали';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get showDetails => 'Показать детали';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get whereGetMoney => 'Источник средств для списания';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get details => 'Детали';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get addRecipient => 'Добавить получателя';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get editRecipient => 'Редактировать получателя';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get saveRecipient => 'Сохранить получателя';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get choosePaymentMethod => 'Способы оплаты (выберите хотя бы 1)';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get recipientFormRule =>
|
|
||||||
'Получатель должен иметь хотя бы один способ оплаты';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get allStatus => 'Все';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get readyStatus => 'Готов';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get registeredStatus => 'Зарегистрирован';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get notRegisteredStatus => 'Не зарегистрирован';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get noRecipientSelected => 'Получатель не выбран';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get companyName => 'Name of your company';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get companynameRequired => 'Company name required';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get errorSignUp => 'Error occured while signing up, try again later';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get companyDescription => 'Company Description';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get companyDescriptionHint =>
|
|
||||||
'Describe any of the fields of the Company\'s business';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get optional => 'optional';
|
|
||||||
}
|
|
||||||
@@ -431,5 +431,7 @@
|
|||||||
"errorSignUp": "Error occured while signing up, try again later",
|
"errorSignUp": "Error occured while signing up, try again later",
|
||||||
"companyDescription": "Company Description",
|
"companyDescription": "Company Description",
|
||||||
"companyDescriptionHint": "Describe any of the fields of the Company's business",
|
"companyDescriptionHint": "Describe any of the fields of the Company's business",
|
||||||
"optional": "optional"
|
"optional": "optional",
|
||||||
|
"ownerRole": "Organization Owner",
|
||||||
|
"ownerRoleDescription": "This role is granted to the organization’s creator, providing full administrative privileges"
|
||||||
}
|
}
|
||||||
@@ -422,5 +422,8 @@
|
|||||||
"registeredStatus": "Зарегистрирован",
|
"registeredStatus": "Зарегистрирован",
|
||||||
"notRegisteredStatus": "Не зарегистрирован",
|
"notRegisteredStatus": "Не зарегистрирован",
|
||||||
|
|
||||||
"noRecipientSelected": "Получатель не выбран"
|
"noRecipientSelected": "Получатель не выбран",
|
||||||
|
|
||||||
|
"ownerRole": "Владелец организации",
|
||||||
|
"ownerRoleDescription": "Эта роль предоставляется создателю организации и даёт ему полные административные права"
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,6 @@ import 'package:pshared/config/constants.dart';
|
|||||||
import 'package:pshared/provider/account.dart';
|
import 'package:pshared/provider/account.dart';
|
||||||
import 'package:pshared/provider/locale.dart';
|
import 'package:pshared/provider/locale.dart';
|
||||||
import 'package:pshared/provider/organizations.dart';
|
import 'package:pshared/provider/organizations.dart';
|
||||||
import 'package:pshared/provider/pfe/provider.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';
|
||||||
@@ -66,7 +65,7 @@ void main() async {
|
|||||||
ChangeNotifierProvider(create: (_) => LocaleProvider(null)),
|
ChangeNotifierProvider(create: (_) => LocaleProvider(null)),
|
||||||
ChangeNotifierProvider(create: (_) => AccountProvider()),
|
ChangeNotifierProvider(create: (_) => AccountProvider()),
|
||||||
ChangeNotifierProvider(create: (_) => OrganizationsProvider()),
|
ChangeNotifierProvider(create: (_) => OrganizationsProvider()),
|
||||||
ChangeNotifierProvider(create: (_) => PfeProvider()),
|
ChangeNotifierProvider(create: (_) => AccountProvider()),
|
||||||
ChangeNotifierProvider(create: (_) => CarouselIndexProvider()),
|
ChangeNotifierProvider(create: (_) => CarouselIndexProvider()),
|
||||||
|
|
||||||
ChangeNotifierProvider(
|
ChangeNotifierProvider(
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:pshared/provider/pfe/provider.dart';
|
import 'package:pshared/provider/account.dart';
|
||||||
|
import 'package:pshared/provider/locale.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';
|
||||||
@@ -34,17 +35,19 @@ class _LoginFormState extends State<LoginForm> {
|
|||||||
final ValueNotifier<bool> _isPasswordAcceptable = ValueNotifier<bool>(false);
|
final ValueNotifier<bool> _isPasswordAcceptable = ValueNotifier<bool>(false);
|
||||||
|
|
||||||
Future<String?> _login(BuildContext context, VoidCallback onLogin, void Function(Object e) onError) async {
|
Future<String?> _login(BuildContext context, VoidCallback onLogin, void Function(Object e) onError) async {
|
||||||
final pfeProvider = Provider.of<PfeProvider>(context, listen: false);
|
final provider = Provider.of<AccountProvider>(context, listen: false);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// final account = await pfeProvider.login(
|
//final account =
|
||||||
// email: _usernameController.text,
|
await provider.login(
|
||||||
// password: _passwordController.text,
|
email: _usernameController.text,
|
||||||
// );
|
password: _passwordController.text,
|
||||||
|
locale: context.read<LocaleProvider>().locale.languageCode,
|
||||||
|
);
|
||||||
onLogin();
|
onLogin();
|
||||||
return 'ok';
|
return 'ok';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
onError(pfeProvider.error == null ? e : pfeProvider.error!);
|
onError(provider.error ?? e);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:flutter_timezone/flutter_timezone.dart';
|
||||||
|
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:pshared/provider/pfe/provider.dart';
|
import 'package:pshared/api/requests/login_data.dart';
|
||||||
|
import 'package:pshared/models/describable.dart';
|
||||||
|
import 'package:pshared/provider/account.dart';
|
||||||
|
import 'package:pshared/provider/locale.dart';
|
||||||
|
|
||||||
import 'package:pweb/app/router/pages.dart';
|
import 'package:pweb/app/router/pages.dart';
|
||||||
import 'package:pweb/pages/signup/form/content.dart';
|
import 'package:pweb/pages/signup/form/content.dart';
|
||||||
@@ -31,7 +36,7 @@ class SignUpFormState extends State<SignUpForm> {
|
|||||||
VoidCallback onSignUp,
|
VoidCallback onSignUp,
|
||||||
void Function(Object e) onError,
|
void Function(Object e) onError,
|
||||||
) async {
|
) async {
|
||||||
final pfeProvider = Provider.of<PfeProvider>(context, listen: false);
|
final provider = Provider.of<AccountProvider>(context, listen: false);
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_autoValidateMode = true;
|
_autoValidateMode = true;
|
||||||
@@ -42,20 +47,34 @@ class SignUpFormState extends State<SignUpForm> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// final account = await pfeProvider.signUp(
|
final orgDescription = controllers.description.text.trim();
|
||||||
// companyName: controllers.companyName.text.trim(),
|
final locs = AppLocalizations.of(context)!;
|
||||||
// description: controllers.description.text.trim().isEmpty
|
final locale = context.read<LocaleProvider>().locale;
|
||||||
// ? null
|
final timezone = await FlutterTimezone.getLocalTimezone(locale.toString());
|
||||||
// : controllers.description.text.trim(),
|
await provider.signup(
|
||||||
// firstName: controllers.firstName.text.trim(),
|
account: AccountData.build(
|
||||||
// lastName: controllers.lastName.text.trim(),
|
login: LoginData.build(
|
||||||
// email: controllers.email.text.trim(),
|
login: controllers.email.text.trim(),
|
||||||
// password: controllers.password.text,
|
password: controllers.password.text,
|
||||||
// );
|
locale: locale.toLanguageTag(),
|
||||||
|
),
|
||||||
|
name: controllers.password.text,
|
||||||
|
lastName: controllers.lastName.text.trim(),
|
||||||
|
),
|
||||||
|
organization: newDescribable(
|
||||||
|
name: controllers.companyName.text.trim(),
|
||||||
|
description: orgDescription.isEmpty ? null : orgDescription,
|
||||||
|
),
|
||||||
|
timezone: timezone.identifier,
|
||||||
|
ownerRole: newDescribable(
|
||||||
|
name: locs.ownerRole,
|
||||||
|
description: locs.ownerRoleDescription,
|
||||||
|
),
|
||||||
|
);
|
||||||
onSignUp();
|
onSignUp();
|
||||||
return 'ok';
|
return 'ok';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
onError(pfeProvider.error ?? e);
|
onError(provider.error ?? e);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user