refactor:

This commit is contained in:
Vyacheslav1557 2024-10-09 22:07:38 +05:00
parent ba26d05516
commit 93eaf89d78
34 changed files with 1270 additions and 742 deletions

View file

@ -1,4 +1,4 @@
package lib
package config
type Config struct {
Env string `env:"ENV" env-default:"prod"`

60
go.mod
View file

@ -3,25 +3,71 @@ module git.sch9.ru/new_gate/ms-auth
go 1.21.3
require (
github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/google/uuid v1.6.0
github.com/ilyakaznacheev/cleanenv v1.5.0
github.com/valkey-io/valkey-go v1.0.38
golang.org/x/crypto v0.24.0
google.golang.org/grpc v1.64.0
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438
github.com/stretchr/testify v1.9.0
github.com/testcontainers/testcontainers-go/modules/valkey v0.33.0
github.com/valkey-io/valkey-go v1.0.41
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.25.0
google.golang.org/grpc v1.64.1
google.golang.org/protobuf v1.34.1
)
require (
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 // indirect
dario.cat/mergo v1.0.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/containerd/containerd v1.7.19 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/user v0.1.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/shirou/gopsutil/v3 v3.24.5 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/testcontainers/testcontainers-go v0.33.0 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.8.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
go.opentelemetry.io/otel v1.28.0 // indirect
go.opentelemetry.io/otel/metric v1.28.0 // indirect
go.opentelemetry.io/otel/trace v1.28.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
)

178
go.sum
View file

@ -1,18 +1,62 @@
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/containerd/containerd v1.7.19 h1:/xQ4XRJ0tamDkdzrrBAUy/LE5nCcxFKdBm4EcPrSMEE=
github.com/containerd/containerd v1.7.19/go.mod h1:h4FtNYUUMB4Phr6v+xG89RYKj9XccvbNSCKjdufCrkc=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E=
github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
github.com/ilyakaznacheev/cleanenv v1.5.0 h1:0VNZXggJE2OYdXE87bfSSwGxeiGt9moSR2lOrsHHvr4=
github.com/ilyakaznacheev/cleanenv v1.5.0/go.mod h1:a5aDzaJrLCQZsazHol1w8InnDcOX0OColm64SlIi6gk=
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0=
@ -29,45 +73,149 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae h1:dIZY4ULFcto4tAFlj1FYZl8ztUZ13bdq+PLY+NOfbyI=
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg=
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/valkey-io/valkey-go v1.0.38 h1:0g+ozx+WUGmu18h8SVGoIyxdWp820utVLlFkGnQ3h0c=
github.com/valkey-io/valkey-go v1.0.38/go.mod h1:LXqAbjygRuA1YRocojTslAGx2dQB4p8feaseGviWka4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/testcontainers/testcontainers-go v0.33.0 h1:zJS9PfXYT5O0ZFXM2xxXfk4J5UMw/kRiISng037Gxdw=
github.com/testcontainers/testcontainers-go v0.33.0/go.mod h1:W80YpTa8D5C3Yy16icheD01UTDu+LmXIA2Keo+jWtT8=
github.com/testcontainers/testcontainers-go/modules/valkey v0.33.0 h1:yLD+3pvvTQiU0h9Aj6/2D+iaoDDl+IaMm0rGEUxQlZs=
github.com/testcontainers/testcontainers-go/modules/valkey v0.33.0/go.mod h1:q4939OS+DBYCAbwALicXW77GPt5TGrBnVLTlWzZBxOY=
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
github.com/valkey-io/valkey-go v1.0.41 h1:pWgh9MP24Vl0ANZ0KxEMwB/LHvTUKwlm2SPuWIrSlFw=
github.com/valkey-io/valkey-go v1.0.41/go.mod h1:LXqAbjygRuA1YRocojTslAGx2dQB4p8feaseGviWka4=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13 h1:vlzZttNJGVqTsRFU9AmdnrcO1Znh8Ew9kCD//yjigk0=
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4=
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA=
google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -76,5 +224,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 h1:slmdOY3vp8a7KQbHkL+FLbvbkgMqmXojpFUO/jENuqQ=
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3/go.mod h1:oVgVk4OWVDi43qWBEyGhXgYxt7+ED4iYNpTngSLX2Iw=

View file

@ -1,6 +1,8 @@
package models
import "git.sch9.ru/new_gate/ms-auth/internal/lib"
import (
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
)
type Role int32
@ -40,5 +42,5 @@ func (role Role) Valid() error {
case RoleSpectator, RoleParticipant, RoleModerator, RoleAdmin:
return nil
}
return lib.ErrBadRole
return utils.ErrBadRole
}

View file

@ -1,7 +1,7 @@
package models
import (
"git.sch9.ru/new_gate/ms-auth/internal/lib"
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
"github.com/golang-jwt/jwt"
"github.com/google/uuid"
)
@ -13,17 +13,17 @@ type Session struct {
func NewSession(userId int32) *Session {
return &Session{
Id: lib.AsStringP(uuid.NewString()),
Id: utils.AsStringP(uuid.NewString()),
UserId: &userId,
}
}
func (s Session) Valid() error {
if s.Id == nil {
return lib.ErrBadSession
return utils.ErrBadSession
}
if s.UserId == nil {
return lib.ErrBadSession
return utils.ErrBadSession
}
return nil
}
@ -35,7 +35,7 @@ func (s Session) Token(secret string) (string, error) {
refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, s)
str, err := refreshToken.SignedString([]byte(secret))
if err != nil {
return "", lib.ErrBadSession
return "", utils.ErrBadSession
}
return str, nil
}
@ -45,7 +45,7 @@ func Parse(tkn string, secret string) (*Session, error) {
return []byte(secret), nil
})
if err != nil {
return nil, lib.ErrBadSession
return nil, utils.ErrBadSession
}
session := parsedToken.Claims.(*Session)
if err = session.Valid(); err != nil {

View file

@ -1,7 +1,7 @@
package models
import (
"git.sch9.ru/new_gate/ms-auth/internal/lib"
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
"golang.org/x/crypto/bcrypt"
"time"
)
@ -19,10 +19,10 @@ type User struct {
func (user *User) ValidUsername() error {
if user.Username == nil {
return lib.ErrBadUsername
return utils.ErrBadUsername
}
err := lib.ValidUsername(*user.Username)
err := utils.ValidUsername(*user.Username)
if err != nil {
return err
}
@ -32,10 +32,10 @@ func (user *User) ValidUsername() error {
func (user *User) ValidPassword() error {
if user.Password == nil {
return lib.ErrBadHandleOrPassword
return utils.ErrBadHandleOrPassword
}
err := lib.ValidPassword(*user.Password)
err := utils.ValidPassword(*user.Password)
if err != nil {
return err
}
@ -45,37 +45,37 @@ func (user *User) ValidPassword() error {
func (user *User) ValidEmail() error {
if user.Email == nil {
return lib.ErrBadEmail
return utils.ErrBadEmail
}
return lib.ValidEmail(*user.Email)
return utils.ValidEmail(*user.Email)
}
func (user *User) ValidRole() error {
if user.Role == nil {
return lib.ErrBadRole
return utils.ErrBadRole
}
return user.Role.Valid()
}
func (user *User) HashPassword() error {
if user.Password == nil {
return lib.ErrBadHandleOrPassword
return utils.ErrBadHandleOrPassword
}
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(*user.Password), bcrypt.DefaultCost)
if err != nil {
return lib.ErrInternal
return utils.ErrInternal
}
user.Password = lib.AsStringP(string(hashedPassword))
user.Password = utils.AsStringP(string(hashedPassword))
return nil
}
func (user *User) ComparePassword(password string) error {
if user.Password == nil {
return lib.ErrBadHandleOrPassword
return utils.ErrBadHandleOrPassword
}
err := bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(password))
if err != nil {
return lib.ErrBadHandleOrPassword
return utils.ErrBadHandleOrPassword
}
return nil
}

View file

@ -1,103 +0,0 @@
package services
import (
"context"
"git.sch9.ru/new_gate/ms-auth/internal/lib"
"git.sch9.ru/new_gate/ms-auth/internal/models"
)
type SessionProvider interface {
CreateSession(ctx context.Context, userId int32) error
ReadSessionByToken(ctx context.Context, token string) (*models.Session, error)
ReadSessionByUserId(ctx context.Context, userId int32) (*models.Session, error)
UpdateSession(ctx context.Context, session *models.Session) error
DeleteSessionByToken(ctx context.Context, token string) error
DeleteSessionByUserId(ctx context.Context, userId int32) error
}
type SessionService struct {
sessionProvider SessionProvider
userProvider UserStorage
cfg lib.Config
}
func NewSessionService(sessionProvider SessionProvider, userProvider UserStorage, cfg lib.Config) *SessionService {
return &SessionService{
sessionProvider: sessionProvider,
userProvider: userProvider,
cfg: cfg,
}
}
func (s *SessionService) Create(ctx context.Context, handle, password string) (*string, error) {
var (
err error
user *models.User
)
if lib.ValidUsername(handle) == nil {
user, err = s.userProvider.ReadUserByUsername(ctx, handle)
} else if lib.ValidEmail(handle) == nil {
user, err = s.userProvider.ReadUserByEmail(ctx, handle)
} else {
return nil, lib.ErrBadHandleOrPassword
}
if err != nil {
return nil, err
}
err = user.ComparePassword(password)
if err != nil {
return nil, err
}
//err = s.sessionProvider.CreateSession(ctx, *user.Id)
//if err != nil {
// return nil, err
//}
s.sessionProvider.CreateSession(ctx, *user.Id) // FIXME
session, err := s.sessionProvider.ReadSessionByUserId(ctx, *user.Id)
if err != nil {
return nil, err
}
token, err := session.Token(s.cfg.JWTSecret)
if err != nil {
return nil, err
}
return &token, nil
}
func (s *SessionService) Read(ctx context.Context, token string) (*int32, error) {
session, err := s.sessionProvider.ReadSessionByToken(ctx, token)
if err != nil {
return nil, err
}
return session.UserId, nil
}
func (s *SessionService) Update(ctx context.Context, token string) error {
session, err := s.sessionProvider.ReadSessionByToken(ctx, token)
if err != nil {
return err
}
err = s.sessionProvider.UpdateSession(ctx, session)
if err != nil {
return err
}
return nil
}
func (s *SessionService) Delete(ctx context.Context, token string) error {
session, err := s.sessionProvider.ReadSessionByToken(ctx, token)
if err != nil {
return err
}
err = s.sessionProvider.DeleteSessionByUserId(ctx, *session.UserId)
if err != nil {
return err
}
return nil
}

View file

@ -1,131 +0,0 @@
package services
import (
"context"
"git.sch9.ru/new_gate/ms-auth/internal/lib"
"git.sch9.ru/new_gate/ms-auth/internal/models"
)
type UserStorage interface {
CreateUser(ctx context.Context, user *models.User) (int32, error)
ReadUserByEmail(ctx context.Context, email string) (*models.User, error)
ReadUserByUsername(ctx context.Context, username string) (*models.User, error)
ReadUserById(ctx context.Context, id int32) (*models.User, error)
UpdateUser(ctx context.Context, user *models.User) error
DeleteUser(ctx context.Context, id int32) error
}
type UserService struct {
userProvider UserStorage
sessionProvider SessionProvider
cfg lib.Config
}
func NewUserService(
userProvider UserStorage,
sessionProvider SessionProvider,
cfg lib.Config,
) *UserService {
return &UserService{
userProvider: userProvider,
sessionProvider: sessionProvider,
cfg: cfg,
}
}
func (u *UserService) CreateUser(ctx context.Context, user *models.User) (int32, error) {
me := ctx.Value("user").(*models.User)
switch *me.Role {
case models.RoleAdmin:
break
case models.RoleModerator:
if !user.Role.AtMost(models.RoleParticipant) {
return 0, lib.ErrNoPermission
}
default:
return 0, lib.ErrNoPermission
}
return u.userProvider.CreateUser(ctx, user)
}
func (u *UserService) ReadUserBySessionToken(ctx context.Context, token string) (*models.User, error) {
session, err := u.sessionProvider.ReadSessionByToken(ctx, token)
if err != nil {
return nil, err
}
return u.userProvider.ReadUserById(ctx, *session.UserId)
}
func (u *UserService) ReadUser(ctx context.Context, id int32) (*models.User, error) {
return u.userProvider.ReadUserById(ctx, id)
}
func (u *UserService) ReadUserByEmail(ctx context.Context, email string) (*models.User, error) {
return u.userProvider.ReadUserByEmail(ctx, email)
}
func (u *UserService) ReadUserByUsername(ctx context.Context, username string) (*models.User, error) {
return u.userProvider.ReadUserByUsername(ctx, username)
}
func (u *UserService) UpdateUser(ctx context.Context, modifiedUser *models.User) error {
me := ctx.Value("user").(*models.User)
user, err := u.userProvider.ReadUserById(ctx, *modifiedUser.Id)
if err != nil {
return err
}
hasAccess := func() bool {
if me.Role.IsAdmin() {
return true
}
if me.Role.IsModerator() {
if !user.Role.AtMost(models.RoleParticipant) {
return false
}
return true
}
if me.Role.IsParticipant() {
if me.Id != user.Id {
return false
}
if modifiedUser.Username != nil {
return false
}
if modifiedUser.Email != nil {
return false
}
if modifiedUser.ExpiresAt != nil {
return false
}
if modifiedUser.Role != nil {
return false
}
return true
}
if me.Role.IsSpectator() {
return false
}
return false
}()
if !hasAccess {
return lib.ErrNoPermission
}
return u.userProvider.UpdateUser(ctx, user)
}
func (u *UserService) DeleteUser(ctx context.Context, id int32) error {
me := ctx.Value("user").(*models.User)
if *me.Id == id || !me.Role.IsAdmin() {
return lib.ErrNoPermission
}
return u.userProvider.DeleteUser(ctx, id)
}

View file

@ -0,0 +1,14 @@
package sessions
import (
"context"
sessionv1 "git.sch9.ru/new_gate/ms-auth/pkg/go/gen/proto/session/v1"
"google.golang.org/protobuf/types/known/emptypb"
)
type SessionHandlers interface {
Create(ctx context.Context, req *sessionv1.CreateSessionRequest) (*sessionv1.CreateSessionResponse, error)
Read(ctx context.Context, req *sessionv1.ReadSessionRequest) (*sessionv1.ReadSessionResponse, error)
Update(ctx context.Context, req *sessionv1.UpdateSessionRequest) (*emptypb.Empty, error)
Delete(ctx context.Context, req *sessionv1.DeleteSessionRequest) (*emptypb.Empty, error)
}

View file

@ -0,0 +1,89 @@
package grpc
import (
"context"
"git.sch9.ru/new_gate/ms-auth/internal/models"
"git.sch9.ru/new_gate/ms-auth/internal/sessions"
"git.sch9.ru/new_gate/ms-auth/internal/users"
sessionv1 "git.sch9.ru/new_gate/ms-auth/pkg/go/gen/proto/session/v1"
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
)
type sessionHandlers struct {
sessionv1.UnimplementedSessionServiceServer
sessionUC sessions.UseCase
userUC users.UseCase
}
func NewSessionHandlers(gserver *grpc.Server, sessionUC sessions.UseCase, userUC users.UseCase) {
handlers := &sessionHandlers{
sessionUC: sessionUC,
userUC: userUC,
}
sessionv1.RegisterSessionServiceServer(gserver, handlers)
}
func (s *sessionHandlers) Create(ctx context.Context, req *sessionv1.CreateSessionRequest) (*sessionv1.CreateSessionResponse, error) {
var (
err error
user *models.User
)
handle := req.GetHandle()
password := req.GetPassword()
if utils.ValidUsername(handle) == nil {
user, err = s.userUC.ReadUserByUsername(ctx, req.GetHandle())
} else if utils.ValidEmail(handle) == nil {
user, err = s.userUC.ReadUserByEmail(ctx, handle)
} else {
return nil, utils.ErrBadHandleOrPassword
}
if err != nil {
return nil, err
}
err = user.ComparePassword(password)
if err != nil {
return nil, err
}
token, err := s.sessionUC.Create(ctx, *user.Id)
if err != nil {
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
}
return &sessionv1.CreateSessionResponse{
Token: *token,
}, nil
}
func (s *sessionHandlers) Read(ctx context.Context, req *sessionv1.ReadSessionRequest) (*sessionv1.ReadSessionResponse, error) {
id, err := s.sessionUC.Read(ctx, req.GetToken())
if err != nil {
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
}
return &sessionv1.ReadSessionResponse{
UserId: *id,
}, nil
}
func (s *sessionHandlers) Update(ctx context.Context, req *sessionv1.UpdateSessionRequest) (*emptypb.Empty, error) {
err := s.sessionUC.Update(ctx, req.GetToken())
if err != nil {
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
}
return &emptypb.Empty{}, nil
}
func (s *sessionHandlers) Delete(ctx context.Context, req *sessionv1.DeleteSessionRequest) (*emptypb.Empty, error) {
err := s.sessionUC.Delete(ctx, req.GetToken())
if err != nil {
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
}
return &emptypb.Empty{}, nil
}

View file

@ -0,0 +1,126 @@
package repository
import (
"context"
"git.sch9.ru/new_gate/ms-auth/config"
"git.sch9.ru/new_gate/ms-auth/internal/models"
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
"github.com/valkey-io/valkey-go"
"go.uber.org/zap"
"time"
)
type valkeyRepository struct {
db valkey.Client
cfg config.Config
logger *zap.Logger
}
func NewValkeyRepository(db valkey.Client, cfg config.Config, logger *zap.Logger) *valkeyRepository {
return &valkeyRepository{
db: db,
cfg: cfg,
logger: logger,
}
}
const sessionLifetime = time.Minute * 40
func (r *valkeyRepository) CreateSession(ctx context.Context, userId int32) error {
session := models.NewSession(userId)
resp := r.db.Do(ctx, r.db.
B().Set().
Key(string(*session.UserId)).
Value(*session.Id).
Nx().
Exat(time.Now().Add(sessionLifetime)).
Build(),
)
if err := resp.Error(); err != nil {
return utils.ErrInternal
}
return nil
}
func (r *valkeyRepository) ReadSessionByToken(ctx context.Context, token string) (*models.Session, error) {
session, err := models.Parse(token, r.cfg.JWTSecret)
if err != nil {
return nil, err
}
sessionRecord, err := r.ReadSessionByUserId(ctx, *session.UserId)
if err != nil {
return nil, err
}
if *session.Id != *sessionRecord.Id {
return nil, utils.ErrInternal
}
return session, err
}
func (r *valkeyRepository) ReadSessionByUserId(ctx context.Context, userId int32) (*models.Session, error) {
resp := r.db.Do(ctx, r.db.B().Get().Key(string(userId)).Build())
if err := resp.Error(); err != nil {
return nil, utils.ErrInternal
}
id, err := resp.ToString()
if err != nil {
return nil, utils.ErrInternal
}
return &models.Session{
Id: &id,
UserId: &userId,
}, err
}
func (r *valkeyRepository) UpdateSession(ctx context.Context, session *models.Session) error {
resp := r.db.Do(ctx, r.db.
B().Set().
Key(string(*session.UserId)).
Value(*session.Id).
Xx().
Exat(time.Now().Add(sessionLifetime)).
Build(),
)
if err := resp.Error(); err != nil {
return utils.ErrInternal
}
return nil
}
func (r *valkeyRepository) DeleteSessionByToken(ctx context.Context, token string) error {
session, err := models.Parse(token, r.cfg.JWTSecret)
if err != nil {
return err
}
err = r.DeleteSessionByUserId(ctx, *session.UserId)
if err != nil {
return err
}
return nil
}
func (r *valkeyRepository) DeleteSessionByUserId(ctx context.Context, userId int32) error {
resp := r.db.Do(ctx, r.db.
B().Del().
Key(string(userId)).
Build(),
)
if err := resp.Error(); err != nil {
return utils.ErrInternal
}
return nil
}

View file

@ -0,0 +1,64 @@
package repository
import (
"context"
"git.sch9.ru/new_gate/ms-auth/config"
"git.sch9.ru/new_gate/ms-auth/internal/sessions"
vk "git.sch9.ru/new_gate/ms-auth/pkg/external/valkey"
"github.com/stretchr/testify/require"
"github.com/testcontainers/testcontainers-go/modules/valkey"
"go.uber.org/zap"
"log"
"testing"
)
func SetupValkey() (sessions.ValkeyRepository, func()) {
ctx := context.Background()
valkeyContainer, err := valkey.Run(ctx,
"docker.io/valkey/valkey:7.2.5",
//valkey.WithSnapshotting(10, 1),
//valkey.WithLogLevel(valkey.LogLevelVerbose),
//valkey.WithConfigFile(filepath.Join("testdata", "valkey7.conf")),
)
if err != nil {
panic(err)
}
dsn, err := valkeyContainer.ConnectionString(ctx)
if err != nil {
panic(err)
}
client, err := vk.NewValkeyClient(dsn)
if err != nil {
panic(err)
}
cfg := config.Config{
JWTSecret: "secret",
}
sessionRepo := NewValkeyRepository(client, cfg, zap.NewNop())
closeVC := func() {
if err := valkeyContainer.Stop(ctx, nil); err != nil {
log.Printf("failed to terminate container: %s", err)
}
}
return sessionRepo, closeVC
}
func TestValkeyRepository_CreateSession(t *testing.T) {
t.Parallel()
sessionRepo, closeVC := SetupValkey()
defer closeVC()
t.Run("valid session creation", func(t *testing.T) {
err := sessionRepo.CreateSession(context.Background(), 1)
require.NoError(t, err)
})
}

View file

@ -0,0 +1,10 @@
package sessions
import "context"
type UseCase interface {
Create(ctx context.Context, userId int32) (*string, error)
Read(ctx context.Context, token string) (*int32, error)
Update(ctx context.Context, token string) error
Delete(ctx context.Context, token string) error
}

View file

@ -0,0 +1,71 @@
package usecase
import (
"context"
"git.sch9.ru/new_gate/ms-auth/config"
"git.sch9.ru/new_gate/ms-auth/internal/sessions"
)
type useCase struct {
sessionRepo sessions.ValkeyRepository
cfg config.Config
}
func NewUseCase(sessionRepo sessions.ValkeyRepository, cfg config.Config) *useCase {
return &useCase{
sessionRepo: sessionRepo,
cfg: cfg,
}
}
func (s *useCase) Create(ctx context.Context, userId int32) (*string, error) {
var (
err error
)
s.sessionRepo.CreateSession(ctx, userId) // FIXME
session, err := s.sessionRepo.ReadSessionByUserId(ctx, userId)
if err != nil {
return nil, err
}
token, err := session.Token(s.cfg.JWTSecret)
if err != nil {
return nil, err
}
return &token, nil
}
func (s *useCase) Read(ctx context.Context, token string) (*int32, error) {
session, err := s.sessionRepo.ReadSessionByToken(ctx, token)
if err != nil {
return nil, err
}
return session.UserId, nil
}
func (s *useCase) Update(ctx context.Context, token string) error {
session, err := s.sessionRepo.ReadSessionByToken(ctx, token)
if err != nil {
return err
}
err = s.sessionRepo.UpdateSession(ctx, session)
if err != nil {
return err
}
return nil
}
func (s *useCase) Delete(ctx context.Context, token string) error {
session, err := s.sessionRepo.ReadSessionByToken(ctx, token)
if err != nil {
return err
}
err = s.sessionRepo.DeleteSessionByUserId(ctx, *session.UserId)
if err != nil {
return err
}
return nil
}

View file

@ -0,0 +1,15 @@
package sessions
import (
"context"
"git.sch9.ru/new_gate/ms-auth/internal/models"
)
type ValkeyRepository interface {
CreateSession(ctx context.Context, userId int32) error
ReadSessionByToken(ctx context.Context, token string) (*models.Session, error)
ReadSessionByUserId(ctx context.Context, userId int32) (*models.Session, error)
UpdateSession(ctx context.Context, session *models.Session) error
DeleteSessionByToken(ctx context.Context, token string) error
DeleteSessionByUserId(ctx context.Context, userId int32) error
}

View file

@ -1,125 +0,0 @@
package storage
import (
"context"
"git.sch9.ru/new_gate/ms-auth/internal/lib"
"git.sch9.ru/new_gate/ms-auth/internal/models"
"github.com/valkey-io/valkey-go"
"go.uber.org/zap"
"time"
)
type SessionStorage struct {
db valkey.Client
cfg lib.Config
logger *zap.Logger
}
func NewSessionStorage(db valkey.Client, cfg lib.Config, logger *zap.Logger) *SessionStorage {
return &SessionStorage{
db: db,
cfg: cfg,
logger: logger,
}
}
const sessionLifetime = time.Minute * 40
func (storage *SessionStorage) CreateSession(ctx context.Context, userId int32) error {
session := models.NewSession(userId)
resp := storage.db.Do(ctx, storage.db.
B().Set().
Key(string(*session.UserId)).
Value(*session.Id).
Nx().
Exat(time.Now().Add(sessionLifetime)).
Build(),
)
if err := resp.Error(); err != nil {
return lib.ErrInternal
}
return nil
}
func (storage *SessionStorage) ReadSessionByToken(ctx context.Context, token string) (*models.Session, error) {
session, err := models.Parse(token, storage.cfg.JWTSecret)
if err != nil {
return nil, err
}
sessionRecord, err := storage.ReadSessionByUserId(ctx, *session.UserId)
if err != nil {
return nil, err
}
if *session.Id != *sessionRecord.Id {
return nil, lib.ErrInternal
}
return session, err
}
func (storage *SessionStorage) ReadSessionByUserId(ctx context.Context, userId int32) (*models.Session, error) {
resp := storage.db.Do(ctx, storage.db.B().Get().Key(string(userId)).Build())
if err := resp.Error(); err != nil {
return nil, lib.ErrInternal
}
id, err := resp.ToString()
if err != nil {
return nil, lib.ErrInternal
}
return &models.Session{
Id: &id,
UserId: &userId,
}, err
}
func (storage *SessionStorage) UpdateSession(ctx context.Context, session *models.Session) error {
resp := storage.db.Do(ctx, storage.db.
B().Set().
Key(string(*session.UserId)).
Value(*session.Id).
Xx().
Exat(time.Now().Add(sessionLifetime)).
Build(),
)
if err := resp.Error(); err != nil {
return lib.ErrInternal
}
return nil
}
func (storage *SessionStorage) DeleteSessionByToken(ctx context.Context, token string) error {
session, err := models.Parse(token, storage.cfg.JWTSecret)
if err != nil {
return err
}
err = storage.DeleteSessionByUserId(ctx, *session.UserId)
if err != nil {
return err
}
return nil
}
func (storage *SessionStorage) DeleteSessionByUserId(ctx context.Context, userId int32) error {
resp := storage.db.Do(ctx, storage.db.
B().Del().
Key(string(userId)).
Build(),
)
if err := resp.Error(); err != nil {
return lib.ErrInternal
}
return nil
}

View file

@ -1,36 +0,0 @@
package transport
import (
"context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type ReqWithToken interface {
GetToken() string
}
func (s *AuthServer) AuthInterceptor() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
reqWithToken, ok := req.(ReqWithToken)
if !ok {
return handler(ctx, req)
}
token := reqWithToken.GetToken()
userId, err := s.sessionService.Read(ctx, token)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "")
}
user, err := s.userService.ReadUser(ctx, *userId)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "")
}
ctx = context.WithValue(ctx, "user", user)
return handler(ctx, req)
}
}

View file

@ -1,93 +0,0 @@
package transport
import (
"context"
"git.sch9.ru/new_gate/ms-auth/internal/models"
sessionv1 "git.sch9.ru/new_gate/ms-auth/pkg/go/gen/proto/session/v1"
userv1 "git.sch9.ru/new_gate/ms-auth/pkg/go/gen/proto/user/v1"
"go.uber.org/zap"
"google.golang.org/protobuf/types/known/timestamppb"
"net"
"time"
"google.golang.org/grpc"
)
type SessionService interface {
Create(ctx context.Context, handle, password string) (*string, error)
Read(ctx context.Context, token string) (*int32, error)
Update(ctx context.Context, token string) error
Delete(ctx context.Context, token string) error
}
type UserService interface {
CreateUser(ctx context.Context, user *models.User) (int32, error)
ReadUser(ctx context.Context, id int32) (*models.User, error)
UpdateUser(ctx context.Context, user *models.User) error
DeleteUser(ctx context.Context, id int32) error
}
type AuthServer struct {
sessionv1.UnimplementedSessionServiceServer
sessionService SessionService
userv1.UnimplementedUserServiceServer
userService UserService
GRPCServer *grpc.Server
logger *zap.Logger
ln net.Listener
}
func NewAuthServer(
sessionService SessionService,
userService UserService,
logger *zap.Logger,
ln net.Listener,
) *AuthServer {
server := &AuthServer{
sessionService: sessionService,
userService: userService,
logger: logger,
ln: ln,
}
grpcServer := grpc.NewServer(
grpc.UnaryInterceptor(server.AuthInterceptor()),
)
server.GRPCServer = grpcServer
sessionv1.RegisterSessionServiceServer(server.GRPCServer, server)
return server
}
func AsTimeP(t *timestamppb.Timestamp) *time.Time {
if t == nil {
return nil
}
tt := t.AsTime()
return &tt
}
func AsMRoleP(v userv1.Role) *models.Role {
vv := models.Role(v.Number())
return &vv
}
func AsTimestampP(t *time.Time) *timestamppb.Timestamp {
if t == nil {
return nil
}
return timestamppb.New(*t)
}
func AsRoleP(r *models.Role) *userv1.Role {
if r == nil {
return nil
}
rr := userv1.Role(*r)
return &rr
}

View file

@ -1,45 +0,0 @@
package transport
import (
"context"
sessionv1 "git.sch9.ru/new_gate/ms-auth/pkg/go/gen/proto/session/v1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
)
func (s *AuthServer) Create(ctx context.Context, req *sessionv1.CreateSessionRequest) (*sessionv1.CreateSessionResponse, error) {
token, err := s.sessionService.Create(ctx, req.GetHandle(), req.GetPassword())
if err != nil {
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
}
return &sessionv1.CreateSessionResponse{
Token: *token,
}, nil
}
func (s *AuthServer) Read(ctx context.Context, req *sessionv1.ReadSessionRequest) (*sessionv1.ReadSessionResponse, error) {
id, err := s.sessionService.Read(ctx, req.GetToken())
if err != nil {
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
}
return &sessionv1.ReadSessionResponse{
UserId: *id,
}, nil
}
func (s *AuthServer) Update(ctx context.Context, req *sessionv1.UpdateSessionRequest) (*emptypb.Empty, error) {
err := s.sessionService.Update(ctx, req.GetToken())
if err != nil {
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
}
return &emptypb.Empty{}, nil
}
func (s *AuthServer) Delete(ctx context.Context, req *sessionv1.DeleteSessionRequest) (*emptypb.Empty, error) {
err := s.sessionService.Delete(ctx, req.GetToken())
if err != nil {
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
}
return &emptypb.Empty{}, nil
}

View file

@ -1,88 +0,0 @@
package transport
import (
"context"
"git.sch9.ru/new_gate/ms-auth/internal/lib"
"git.sch9.ru/new_gate/ms-auth/internal/models"
userv1 "git.sch9.ru/new_gate/ms-auth/pkg/go/gen/proto/user/v1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
)
func (s *AuthServer) CreateUser(ctx context.Context, req *userv1.CreateUserRequest) (*userv1.CreateUserResponse, error) {
user := req.GetUser()
if user == nil {
return nil, status.Errorf(codes.Unknown, "") // FIXME
}
id, err := s.userService.CreateUser(
ctx,
&models.User{
Username: lib.AsStringP(user.GetUsername()),
Password: lib.AsStringP(user.GetPassword()),
Email: nil,
ExpiresAt: AsTimeP(user.ExpiresAt),
Role: AsMRoleP(user.GetRole()),
},
)
if err != nil {
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
}
return &userv1.CreateUserResponse{
Id: id,
}, nil
}
func (s *AuthServer) ReadUser(ctx context.Context, req *userv1.ReadUserRequest) (*userv1.ReadUserResponse, error) {
user, err := s.userService.ReadUser(
ctx,
req.GetId(),
)
if err != nil {
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
}
return &userv1.ReadUserResponse{
User: &userv1.ReadUserResponse_User{
Id: *user.Id,
Username: *user.Username,
ExpiresAt: AsTimestampP(user.ExpiresAt),
CreatedAt: AsTimestampP(user.CreatedAt),
Role: *AsRoleP(user.Role),
},
}, nil
}
func (s *AuthServer) UpdateUser(ctx context.Context, req *userv1.UpdateUserRequest) (*emptypb.Empty, error) {
user := req.GetUser()
if user == nil {
return nil, status.Errorf(codes.Unknown, "") // FIXME
}
err := s.userService.UpdateUser(
ctx,
&models.User{
Id: lib.AsInt32P(user.GetId()),
Username: lib.AsStringP(user.GetUsername()),
Password: lib.AsStringP(user.GetPassword()),
Email: nil,
ExpiresAt: AsTimeP(user.ExpiresAt),
Role: AsMRoleP(user.GetRole()),
},
)
if err != nil {
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
}
return &emptypb.Empty{}, nil
}
func (s *AuthServer) DeleteUser(ctx context.Context, req *userv1.DeleteUserRequest) (*emptypb.Empty, error) {
err := s.userService.DeleteUser(
ctx,
req.GetId(),
)
if err != nil {
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
}
return &emptypb.Empty{}, nil
}

View file

@ -0,0 +1,14 @@
package users
import (
"context"
userv1 "git.sch9.ru/new_gate/ms-auth/pkg/go/gen/proto/user/v1"
"google.golang.org/protobuf/types/known/emptypb"
)
type UserHandlers interface {
CreateUser(ctx context.Context, req *userv1.CreateUserRequest) (*userv1.CreateUserResponse, error)
ReadUser(ctx context.Context, req *userv1.ReadUserRequest) (*userv1.ReadUserResponse, error)
UpdateUser(ctx context.Context, req *userv1.UpdateUserRequest) (*emptypb.Empty, error)
DeleteUser(ctx context.Context, req *userv1.DeleteUserRequest) (*emptypb.Empty, error)
}

View file

@ -0,0 +1,116 @@
package grpc
import (
"context"
"git.sch9.ru/new_gate/ms-auth/internal/models"
"git.sch9.ru/new_gate/ms-auth/internal/users"
userv1 "git.sch9.ru/new_gate/ms-auth/pkg/go/gen/proto/user/v1"
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
)
type userHandlers struct {
userv1.UnimplementedUserServiceServer
userUC users.UseCase
}
func NewUserHandlers(gserver *grpc.Server, userUC users.UseCase) {
handlers := &userHandlers{
userUC: userUC,
}
userv1.RegisterUserServiceServer(gserver, handlers)
}
func (h *userHandlers) CreateUser(ctx context.Context, req *userv1.CreateUserRequest) (*userv1.CreateUserResponse, error) {
user := req.GetUser()
if user == nil {
return nil, status.Errorf(codes.Unknown, "") // FIXME
}
id, err := h.userUC.CreateUser(
ctx,
&models.User{
Username: utils.AsStringP(user.GetUsername()),
Password: utils.AsStringP(user.GetPassword()),
Email: nil,
ExpiresAt: utils.TimeP(user.ExpiresAt),
Role: AsMRoleP(user.GetRole()),
},
)
if err != nil {
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
}
return &userv1.CreateUserResponse{
Id: id,
}, nil
}
func (h *userHandlers) ReadUser(ctx context.Context, req *userv1.ReadUserRequest) (*userv1.ReadUserResponse, error) {
user, err := h.userUC.ReadUser(
ctx,
req.GetId(),
)
if err != nil {
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
}
return &userv1.ReadUserResponse{
User: &userv1.ReadUserResponse_User{
Id: *user.Id,
Username: *user.Username,
ExpiresAt: utils.TimestampP(user.ExpiresAt),
CreatedAt: utils.TimestampP(user.CreatedAt),
Role: *AsRoleP(user.Role),
},
}, nil
}
func (h *userHandlers) UpdateUser(ctx context.Context, req *userv1.UpdateUserRequest) (*emptypb.Empty, error) {
user := req.GetUser()
if user == nil {
return nil, status.Errorf(codes.Unknown, "") // FIXME
}
err := h.userUC.UpdateUser(
ctx,
&models.User{
Id: utils.AsInt32P(user.GetId()),
Username: utils.AsStringP(user.GetUsername()),
Password: utils.AsStringP(user.GetPassword()),
Email: nil,
ExpiresAt: utils.TimeP(user.ExpiresAt),
Role: AsMRoleP(user.GetRole()),
},
)
if err != nil {
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
}
return &emptypb.Empty{}, nil
}
func (h *userHandlers) DeleteUser(ctx context.Context, req *userv1.DeleteUserRequest) (*emptypb.Empty, error) {
err := h.userUC.DeleteUser(
ctx,
req.GetId(),
)
if err != nil {
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
}
return &emptypb.Empty{}, nil
}
func AsMRoleP(v userv1.Role) *models.Role {
vv := models.Role(v.Number())
return &vv
}
func AsRoleP(r *models.Role) *userv1.Role {
if r == nil {
return nil
}
rr := userv1.Role(*r)
return &rr
}

View file

@ -0,0 +1,30 @@
package grpc
import (
"context"
"git.sch9.ru/new_gate/ms-auth/internal/sessions"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
func TokenInterceptor(s sessions.UseCase) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return handler(ctx, req)
}
auth := md.Get("authorization")
if len(auth) == 0 {
return handler(ctx, req)
}
userId, err := s.Read(ctx, auth[0])
if err != nil {
return handler(ctx, req)
}
ctx = context.WithValue(ctx, "userId", *userId)
return handler(ctx, req)
}
}

View file

@ -0,0 +1,15 @@
package users
import (
"context"
"git.sch9.ru/new_gate/ms-auth/internal/models"
)
type PgRepository interface {
CreateUser(ctx context.Context, user *models.User) (int32, error)
ReadUserByEmail(ctx context.Context, email string) (*models.User, error)
ReadUserByUsername(ctx context.Context, username string) (*models.User, error)
ReadUserById(ctx context.Context, id int32) (*models.User, error)
UpdateUser(ctx context.Context, user *models.User) error
DeleteUser(ctx context.Context, id int32) error
}

View file

@ -1,10 +1,10 @@
package storage
package repository
import (
"context"
"errors"
"git.sch9.ru/new_gate/ms-auth/internal/lib"
"git.sch9.ru/new_gate/ms-auth/internal/models"
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
"github.com/jackc/pgerrcode"
"github.com/jackc/pgx/v5/pgconn"
"github.com/jmoiron/sqlx"
@ -12,13 +12,13 @@ import (
"time"
)
type UserStorage struct {
type UsersRepository struct {
db *sqlx.DB
logger *zap.Logger
}
func NewUserStorage(db *sqlx.DB, logger *zap.Logger) *UserStorage {
return &UserStorage{
func NewUserRepository(db *sqlx.DB, logger *zap.Logger) *UsersRepository {
return &UsersRepository{
db: db,
logger: logger,
}
@ -26,7 +26,14 @@ func NewUserStorage(db *sqlx.DB, logger *zap.Logger) *UserStorage {
const year = time.Hour * 24 * 365
func (storage *UserStorage) CreateUser(ctx context.Context, user *models.User) (int32, error) {
const createUser = `
INSERT INTO users
(username, hashed_pwd, email, expires_at, role)
VALUES (?, ?, ?, ?, ?)
RETURNING id
`
func (storage *UsersRepository) CreateUser(ctx context.Context, user *models.User) (int32, error) {
if err := user.ValidUsername(); err != nil {
return 0, err
}
@ -39,16 +46,13 @@ func (storage *UserStorage) CreateUser(ctx context.Context, user *models.User) (
if err := user.ValidRole(); err != nil {
return 0, err
}
if err := user.HashPassword(); err != nil {
if err := user.HashPassword(); err != nil { // FIXME: get rid of mutation
return 0, err
}
query := storage.db.Rebind(`
INSERT INTO users
(username, hashed_pwd, email, expires_at, role)
VALUES (?, ?, ?, ?, ?)
RETURNING id
`)
user.ExpiresAt = utils.AsTimeP(time.Now().Add(100 * year)) // FIXME: get rid of mutation
query := storage.db.Rebind(createUser)
rows, err := storage.db.QueryxContext(
ctx,
@ -56,7 +60,7 @@ RETURNING id
user.Username,
user.Password,
user.Email,
time.Now().Add(100*year),
user.ExpiresAt,
user.Role,
)
if err != nil {
@ -65,7 +69,8 @@ RETURNING id
defer rows.Close()
var id int32
err = rows.StructScan(&id)
rows.Next()
err = rows.Scan(&id)
if err != nil {
return 0, storage.handlePgErr(err)
}
@ -73,9 +78,11 @@ RETURNING id
return id, nil
}
func (storage *UserStorage) ReadUserByEmail(ctx context.Context, email string) (*models.User, error) {
const readUserByEmail = "SELECT * from users WHERE email=? LIMIT 1"
func (storage *UsersRepository) ReadUserByEmail(ctx context.Context, email string) (*models.User, error) {
var user models.User
query := storage.db.Rebind("SELECT * from users WHERE email=? LIMIT 1")
query := storage.db.Rebind(readUserByEmail)
err := storage.db.GetContext(ctx, &user, query, email)
if err != nil {
return nil, storage.handlePgErr(err)
@ -83,9 +90,11 @@ func (storage *UserStorage) ReadUserByEmail(ctx context.Context, email string) (
return &user, nil
}
func (storage *UserStorage) ReadUserByUsername(ctx context.Context, username string) (*models.User, error) {
const readUserByUsername = "SELECT * from users WHERE username=? LIMIT 1"
func (storage *UsersRepository) ReadUserByUsername(ctx context.Context, username string) (*models.User, error) {
var user models.User
query := storage.db.Rebind("SELECT * from users WHERE username=? LIMIT 1")
query := storage.db.Rebind(readUserByUsername)
err := storage.db.GetContext(ctx, &user, query, username)
if err != nil {
return nil, storage.handlePgErr(err)
@ -93,9 +102,11 @@ func (storage *UserStorage) ReadUserByUsername(ctx context.Context, username str
return &user, nil
}
func (storage *UserStorage) ReadUserById(ctx context.Context, id int32) (*models.User, error) {
const readUserById = "SELECT * from users WHERE id=? LIMIT 1"
func (storage *UsersRepository) ReadUserById(ctx context.Context, id int32) (*models.User, error) {
var user models.User
query := storage.db.Rebind("SELECT * from users WHERE id=? LIMIT 1")
query := storage.db.Rebind(readUserById)
err := storage.db.GetContext(ctx, &user, query, id)
if err != nil {
return nil, storage.handlePgErr(err)
@ -103,7 +114,17 @@ func (storage *UserStorage) ReadUserById(ctx context.Context, id int32) (*models
return &user, nil
}
func (storage *UserStorage) UpdateUser(ctx context.Context, user *models.User) error {
const updateUser = `
UPDATE users
SET username = COALESCE(?, username),
hashed_pwd = COALESCE(?, hashed_pwd),
email = COALESCE(?, email),
expires_at = COALESCE(?, expires_at),
role = COALESCE(?, role)
WHERE id = ?
`
func (storage *UsersRepository) UpdateUser(ctx context.Context, user *models.User) error {
var err error
if user.Username != nil {
if err = user.ValidUsername(); err != nil {
@ -119,7 +140,7 @@ func (storage *UserStorage) UpdateUser(ctx context.Context, user *models.User) e
}
}
if user.Email != nil {
if err = lib.ValidEmail(*user.Email); err != nil {
if err = utils.ValidEmail(*user.Email); err != nil {
return err
}
}
@ -129,14 +150,7 @@ func (storage *UserStorage) UpdateUser(ctx context.Context, user *models.User) e
}
}
query := storage.db.Rebind(`
UPDATE users
SET username = COALESCE(?, username),
hashed_pwd = COALESCE(?, hashed_pwd),
email = COALESCE(?, email),
expires_at = COALESCE(?, expires_at),
role = COALESCE(?, role)
WHERE id = ?`)
query := storage.db.Rebind(updateUser)
_, err = storage.db.ExecContext(
ctx,
@ -154,8 +168,11 @@ WHERE id = ?`)
}
return nil
}
func (storage *UserStorage) DeleteUser(ctx context.Context, id int32) error {
query := storage.db.Rebind("UPDATE users SET expired_at=now() WHERE id = ?")
const deleteUser = "UPDATE users SET expired_at=now() WHERE id = ?"
func (storage *UsersRepository) DeleteUser(ctx context.Context, id int32) error {
query := storage.db.Rebind(deleteUser)
_, err := storage.db.ExecContext(ctx, query, id)
if err != nil {
return storage.handlePgErr(err)
@ -164,15 +181,15 @@ func (storage *UserStorage) DeleteUser(ctx context.Context, id int32) error {
return nil
}
func (storage *UserStorage) handlePgErr(err error) error {
func (storage *UsersRepository) handlePgErr(err error) error {
var pgErr *pgconn.PgError
if !errors.As(err, &pgErr) {
storage.logger.DPanic("unexpected error from postgres", zap.String("err", err.Error()))
return lib.ErrUnexpected
return utils.ErrUnexpected
}
if pgerrcode.IsIntegrityConstraintViolation(pgErr.Code) {
return errors.New("unique key violation") // FIXME
}
storage.logger.DPanic("unexpected internal error from postgres", zap.String("err", err.Error()))
return lib.ErrInternal
return utils.ErrInternal
}

View file

@ -0,0 +1,146 @@
package repository
import (
"context"
"database/sql/driver"
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
"github.com/DATA-DOG/go-sqlmock"
"github.com/jmoiron/sqlx"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"testing"
"time"
"git.sch9.ru/new_gate/ms-auth/internal/models"
)
func TestUsersRepository_CreateUser(t *testing.T) {
t.Parallel()
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
require.NoError(t, err)
defer db.Close()
sqlxDB := sqlx.NewDb(db, "sqlmock")
defer sqlxDB.Close()
userRepo := NewUserRepository(sqlxDB, zap.NewNop())
t.Run("valid user creation", func(t *testing.T) {
rows := sqlmock.NewRows([]string{"id"}).AddRow(1)
user := &models.User{
Username: utils.AsStringP("testuser"),
Password: utils.AsStringP("testpassword"),
Email: utils.AsStringP("test@example.com"),
Role: AsRoleP(models.RoleAdmin),
}
mock.ExpectQuery(sqlxDB.Rebind(createUser)).WithArgs(
user.Username,
AnyString{},
user.Email,
AnyTime{},
user.Role,
).WillReturnRows(rows)
_, err = userRepo.CreateUser(context.Background(), user)
require.NoError(t, err)
})
// TODO: add more tests
// invalid username
// invalid password
// invalid email
// invalid role
// password hashing error
// database query error
// database scan error
// etc
}
func TestUsersRepository_UpdateUser(t *testing.T) {
t.Parallel()
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
require.NoError(t, err)
defer db.Close()
sqlxDB := sqlx.NewDb(db, "sqlmock")
defer sqlxDB.Close()
userRepo := NewUserRepository(sqlxDB, zap.NewNop())
t.Run("valid user update", func(t *testing.T) {
user := &models.User{
Id: utils.AsInt32P(1),
Username: utils.AsStringP("testuser"),
Password: utils.AsStringP("testpassword"),
Email: utils.AsStringP("test@example.com"),
ExpiresAt: utils.AsTimeP(time.Now().Add(1 * time.Hour)),
Role: AsRoleP(models.RoleAdmin),
}
require.NoError(t, err)
mock.ExpectExec(sqlxDB.Rebind(updateUser)).
WithArgs(
*user.Username,
AnyString{},
*user.Email,
*user.ExpiresAt,
*user.Role,
*user.Id,
).WillReturnResult(sqlmock.NewResult(1, 1))
err = userRepo.UpdateUser(context.Background(), user)
require.NoError(t, err)
})
// TODO: add more tests
}
func TestUsersRepository_DeleteUser(t *testing.T) {
t.Parallel()
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
require.NoError(t, err)
defer db.Close()
sqlxDB := sqlx.NewDb(db, "sqlmock")
defer sqlxDB.Close()
userRepo := NewUserRepository(sqlxDB, zap.NewNop())
t.Run("valid user deletion", func(t *testing.T) {
user := &models.User{
Id: utils.AsInt32P(1),
}
mock.ExpectExec(sqlxDB.Rebind(deleteUser)).WithArgs(*user.Id).WillReturnResult(sqlmock.NewResult(1, 1))
err = userRepo.DeleteUser(context.Background(), *user.Id)
require.NoError(t, err)
})
// TODO: add more tests
}
func AsRoleP(r models.Role) *models.Role {
return &r
}
type AnyTime struct{}
// Match satisfies sqlmock.Argument interface
func (a AnyTime) Match(v driver.Value) bool {
_, ok := v.(time.Time)
return ok
}
type AnyString struct{}
func (a AnyString) Match(v driver.Value) bool {
_, ok := v.(string)
return ok
}

16
internal/users/usecase.go Normal file
View file

@ -0,0 +1,16 @@
package users
import (
"context"
"git.sch9.ru/new_gate/ms-auth/internal/models"
)
type UseCase interface {
CreateUser(ctx context.Context, user *models.User) (int32, error)
ReadUserBySessionToken(ctx context.Context, token string) (*models.User, error)
ReadUser(ctx context.Context, id int32) (*models.User, error)
ReadUserByEmail(ctx context.Context, email string) (*models.User, error)
ReadUserByUsername(ctx context.Context, username string) (*models.User, error)
UpdateUser(ctx context.Context, modifiedUser *models.User) error
DeleteUser(ctx context.Context, id int32) error
}

View file

@ -0,0 +1,149 @@
package usecase
import (
"context"
"git.sch9.ru/new_gate/ms-auth/config"
"git.sch9.ru/new_gate/ms-auth/internal/models"
"git.sch9.ru/new_gate/ms-auth/internal/sessions"
"git.sch9.ru/new_gate/ms-auth/internal/users"
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
)
type useCase struct {
userRepo users.PgRepository
sessionProvider sessions.ValkeyRepository
cfg config.Config
}
func NewUseCase(
userRepo users.PgRepository,
sessionRepo sessions.ValkeyRepository,
cfg config.Config,
) *useCase {
return &useCase{
userRepo: userRepo,
sessionProvider: sessionRepo,
cfg: cfg,
}
}
func (u *useCase) CreateUser(ctx context.Context, user *models.User) (int32, error) {
meId, ok := ctx.Value("userId").(*int32)
if !ok {
return 0, utils.ErrNoPermission
}
me, err := u.ReadUser(ctx, *meId)
if err != nil {
return 0, err
}
switch *me.Role {
case models.RoleAdmin:
break
case models.RoleModerator:
if !user.Role.AtMost(models.RoleParticipant) {
return 0, utils.ErrNoPermission
}
default:
return 0, utils.ErrNoPermission
}
return u.userRepo.CreateUser(ctx, user)
}
func (u *useCase) ReadUserBySessionToken(ctx context.Context, token string) (*models.User, error) {
session, err := u.sessionProvider.ReadSessionByToken(ctx, token)
if err != nil {
return nil, err
}
return u.userRepo.ReadUserById(ctx, *session.UserId)
}
func (u *useCase) ReadUser(ctx context.Context, id int32) (*models.User, error) {
return u.userRepo.ReadUserById(ctx, id)
}
func (u *useCase) ReadUserByEmail(ctx context.Context, email string) (*models.User, error) {
return u.userRepo.ReadUserByEmail(ctx, email)
}
func (u *useCase) ReadUserByUsername(ctx context.Context, username string) (*models.User, error) {
return u.userRepo.ReadUserByUsername(ctx, username)
}
func (u *useCase) UpdateUser(ctx context.Context, modifiedUser *models.User) error {
meId, ok := ctx.Value("userId").(*int32)
if !ok {
return utils.ErrNoPermission
}
me, err := u.ReadUser(ctx, *meId)
if err != nil {
return err
}
user, err := u.userRepo.ReadUserById(ctx, *modifiedUser.Id)
if err != nil {
return err
}
hasAccess := func() bool {
if me.Role.IsAdmin() {
return true
}
if me.Role.IsModerator() {
if !user.Role.AtMost(models.RoleParticipant) {
return false
}
return true
}
if me.Role.IsParticipant() {
if me.Id != user.Id {
return false
}
if modifiedUser.Username != nil {
return false
}
if modifiedUser.Email != nil {
return false
}
if modifiedUser.ExpiresAt != nil {
return false
}
if modifiedUser.Role != nil {
return false
}
return true
}
if me.Role.IsSpectator() {
return false
}
return false
}()
if !hasAccess {
return utils.ErrNoPermission
}
return u.userRepo.UpdateUser(ctx, user)
}
func (u *useCase) DeleteUser(ctx context.Context, id int32) error {
userId, ok := ctx.Value("userId").(*int32)
if !ok {
return utils.ErrNoPermission
}
me, err := u.ReadUser(ctx, *userId)
if err != nil {
return err
}
if *me.Id == id || !me.Role.IsAdmin() {
return utils.ErrNoPermission
}
return u.userRepo.DeleteUser(ctx, id)
}

47
main.go
View file

@ -2,15 +2,20 @@ package main
import (
"fmt"
"git.sch9.ru/new_gate/ms-auth/internal/lib"
"git.sch9.ru/new_gate/ms-auth/internal/services"
"git.sch9.ru/new_gate/ms-auth/internal/storage"
"git.sch9.ru/new_gate/ms-auth/internal/transport"
"git.sch9.ru/new_gate/ms-auth/config"
sessionsDelivery "git.sch9.ru/new_gate/ms-auth/internal/sessions/delivery/grpc"
sessionsRepository "git.sch9.ru/new_gate/ms-auth/internal/sessions/repository"
sessionsUseCase "git.sch9.ru/new_gate/ms-auth/internal/sessions/usecase"
usersDelivery "git.sch9.ru/new_gate/ms-auth/internal/users/delivery/grpc"
usersRepository "git.sch9.ru/new_gate/ms-auth/internal/users/repository"
usersUseCase "git.sch9.ru/new_gate/ms-auth/internal/users/usecase"
"git.sch9.ru/new_gate/ms-auth/pkg/external/postgres"
"git.sch9.ru/new_gate/ms-auth/pkg/external/valkey"
"github.com/ilyakaznacheev/cleanenv"
_ "github.com/jackc/pgx/v5/stdlib"
"github.com/jmoiron/sqlx"
"github.com/valkey-io/valkey-go"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
"net"
"os"
"os/signal"
@ -18,7 +23,7 @@ import (
)
func main() {
var cfg lib.Config
var cfg config.Config
err := cleanenv.ReadConfig(".env", &cfg)
if err != nil {
panic(fmt.Sprintf("error reading config: %s", err.Error()))
@ -33,39 +38,33 @@ func main() {
panic(fmt.Sprintf(`error reading config: env expected "prod" or "dev", got "%s"`, cfg.Env))
}
db, err := sqlx.Connect("pgx", cfg.PostgresDSN)
db, err := postgres.NewPostgresDB(cfg.PostgresDSN)
if err != nil {
panic(err)
}
defer db.Close()
userStorage := storage.NewUserStorage(db, logger)
vk, err := valkey.NewValkeyClient(cfg.RedisDSN)
opts, err := valkey.ParseURL(cfg.RedisDSN)
if err != nil {
panic(err)
}
userRepo := usersRepository.NewUserRepository(db, logger)
userUC := usersUseCase.NewUseCase(userRepo, nil, cfg)
vk, err := valkey.NewClient(opts)
if err != nil {
panic(err)
}
defer vk.Close()
sessionRepo := sessionsRepository.NewValkeyRepository(vk, cfg, logger)
sessionUC := sessionsUseCase.NewUseCase(sessionRepo, cfg)
sessionStorage := storage.NewSessionStorage(vk, cfg, logger)
gserver := grpc.NewServer(grpc.UnaryInterceptor(usersDelivery.TokenInterceptor(sessionUC)))
sessionService := services.NewSessionService(sessionStorage, userStorage, cfg)
userService := services.NewUserService(userStorage, sessionStorage, cfg)
usersDelivery.NewUserHandlers(gserver, userUC)
sessionsDelivery.NewSessionHandlers(gserver, sessionUC, userUC)
reflection.Register(gserver)
ln, err := net.Listen("tcp", cfg.Address)
if err != nil {
panic(err)
}
server := transport.NewAuthServer(sessionService, userService, logger, ln)
go func() {
if err = server.GRPCServer.Serve(ln); err != nil {
if err = gserver.Serve(ln); err != nil {
panic(err)
}
}()

30
pkg/external/postgres/postgres.go vendored Normal file
View file

@ -0,0 +1,30 @@
package postgres
import (
"github.com/jmoiron/sqlx"
"time"
)
const (
maxOpenConns = 60
connMaxLifetime = 120
maxIdleConns = 30
connMaxIdleTime = 20
)
func NewPostgresDB(dsn string) (*sqlx.DB, error) {
db, err := sqlx.Open("pgx", dsn)
if err != nil {
return nil, err
}
db.SetMaxOpenConns(maxOpenConns)
db.SetConnMaxLifetime(connMaxLifetime * time.Second)
db.SetMaxIdleConns(maxIdleConns)
db.SetConnMaxIdleTime(connMaxIdleTime * time.Second)
if err = db.Ping(); err != nil {
return nil, err
}
return db, nil
}

12
pkg/external/valkey/valkey.go vendored Normal file
View file

@ -0,0 +1,12 @@
package valkey
import "github.com/valkey-io/valkey-go"
func NewValkeyClient(dsn string) (valkey.Client, error) {
opts, err := valkey.ParseURL(dsn)
if err != nil {
return nil, err
}
return valkey.NewClient(opts)
}

33
pkg/utils/converter.go Normal file
View file

@ -0,0 +1,33 @@
package utils
import (
"google.golang.org/protobuf/types/known/timestamppb"
"time"
)
func TimeP(t *timestamppb.Timestamp) *time.Time {
if t == nil {
return nil
}
tt := t.AsTime()
return &tt
}
func TimestampP(t *time.Time) *timestamppb.Timestamp {
if t == nil {
return nil
}
return timestamppb.New(*t)
}
func AsTimeP(t time.Time) *time.Time {
return &t
}
func AsInt32P(v int32) *int32 {
return &v
}
func AsStringP(str string) *string {
return &str
}

View file

@ -1,4 +1,4 @@
package lib
package utils
import (
"errors"

View file

@ -1,21 +1,6 @@
package lib
package utils
import (
"net/mail"
"time"
)
func AsTimeP(t time.Time) *time.Time {
return &t
}
func AsInt32P(v int32) *int32 {
return &v
}
func AsStringP(str string) *string {
return &str
}
import "net/mail"
func ValidPassword(str string) error {
if len(str) < 5 {