Initial commit
This commit is contained in:
commit
2fa110e760
28 changed files with 2346 additions and 0 deletions
9
Dockerfile
Normal file
9
Dockerfile
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
FROM golang:latest
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN go build ./main.go
|
||||||
|
|
||||||
|
CMD ["./main"]
|
4
Makefile
Normal file
4
Makefile
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
all:
|
||||||
|
go build
|
||||||
|
bug generate proto
|
||||||
|
|
12
buf.gen.yaml
Normal file
12
buf.gen.yaml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
version: v1
|
||||||
|
managed:
|
||||||
|
enabled: true
|
||||||
|
go_package_prefix:
|
||||||
|
default: ms-auth/pkg/go/gen
|
||||||
|
plugins:
|
||||||
|
- name: go
|
||||||
|
out: pkg/go/gen
|
||||||
|
opt: paths=source_relative
|
||||||
|
- name: go-grpc
|
||||||
|
out: pkg/go/gen
|
||||||
|
opt: paths=source_relative
|
7
buf.yaml
Normal file
7
buf.yaml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
version: v1
|
||||||
|
breaking:
|
||||||
|
use:
|
||||||
|
- FILE
|
||||||
|
lint:
|
||||||
|
use:
|
||||||
|
- DEFAULT
|
75
docker-compose.yaml
Normal file
75
docker-compose.yaml
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
networks:
|
||||||
|
local:
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db:
|
||||||
|
|
||||||
|
services:
|
||||||
|
auth-service:
|
||||||
|
build:
|
||||||
|
dockerfile: ./Dockerfile
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
ports:
|
||||||
|
- "8090:8090"
|
||||||
|
depends_on:
|
||||||
|
# postgres:
|
||||||
|
# condition: service_healthy
|
||||||
|
# valkey:
|
||||||
|
# condition: service_healthy
|
||||||
|
migrate:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
networks:
|
||||||
|
- local
|
||||||
|
|
||||||
|
postgres:
|
||||||
|
image: postgres:14.1-alpine
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: supersecretpassword
|
||||||
|
networks:
|
||||||
|
- local
|
||||||
|
ports:
|
||||||
|
- '5432:5432'
|
||||||
|
volumes:
|
||||||
|
- db:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: pg_isready -U postgres -d postgres
|
||||||
|
interval: 10s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
migrate:
|
||||||
|
image: ghcr.io/kukymbr/goose-docker:latest
|
||||||
|
networks:
|
||||||
|
- local
|
||||||
|
volumes:
|
||||||
|
- ./migrations:/migrations
|
||||||
|
environment:
|
||||||
|
GOOSE_DRIVER: "postgres"
|
||||||
|
GOOSE_DBSTRING: "host=postgres user=postgres password=supersecretpassword dbname=postgres port=5432 sslmode=disable"
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
valkey:
|
||||||
|
container_name: valkey
|
||||||
|
hostname: valkey
|
||||||
|
image: valkey/valkey:latest
|
||||||
|
build: .
|
||||||
|
volumes:
|
||||||
|
- ./conf/valkey.conf:/usr/local/etc/valkey/valkey.conf
|
||||||
|
- ./data:/data
|
||||||
|
command: ["valkey-server", "/usr/local/etc/valkey/valkey.conf"]
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "valkey-cli ping | grep PONG"]
|
||||||
|
interval: 1s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 5
|
||||||
|
ports:
|
||||||
|
- 6379:6379
|
||||||
|
networks:
|
||||||
|
- local
|
37
go.mod
Normal file
37
go.mod
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
module ms-auth
|
||||||
|
|
||||||
|
go 1.19
|
||||||
|
|
||||||
|
require (
|
||||||
|
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
|
||||||
|
google.golang.org/protobuf v1.34.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 // 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/rogpeppe/go-internal v1.12.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/sync v0.7.0 // indirect
|
||||||
|
golang.org/x/sys v0.21.0 // indirect
|
||||||
|
golang.org/x/text v0.16.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/BurntSushi/toml v1.2.1 // indirect
|
||||||
|
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||||
|
github.com/jackc/pgx/v5 v5.6.0
|
||||||
|
github.com/jmoiron/sqlx v1.4.0
|
||||||
|
github.com/joho/godotenv v1.5.1 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 // indirect
|
||||||
|
)
|
80
go.sum
Normal file
80
go.sum
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
|
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
|
||||||
|
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
|
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/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/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/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=
|
||||||
|
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
|
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
||||||
|
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
|
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/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/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/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
|
||||||
|
github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0=
|
||||||
|
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/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/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=
|
||||||
|
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/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/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
|
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/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=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
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=
|
||||||
|
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=
|
35
internal/app/app.go
Normal file
35
internal/app/app.go
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
"ms-auth/internal/lib"
|
||||||
|
|
||||||
|
_ "github.com/jackc/pgx/v5/stdlib"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server interface {
|
||||||
|
Start()
|
||||||
|
GracefullyStop()
|
||||||
|
}
|
||||||
|
|
||||||
|
type App struct {
|
||||||
|
server Server
|
||||||
|
cfg *lib.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewApp(cfg *lib.Config, server Server) *App {
|
||||||
|
return &App{
|
||||||
|
server: server,
|
||||||
|
cfg: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *App) Start() {
|
||||||
|
app.server.Start()
|
||||||
|
slog.Info("app started")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *App) GracefullyStop() {
|
||||||
|
app.server.GracefullyStop()
|
||||||
|
slog.Info("app stopped")
|
||||||
|
}
|
27
internal/lib/config.go
Normal file
27
internal/lib/config.go
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/ilyakaznacheev/cleanenv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Env string `env:"ENV" env-default:"prod"`
|
||||||
|
|
||||||
|
PostgresDSN string `env:"POSTGRES_DSN" required:"true"`
|
||||||
|
RedisDSN string `env:"REDIS_DSN" required:"true"`
|
||||||
|
|
||||||
|
Email string `env:"EMAIL" required:"true"`
|
||||||
|
Password string `env:"PASSWORD" required:"true"`
|
||||||
|
|
||||||
|
JWTSecret string `env:"JWT_SECRET" required:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustSetupConfig() *Config {
|
||||||
|
var cfg Config
|
||||||
|
err := cleanenv.ReadConfig(".env", &cfg)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("error reading config: %s", err.Error()))
|
||||||
|
}
|
||||||
|
return &cfg
|
||||||
|
}
|
22
internal/lib/errors.go
Normal file
22
internal/lib/errors.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInternal = errors.New("internal")
|
||||||
|
ErrUnexpected = errors.New("unexpected")
|
||||||
|
ErrNoPermission = errors.New("no permission")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrBadHandleOrPassword = errors.New("bad handle or password")
|
||||||
|
ErrBadRole = errors.New("bad role")
|
||||||
|
ErrTooShortPassword = errors.New("too short password")
|
||||||
|
ErrTooLongPassword = errors.New("too long password")
|
||||||
|
ErrBadEmail = errors.New("bad email")
|
||||||
|
ErrBadUsername = errors.New("bad username")
|
||||||
|
ErrTooShortUsername = errors.New("too short username")
|
||||||
|
ErrTooLongUsername = errors.New("too long username")
|
||||||
|
)
|
40
internal/lib/lib.go
Normal file
40
internal/lib/lib.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
RoleSpectator int32 = 0
|
||||||
|
RoleParticipant int32 = 1
|
||||||
|
RoleModerator int32 = 2
|
||||||
|
RoleAdmin int32 = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsAdmin(role int32) bool {
|
||||||
|
return role == RoleAdmin
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsModerator(role int32) bool {
|
||||||
|
return role == RoleModerator
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsParticipant(role int32) bool {
|
||||||
|
return role == RoleParticipant
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsSpectator(role int32) bool {
|
||||||
|
return role == RoleSpectator
|
||||||
|
}
|
||||||
|
|
||||||
|
func AsTimeP(t time.Time) *time.Time {
|
||||||
|
return &t
|
||||||
|
}
|
||||||
|
|
||||||
|
func AsInt32P(v int32) *int32 {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
func AsStringP(str string) *string {
|
||||||
|
return &str
|
||||||
|
}
|
19
internal/lib/mail.go
Normal file
19
internal/lib/mail.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/smtp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SendMail(cfg Config, to []string, subject, body string) error {
|
||||||
|
auth := smtp.PlainAuth("", cfg.Email, cfg.Password, "smtp.gmail.com")
|
||||||
|
|
||||||
|
msg := fmt.Sprintf("From: %s\nTo: %s\nSubject: %s\n%s", cfg.Email, "", subject, body)
|
||||||
|
|
||||||
|
err := smtp.SendMail("smtp.gmail.com:587", auth, cfg.Email, to, []byte(msg))
|
||||||
|
if err != nil {
|
||||||
|
return err // FIXME
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
44
internal/lib/validation.go
Normal file
44
internal/lib/validation.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/mail"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ValidPassword(str string) error {
|
||||||
|
if len(str) < 5 {
|
||||||
|
return ErrTooShortPassword
|
||||||
|
}
|
||||||
|
if len(str) > 70 {
|
||||||
|
return ErrTooLongPassword
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidUsername(str string) error {
|
||||||
|
if len(str) < 5 {
|
||||||
|
return ErrTooShortUsername
|
||||||
|
}
|
||||||
|
if len(str) > 70 {
|
||||||
|
return ErrTooLongUsername
|
||||||
|
}
|
||||||
|
if err := ValidEmail(str); err == nil {
|
||||||
|
return ErrBadUsername
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidEmail(str string) error {
|
||||||
|
emailAddress, err := mail.ParseAddress(str)
|
||||||
|
if err != nil || emailAddress.Address != str {
|
||||||
|
return ErrBadEmail
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidRole(role int32) error {
|
||||||
|
switch role {
|
||||||
|
case RoleSpectator, RoleParticipant, RoleModerator, RoleAdmin:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ErrBadRole
|
||||||
|
}
|
1
internal/services/email.go
Normal file
1
internal/services/email.go
Normal file
|
@ -0,0 +1 @@
|
||||||
|
package services
|
147
internal/services/session.go
Normal file
147
internal/services/session.go
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"ms-auth/internal/lib"
|
||||||
|
"ms-auth/internal/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SessionProvider interface {
|
||||||
|
CreateSession(ctx context.Context, userId int32) error
|
||||||
|
ReadSessionByToken(ctx context.Context, token string) (*storage.Session, error)
|
||||||
|
ReadSessionByUserId(ctx context.Context, userId int32) (*storage.Session, error)
|
||||||
|
UpdateSession(ctx context.Context, session *storage.Session) error
|
||||||
|
DeleteSessionByToken(ctx context.Context, token string) error
|
||||||
|
DeleteSessionByUserId(ctx context.Context, userId int32) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionService represents a service for managing sessions.
|
||||||
|
type SessionService struct {
|
||||||
|
sessionProvider SessionProvider
|
||||||
|
userProvider UserProvider
|
||||||
|
cfg *lib.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSessionService creates a new SessionService instance.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - sessionProvider: The SessionProvider implementation used by the SessionService.
|
||||||
|
// - userProvider: The UserProvider implementation used by the SessionService.
|
||||||
|
// - cfg: The lib.Config object used by the SessionService.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - *SessionService: A pointer to the SessionService instance.
|
||||||
|
func NewSessionService(sessionProvider SessionProvider, userProvider UserProvider, cfg *lib.Config) *SessionService {
|
||||||
|
return &SessionService{
|
||||||
|
sessionProvider: sessionProvider,
|
||||||
|
userProvider: userProvider,
|
||||||
|
cfg: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create creates a new session for a user with the given handle and password.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ctx: The context.Context object for the request.
|
||||||
|
// - handle: The handle (username or email) of the user.
|
||||||
|
// - password: The password of the user.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - *string: A pointer to the token of the newly created session, or nil if there was an error.
|
||||||
|
// - error: An error if the creation of the session or the retrieval of the session's token failed.
|
||||||
|
func (s *SessionService) Create(ctx context.Context, handle, password string) (*string, error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
user *storage.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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read retrieves the user ID associated with the given session token.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ctx: The context.Context object for the request.
|
||||||
|
// - token: The session token.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - *int32: The user ID associated with the session token, or nil if an error occurs.
|
||||||
|
// - error: An error object if any error occurs during the retrieval process.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates the session associated with the given token.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ctx: The context.Context object for the request.
|
||||||
|
// - token: The session token.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - error: An error object if any error occurs during the update process.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes the session associated with the given token.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ctx: The context.Context object for the request.
|
||||||
|
// - token: The session token.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - error: An error object if any error occurs during the deletion process.
|
||||||
|
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
|
||||||
|
}
|
244
internal/services/user.go
Normal file
244
internal/services/user.go
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"ms-auth/internal/lib"
|
||||||
|
"ms-auth/internal/storage"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserProvider interface {
|
||||||
|
CreateUser(
|
||||||
|
ctx context.Context,
|
||||||
|
username string,
|
||||||
|
password string,
|
||||||
|
email *string,
|
||||||
|
expiresAt *time.Time,
|
||||||
|
role *int32,
|
||||||
|
) (*int32, error)
|
||||||
|
ReadUserByEmail(ctx context.Context, email string) (*storage.User, error)
|
||||||
|
ReadUserByUsername(ctx context.Context, username string) (*storage.User, error)
|
||||||
|
ReadUserById(ctx context.Context, id int32) (*storage.User, error)
|
||||||
|
UpdateUser(
|
||||||
|
ctx context.Context,
|
||||||
|
id int32,
|
||||||
|
username *string,
|
||||||
|
password *string,
|
||||||
|
email *string,
|
||||||
|
expiresAt *time.Time,
|
||||||
|
role *int32,
|
||||||
|
) error
|
||||||
|
DeleteUser(ctx context.Context, id int32) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfirmationProvider interface {
|
||||||
|
CreateConfirmation(ctx context.Context, conf *storage.Confirmation) error
|
||||||
|
ReadConfirmation(ctx context.Context, confId string) (*storage.Confirmation, error)
|
||||||
|
DeleteConfirmation(ctx context.Context, confId string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmailProvider interface {
|
||||||
|
SendMail(ctx context.Context, to []string, subject string, body string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserService represents a service for managing users.
|
||||||
|
type UserService struct {
|
||||||
|
userProvider UserProvider
|
||||||
|
sessionProvider SessionProvider
|
||||||
|
confirmationProvider ConfirmationProvider
|
||||||
|
//emailProvider EmailProvider
|
||||||
|
cfg *lib.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserService creates a new UserService instance.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - userProvider: The UserProvider implementation used by the UserService.
|
||||||
|
// - sessionProvider: The SessionProvider implementation used by the UserService.
|
||||||
|
// - confirmationProvider: The ConfirmationProvider implementation used by the UserService.
|
||||||
|
// - emailProvider: The EmailProvider implementation used by the UserService.
|
||||||
|
// - cfg: The lib.Config object used by the UserService.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - *UserService: A pointer to the newly created UserService instance.
|
||||||
|
func NewUserService(
|
||||||
|
userProvider UserProvider,
|
||||||
|
sessionProvider SessionProvider,
|
||||||
|
confirmationProvider ConfirmationProvider,
|
||||||
|
//emailProvider EmailProvider,
|
||||||
|
cfg *lib.Config,
|
||||||
|
) *UserService {
|
||||||
|
return &UserService{
|
||||||
|
userProvider: userProvider,
|
||||||
|
sessionProvider: sessionProvider,
|
||||||
|
confirmationProvider: confirmationProvider,
|
||||||
|
//emailProvider: emailProvider,
|
||||||
|
cfg: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateUser creates a new user with the provided information.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ctx: The context for the operation.
|
||||||
|
// - token: The token associated with the session.
|
||||||
|
// - username: The username of the new user.
|
||||||
|
// - password: The password of the new user.
|
||||||
|
// - email: The email of the new user (can be nil).
|
||||||
|
// - expiresAt: The expiration time for the user account (can be nil).
|
||||||
|
// - role: The role of the new user.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - *int32: The ID of the created user.
|
||||||
|
// - error: An error if the operation fails.
|
||||||
|
func (u *UserService) CreateUser(ctx context.Context, token, username, password string, email *string, expiresAt *time.Time, role *int32) (*int32, error) {
|
||||||
|
user, err := u.ReadUserBySessionToken(ctx, token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
canCreate := func() bool {
|
||||||
|
if !user.IsAdmin() && !user.IsModerator() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if role != nil && user.IsModerator() {
|
||||||
|
if lib.IsModerator(*role) || lib.IsAdmin(*role) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}()
|
||||||
|
|
||||||
|
if !canCreate {
|
||||||
|
return nil, lib.ErrNoPermission
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.userProvider.CreateUser(ctx, username, password, email, expiresAt, role)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadUserBySessionToken reads a user by session token.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ctx: The context of the request.
|
||||||
|
// - token: The session token to identify the user.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - *storage.User: The user information.
|
||||||
|
// - error: An error if the operation fails.
|
||||||
|
func (u *UserService) ReadUserBySessionToken(ctx context.Context, token string) (*storage.User, error) {
|
||||||
|
session, err := u.sessionProvider.ReadSessionByToken(ctx, token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.userProvider.ReadUserById(ctx, *session.UserId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadUser reads a user by ID.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ctx: The context of the request.
|
||||||
|
// - token: The session token to identify the user.
|
||||||
|
// - id: The ID of the user to read.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - *storage.User: The user information.
|
||||||
|
// - error: An error if the operation fails.
|
||||||
|
func (u *UserService) ReadUser(ctx context.Context, token string, id int32) (*storage.User, error) {
|
||||||
|
_, err := u.ReadUserBySessionToken(ctx, token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.userProvider.ReadUserById(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateUser updates a user's information.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ctx: The context of the request.
|
||||||
|
// - token: The session token to identify the user.
|
||||||
|
// - id: The ID of the user to update.
|
||||||
|
// - username: The new username (can be nil).
|
||||||
|
// - password: The new password (can be nil).
|
||||||
|
// - email: The new email (can be nil).
|
||||||
|
// - expiresAt: The new expiration time (can be nil).
|
||||||
|
// - role: The new role (can be nil).
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - error: An error if the operation fails.
|
||||||
|
func (u *UserService) UpdateUser(
|
||||||
|
ctx context.Context,
|
||||||
|
token string,
|
||||||
|
id int32,
|
||||||
|
username *string,
|
||||||
|
password *string,
|
||||||
|
email *string,
|
||||||
|
expiresAt *time.Time,
|
||||||
|
role *int32,
|
||||||
|
) error {
|
||||||
|
me, err := u.ReadUserBySessionToken(ctx, token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := u.userProvider.ReadUserById(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAccess := func() bool {
|
||||||
|
if me.Id == user.Id {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if me.IsAdmin() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if me.IsModerator() && (user.IsParticipant() || user.IsSpectator()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}()
|
||||||
|
|
||||||
|
if !hasAccess {
|
||||||
|
return lib.ErrNoPermission
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.userProvider.UpdateUser(ctx, id, username, password, email, expiresAt, role)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUser deletes a user by id.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ctx: The context of the request.
|
||||||
|
// - token: The session token to identify the authenticated user.
|
||||||
|
// - id: The ID of the user to delete.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - error: An error if the operation fails.
|
||||||
|
func (u *UserService) DeleteUser(ctx context.Context, token string, id int32) error {
|
||||||
|
user, err := u.ReadUserBySessionToken(ctx, token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.Id == id || !user.IsAdmin() {
|
||||||
|
return lib.ErrNoPermission
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.userProvider.DeleteUser(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadUserByEmail reads a user by email.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ctx: The context of the request.
|
||||||
|
// - email: The email of the user to read.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - *storage.User: The user information.
|
||||||
|
// - error: An error if the operation fails.
|
||||||
|
func (u *UserService) ReadUserByEmail(ctx context.Context, email string) (*storage.User, error) {
|
||||||
|
return u.userProvider.ReadUserByEmail(ctx, email)
|
||||||
|
}
|
267
internal/storage/postgresql.go
Normal file
267
internal/storage/postgresql.go
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"github.com/jackc/pgerrcode"
|
||||||
|
"github.com/jackc/pgx/v5/pgconn"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
"ms-auth/internal/lib"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PostgresqlStorage struct {
|
||||||
|
db *sqlx.DB
|
||||||
|
logger *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserStorage(dsn string, logger *zap.Logger) *PostgresqlStorage {
|
||||||
|
db, err := sqlx.Connect("pgx", dsn)
|
||||||
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return &PostgresqlStorage{db: db, logger: logger}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (storage *PostgresqlStorage) Stop() error {
|
||||||
|
return storage.db.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
shortUserLifetime = time.Hour * 24 * 30
|
||||||
|
defaultUserLifetime = time.Hour * 24 * 365 * 100
|
||||||
|
)
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
Id int32 `db:"id"`
|
||||||
|
|
||||||
|
Username string `db:"username"`
|
||||||
|
HashedPassword [60]byte `db:"hashed_pwd"`
|
||||||
|
|
||||||
|
Email *string `db:"email"`
|
||||||
|
|
||||||
|
ExpiresAt time.Time `db:"expires_at"`
|
||||||
|
CreatedAt time.Time `db:"created_at"`
|
||||||
|
|
||||||
|
Role int32 `db:"role"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (user *User) IsAdmin() bool {
|
||||||
|
return lib.IsAdmin(user.Role)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (user *User) IsModerator() bool {
|
||||||
|
return lib.IsModerator(user.Role)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (user *User) IsParticipant() bool {
|
||||||
|
return lib.IsParticipant(user.Role)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (user *User) IsSpectator() bool {
|
||||||
|
return lib.IsSpectator(user.Role)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (user *User) AtLeast(role int32) bool {
|
||||||
|
return user.Role >= role
|
||||||
|
}
|
||||||
|
|
||||||
|
func (user *User) ComparePassword(password string) error {
|
||||||
|
if bcrypt.CompareHashAndPassword(user.HashedPassword[:], []byte(password)) != nil {
|
||||||
|
return lib.ErrBadHandleOrPassword
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (storage *PostgresqlStorage) CreateUser(
|
||||||
|
ctx context.Context,
|
||||||
|
username string,
|
||||||
|
password string,
|
||||||
|
email *string,
|
||||||
|
expiresAt *time.Time,
|
||||||
|
role *int32,
|
||||||
|
) (*int32, error) {
|
||||||
|
if err := lib.ValidUsername(username); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := lib.ValidPassword(password); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if email != nil {
|
||||||
|
if err := lib.ValidEmail(*email); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if role != nil {
|
||||||
|
if err := lib.ValidRole(*role); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
username = strings.ToLower(username)
|
||||||
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||||
|
if err != nil {
|
||||||
|
storage.logger.Error(err.Error())
|
||||||
|
return nil, lib.ErrInternal
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
username = strings.ToLower(username)
|
||||||
|
if email != nil {
|
||||||
|
*email = strings.ToLower(*email)
|
||||||
|
}
|
||||||
|
if role == nil {
|
||||||
|
role = lib.AsInt32P(lib.RoleSpectator)
|
||||||
|
}
|
||||||
|
if expiresAt == nil {
|
||||||
|
if email == nil {
|
||||||
|
expiresAt = lib.AsTimeP(now.Add(shortUserLifetime))
|
||||||
|
} else {
|
||||||
|
expiresAt = lib.AsTimeP(now.Add(defaultUserLifetime))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query := storage.db.Rebind(`
|
||||||
|
INSERT INTO users
|
||||||
|
(username, hashed_pwd, email, expires_at, role)
|
||||||
|
VALUES (?, ?, ?, ?, ?)
|
||||||
|
RETURNING id
|
||||||
|
`)
|
||||||
|
|
||||||
|
rows, err := storage.db.QueryxContext(ctx, query, username, hashedPassword, email, expiresAt, role)
|
||||||
|
if err != nil {
|
||||||
|
return nil, storage.handlePgErr(err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var id int32
|
||||||
|
err = rows.StructScan(&id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, storage.handlePgErr(err)
|
||||||
|
}
|
||||||
|
return &id, nil
|
||||||
|
}
|
||||||
|
func (storage *PostgresqlStorage) ReadUserByEmail(ctx context.Context, email string) (*User, error) {
|
||||||
|
if err := lib.ValidEmail(email); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
email = strings.ToLower(email)
|
||||||
|
|
||||||
|
var user User
|
||||||
|
query := storage.db.Rebind("SELECT * from users WHERE email=? LIMIT 1")
|
||||||
|
err := storage.db.GetContext(ctx, &user, query, email)
|
||||||
|
if err != nil {
|
||||||
|
return nil, storage.handlePgErr(err)
|
||||||
|
}
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
func (storage *PostgresqlStorage) ReadUserByUsername(ctx context.Context, username string) (*User, error) {
|
||||||
|
if err := lib.ValidUsername(username); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
username = strings.ToLower(username)
|
||||||
|
|
||||||
|
var user User
|
||||||
|
query := storage.db.Rebind("SELECT * from users WHERE username=? LIMIT 1")
|
||||||
|
err := storage.db.GetContext(ctx, &user, query, username)
|
||||||
|
if err != nil {
|
||||||
|
return nil, storage.handlePgErr(err)
|
||||||
|
}
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
func (storage *PostgresqlStorage) ReadUserById(ctx context.Context, id int32) (*User, error) {
|
||||||
|
var user User
|
||||||
|
query := storage.db.Rebind("SELECT * from users WHERE id=? LIMIT 1")
|
||||||
|
err := storage.db.GetContext(ctx, &user, query, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, storage.handlePgErr(err)
|
||||||
|
}
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (storage *PostgresqlStorage) UpdateUser(
|
||||||
|
ctx context.Context,
|
||||||
|
id int32,
|
||||||
|
username *string,
|
||||||
|
password *string,
|
||||||
|
email *string,
|
||||||
|
expiresAt *time.Time,
|
||||||
|
role *int32,
|
||||||
|
) error {
|
||||||
|
var err error
|
||||||
|
if username != nil {
|
||||||
|
if err = lib.ValidUsername(*username); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var hashedPassword []byte
|
||||||
|
if password != nil {
|
||||||
|
if err = lib.ValidPassword(*password); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hashedPassword, err = bcrypt.GenerateFromPassword([]byte(*password), bcrypt.DefaultCost)
|
||||||
|
if err != nil {
|
||||||
|
storage.logger.Error(err.Error())
|
||||||
|
return lib.ErrInternal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if email != nil {
|
||||||
|
if err = lib.ValidEmail(*email); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if role != nil {
|
||||||
|
if err = lib.ValidRole(*role); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if username != nil {
|
||||||
|
*username = strings.ToLower(*username)
|
||||||
|
}
|
||||||
|
if email != nil {
|
||||||
|
*email = strings.ToLower(*email)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = ?`)
|
||||||
|
|
||||||
|
_, err = storage.db.ExecContext(ctx, query, username, hashedPassword, email, expiresAt, role, id)
|
||||||
|
if err != nil {
|
||||||
|
return storage.handlePgErr(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (storage *PostgresqlStorage) DeleteUser(ctx context.Context, id int32) error {
|
||||||
|
query := storage.db.Rebind("UPDATE users SET expired_at=NOW() WHERE id = ?")
|
||||||
|
_, err := storage.db.ExecContext(ctx, query, id)
|
||||||
|
if err != nil {
|
||||||
|
return storage.handlePgErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (storage *PostgresqlStorage) 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
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
332
internal/storage/valkey.go
Normal file
332
internal/storage/valkey.go
Normal file
|
@ -0,0 +1,332 @@
|
||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"ms-auth/internal/lib"
|
||||||
|
|
||||||
|
"github.com/golang-jwt/jwt"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/valkey-io/valkey-go"
|
||||||
|
"github.com/valkey-io/valkey-go/valkeylock"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ValkeyStorage struct {
|
||||||
|
db valkey.Client
|
||||||
|
locker valkeylock.Locker
|
||||||
|
cfg *lib.Config
|
||||||
|
logger *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewValkeyStorage(dsn string, cfg *lib.Config, logger *zap.Logger) *ValkeyStorage {
|
||||||
|
opts, err := valkey.ParseURL(dsn)
|
||||||
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := valkey.NewClient(opts)
|
||||||
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
locker, err := valkeylock.NewLocker(valkeylock.LockerOption{
|
||||||
|
ClientOption: opts,
|
||||||
|
KeyMajority: 1,
|
||||||
|
NoLoopTracking: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ValkeyStorage{
|
||||||
|
db: db,
|
||||||
|
locker: locker,
|
||||||
|
cfg: cfg,
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (storage *ValkeyStorage) Stop() error {
|
||||||
|
storage.db.Close()
|
||||||
|
storage.locker.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
sessionLifetime = time.Minute * 40
|
||||||
|
confirmationLifetime = time.Hour * 5
|
||||||
|
)
|
||||||
|
|
||||||
|
func (storage *ValkeyStorage) CreateSession(
|
||||||
|
ctx context.Context,
|
||||||
|
user_id int32,
|
||||||
|
) error {
|
||||||
|
session := NewSession(user_id)
|
||||||
|
|
||||||
|
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 {
|
||||||
|
storage.logger.Error(err.Error())
|
||||||
|
return lib.ErrInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (storage *ValkeyStorage) ReadSessionByToken(ctx context.Context, token string) (*Session, error) {
|
||||||
|
session, err := Parse(token, storage.cfg.JWTSecret)
|
||||||
|
if err != nil {
|
||||||
|
storage.logger.Error(err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
real_session, err := storage.ReadSessionByUserId(ctx, *session.UserId)
|
||||||
|
if err != nil {
|
||||||
|
storage.logger.Error(err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if *session.Id != *real_session.Id {
|
||||||
|
storage.logger.Error(err.Error())
|
||||||
|
return nil, lib.ErrInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
return session, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (storage *ValkeyStorage) ReadSessionByUserId(ctx context.Context, user_id int32) (*Session, error) {
|
||||||
|
resp := storage.db.Do(ctx, storage.db.B().Get().Key(string(user_id)).Build())
|
||||||
|
if err := resp.Error(); err != nil {
|
||||||
|
storage.logger.Error(err.Error())
|
||||||
|
return nil, lib.ErrInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := resp.ToString()
|
||||||
|
if err != nil {
|
||||||
|
storage.logger.Error(err.Error())
|
||||||
|
return nil, lib.ErrInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Session{
|
||||||
|
Id: &id,
|
||||||
|
UserId: &user_id,
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (storage *ValkeyStorage) UpdateSession(ctx context.Context, session *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 {
|
||||||
|
storage.logger.Error(err.Error())
|
||||||
|
return lib.ErrInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (storage *ValkeyStorage) DeleteSessionByToken(ctx context.Context, token string) error {
|
||||||
|
session, err := Parse(token, storage.cfg.JWTSecret)
|
||||||
|
if err != nil {
|
||||||
|
storage.logger.Error(err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = storage.DeleteSessionByUserId(ctx, *session.UserId)
|
||||||
|
if err != nil {
|
||||||
|
storage.logger.Error(err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (storage *ValkeyStorage) DeleteSessionByUserId(ctx context.Context, user_id int32) error {
|
||||||
|
resp := storage.db.Do(ctx, storage.db.
|
||||||
|
B().Del().
|
||||||
|
Key(string(user_id)).
|
||||||
|
Build(),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := resp.Error(); err != nil {
|
||||||
|
storage.logger.Error(err.Error())
|
||||||
|
return lib.ErrInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (storage *ValkeyStorage) CreateConfirmation(ctx context.Context, conf *Confirmation) error {
|
||||||
|
resp := storage.db.Do(ctx, storage.db.
|
||||||
|
B().Set().
|
||||||
|
Key(*conf.Id).
|
||||||
|
Value(string(conf.JSON())).
|
||||||
|
Exat(time.Now().Add(confirmationLifetime)).
|
||||||
|
Build(),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := resp.Error(); err != nil {
|
||||||
|
storage.logger.Error(err.Error())
|
||||||
|
return lib.ErrInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (storage *ValkeyStorage) ReadConfirmation(ctx context.Context, conf_id string) (*Confirmation, error) {
|
||||||
|
resp := storage.db.Do(ctx, storage.db.
|
||||||
|
B().Get().
|
||||||
|
Key(conf_id).
|
||||||
|
Build(),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := resp.Error(); err != nil {
|
||||||
|
storage.logger.Error(err.Error())
|
||||||
|
return nil, lib.ErrInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := resp.AsBytes()
|
||||||
|
if err != nil {
|
||||||
|
storage.logger.Error(err.Error())
|
||||||
|
return nil, lib.ErrInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
var conf Confirmation
|
||||||
|
err = json.Unmarshal(b, &conf)
|
||||||
|
if err != nil {
|
||||||
|
storage.logger.Error(err.Error())
|
||||||
|
return nil, lib.ErrInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
return &conf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (storage *ValkeyStorage) DeleteConfirmation(ctx context.Context, conf_id string) error {
|
||||||
|
resp := storage.db.Do(ctx, storage.db.
|
||||||
|
B().Del().
|
||||||
|
Key(conf_id).
|
||||||
|
Build(),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := resp.Error(); err != nil {
|
||||||
|
storage.logger.Error(err.Error())
|
||||||
|
return lib.ErrInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrBadSession = errors.New("bad session")
|
||||||
|
ErrBadConfirmation = errors.New("bad confirmation")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Confirmation struct {
|
||||||
|
Id *string `json:"id"`
|
||||||
|
UserId *int32 `json:"user_id,omitempty"`
|
||||||
|
Email *string `json:"email"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfirmation(userId *int32, email string) (*Confirmation, error) {
|
||||||
|
c := &Confirmation{
|
||||||
|
Id: lib.AsStringP(uuid.NewString()),
|
||||||
|
UserId: userId,
|
||||||
|
Email: &email,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Valid(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Confirmation) Valid() error {
|
||||||
|
if c.Id == nil {
|
||||||
|
return ErrBadConfirmation
|
||||||
|
}
|
||||||
|
// FIXME
|
||||||
|
// if c.userId == nil {
|
||||||
|
// return ErrBadConfirmation
|
||||||
|
// }
|
||||||
|
if c.Email == nil {
|
||||||
|
return ErrBadConfirmation
|
||||||
|
}
|
||||||
|
if err := lib.ValidEmail(*c.Email); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Confirmation) JSON() []byte {
|
||||||
|
b, err := json.Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
type Session struct {
|
||||||
|
Id *string
|
||||||
|
UserId *int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSession(userId int32) *Session {
|
||||||
|
return &Session{
|
||||||
|
Id: lib.AsStringP(uuid.NewString()),
|
||||||
|
UserId: &userId,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Session) Valid() error {
|
||||||
|
if s.Id == nil {
|
||||||
|
return ErrBadSession
|
||||||
|
}
|
||||||
|
if s.UserId == nil {
|
||||||
|
return ErrBadSession
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Session) Token(secret string) (string, error) {
|
||||||
|
if err := s.Valid(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, s)
|
||||||
|
str, err := refreshToken.SignedString([]byte(secret))
|
||||||
|
if err != nil {
|
||||||
|
return "", ErrBadSession
|
||||||
|
}
|
||||||
|
return str, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Parse(tkn string, secret string) (*Session, error) {
|
||||||
|
parsedToken, err := jwt.ParseWithClaims(tkn, &Session{}, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
return []byte(secret), nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrBadSession
|
||||||
|
}
|
||||||
|
session := parsedToken.Claims.(*Session)
|
||||||
|
if err := session.Valid(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return session, nil
|
||||||
|
}
|
11
internal/transport/email_server.go
Normal file
11
internal/transport/email_server.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package transport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
emailv1 "ms-auth/pkg/go/gen/email/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *AuthServer) SendEmail(ctx context.Context, req *emailv1.SendEmailRequest) (*emptypb.Empty, error) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
1
internal/transport/interceptors.go
Normal file
1
internal/transport/interceptors.go
Normal file
|
@ -0,0 +1 @@
|
||||||
|
package transport
|
123
internal/transport/server.go
Normal file
123
internal/transport/server.go
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
package transport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
"ms-auth/internal/storage"
|
||||||
|
emailv1 "ms-auth/pkg/go/gen/email/v1"
|
||||||
|
sessionv1 "ms-auth/pkg/go/gen/session/v1"
|
||||||
|
userv1 "ms-auth/pkg/go/gen/user/v1"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SessionServiceI 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 UserServiceI interface {
|
||||||
|
CreateUser(ctx context.Context, token, username, password string, email *string, expiresAt *time.Time, role *int32) (*int32, error)
|
||||||
|
ReadUser(ctx context.Context, token string, id int32) (*storage.User, error)
|
||||||
|
UpdateUser(ctx context.Context, token string, id int32, username *string, password *string, email *string, expiresAt *time.Time, role *int32) error
|
||||||
|
DeleteUser(ctx context.Context, token string, id int32) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthServer struct {
|
||||||
|
emailv1.UnimplementedEmailServiceServer
|
||||||
|
|
||||||
|
sessionv1.UnimplementedSessionServiceServer
|
||||||
|
sessionService SessionServiceI
|
||||||
|
|
||||||
|
userv1.UnimplementedUserServiceServer
|
||||||
|
userService UserServiceI
|
||||||
|
|
||||||
|
gRPCServer *grpc.Server
|
||||||
|
logger *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAuthServer creates a new instance of the AuthServer struct.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - sessionService: A pointer to the SessionServiceI interface.
|
||||||
|
// - gRPCServer: A pointer to the grpc.Server struct.
|
||||||
|
// - logger: A pointer to the zap.Logger struct.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - *AuthServer: A pointer to the AuthServer struct.
|
||||||
|
func NewAuthServer(sessionService SessionServiceI, userService UserServiceI, gRPCServer *grpc.Server, logger *zap.Logger) *AuthServer {
|
||||||
|
return &AuthServer{
|
||||||
|
sessionService: sessionService,
|
||||||
|
userService: userService,
|
||||||
|
gRPCServer: gRPCServer,
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start starts the AuthServer and listens on port :8090.
|
||||||
|
//
|
||||||
|
// It creates a listener on the specified address and starts serving incoming requests.
|
||||||
|
// It also logs the server start and any errors that occur during serving.
|
||||||
|
//
|
||||||
|
// No parameters.
|
||||||
|
// No return values.
|
||||||
|
func (s *AuthServer) Start() {
|
||||||
|
lis, err := net.Listen("tcp", ":8090")
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Fatal("")
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionv1.RegisterSessionServiceServer(s.gRPCServer, s)
|
||||||
|
go func() {
|
||||||
|
s.logger.Info("Listening on :8090")
|
||||||
|
if err := s.gRPCServer.Serve(lis); err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
s.logger.Info("server started")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GracefullyStop stops the server gracefully.
|
||||||
|
//
|
||||||
|
// No parameters.
|
||||||
|
// No return values.
|
||||||
|
func (s *AuthServer) GracefullyStop() {
|
||||||
|
s.gRPCServer.GracefulStop()
|
||||||
|
s.logger.Info("server stopped")
|
||||||
|
}
|
||||||
|
|
||||||
|
func AsTimeP(t *timestamppb.Timestamp) *time.Time {
|
||||||
|
if t == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
tt := t.AsTime()
|
||||||
|
return &tt
|
||||||
|
}
|
||||||
|
|
||||||
|
func AsInt32P(v *userv1.Role) *int32 {
|
||||||
|
if v == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
vv := int32(v.Number())
|
||||||
|
return &vv
|
||||||
|
}
|
||||||
|
|
||||||
|
func AsTimestampP(t *time.Time) *timestamppb.Timestamp {
|
||||||
|
if t == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return timestamppb.New(*t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AsRoleP(r *int32) *userv1.Role {
|
||||||
|
if r == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rr := userv1.Role(*r)
|
||||||
|
return &rr
|
||||||
|
}
|
45
internal/transport/session_server.go
Normal file
45
internal/transport/session_server.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package transport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
sessionv1 "ms-auth/pkg/go/gen/session/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
124
internal/transport/user_server.go
Normal file
124
internal/transport/user_server.go
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
package transport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
userv1 "ms-auth/pkg/go/gen/user/v1"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
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,
|
||||||
|
req.GetToken(),
|
||||||
|
user.GetUsername(),
|
||||||
|
user.GetPassword(),
|
||||||
|
user.Email,
|
||||||
|
AsTimeP(user.ExpiresAt),
|
||||||
|
AsInt32P(user.Role),
|
||||||
|
)
|
||||||
|
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.GetToken(),
|
||||||
|
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,
|
||||||
|
Email: user.Email,
|
||||||
|
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,
|
||||||
|
req.GetToken(),
|
||||||
|
user.GetId(),
|
||||||
|
user.Username,
|
||||||
|
user.Password,
|
||||||
|
user.Email,
|
||||||
|
AsTimeP(user.ExpiresAt),
|
||||||
|
AsInt32P(user.Role),
|
||||||
|
)
|
||||||
|
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.GetToken(),
|
||||||
|
req.GetId(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
|
||||||
|
}
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AuthServer) ConfirmEmail(ctx context.Context, req *userv1.ConfirmEmailRequest) (*emptypb.Empty, error) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AuthServer) RegisterUser(ctx context.Context, req *userv1.RegisterUserRequest) (*emptypb.Empty, error) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AuthServer) ConfirmRegisterUser(ctx context.Context, req *userv1.ConfirmRegisterUserRequest) (*emptypb.Empty, error) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AuthServer) ResetPassword(ctx context.Context, req *userv1.ResetPasswordRequest) (*emptypb.Empty, error) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AuthServer) ConfirmResetPassword(ctx context.Context, req *userv1.ConfirmResetPasswordRequest) (*emptypb.Empty, error) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func shortenEmail(email *string) *string {
|
||||||
|
if email == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
parts := strings.Split(*email, "@")
|
||||||
|
p1 := parts[0]
|
||||||
|
p2 := parts[1]
|
||||||
|
a := "****"
|
||||||
|
if len(p1) <= 4 {
|
||||||
|
e := a + "@" + p2
|
||||||
|
return &e
|
||||||
|
}
|
||||||
|
e := p1[:len(p1)-4] + a + "@" + p2
|
||||||
|
return &e
|
||||||
|
}
|
47
main.go
Normal file
47
main.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"ms-auth/internal/app"
|
||||||
|
"ms-auth/internal/lib"
|
||||||
|
"ms-auth/internal/services"
|
||||||
|
"ms-auth/internal/storage"
|
||||||
|
"ms-auth/internal/transport"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg := lib.MustSetupConfig()
|
||||||
|
|
||||||
|
var logger *zap.Logger
|
||||||
|
if cfg.Env == "prod" {
|
||||||
|
logger = zap.Must(zap.NewProduction())
|
||||||
|
} else if cfg.Env == "dev" {
|
||||||
|
logger = zap.Must(zap.NewDevelopment())
|
||||||
|
} else {
|
||||||
|
panic(fmt.Sprintf(`error reading config: env expected "prod" or "dev", got "%s"`, cfg.Env))
|
||||||
|
}
|
||||||
|
|
||||||
|
postgres := storage.NewUserStorage(cfg.PostgresDSN, logger)
|
||||||
|
|
||||||
|
vk := storage.NewValkeyStorage(cfg.RedisDSN, cfg, logger)
|
||||||
|
|
||||||
|
sessionService := services.NewSessionService(vk, postgres, cfg)
|
||||||
|
userService := services.NewUserService(postgres, vk, vk, cfg)
|
||||||
|
|
||||||
|
server := transport.NewAuthServer(sessionService, userService, grpc.NewServer(), logger)
|
||||||
|
|
||||||
|
application := app.NewApp(cfg, server)
|
||||||
|
|
||||||
|
application.Start()
|
||||||
|
|
||||||
|
stop := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(stop, syscall.SIGTERM, syscall.SIGINT)
|
||||||
|
|
||||||
|
<-stop
|
||||||
|
application.GracefullyStop()
|
||||||
|
}
|
43
migrations/20240608163806_initial.sql
Normal file
43
migrations/20240608163806_initial.sql
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE TABLE IF NOT EXISTS users
|
||||||
|
(
|
||||||
|
id serial NOT NULL,
|
||||||
|
username VARCHAR(70) UNIQUE NOT NULL,
|
||||||
|
hashed_pwd VARCHAR(60) NOT NULL,
|
||||||
|
email VARCHAR(70) UNIQUE,
|
||||||
|
role INT NOT NULL,
|
||||||
|
expires_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
CHECK (length(username) != 0 AND username = lower(username)),
|
||||||
|
CHECK (length(email) != 0 AND email = lower(email)),
|
||||||
|
CHECK (lower(username) != lower(email)),
|
||||||
|
CHECK (length(hashed_pwd) != 0),
|
||||||
|
CHECK (role BETWEEN 0 AND 3)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX ON users (id);
|
||||||
|
CREATE INDEX ON users (username);
|
||||||
|
CREATE INDEX ON users (email);
|
||||||
|
|
||||||
|
CREATE FUNCTION usr_upd_trg_fn() RETURNS TRIGGER
|
||||||
|
LANGUAGE plpgsql AS
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
NEW.updated_at = NOW();
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
CREATE TRIGGER usr_upd_trg
|
||||||
|
BEFORE UPDATE
|
||||||
|
ON users
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE usr_upd_trg_fn();
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
DROP TABLE IF EXISTS users;
|
412
pkg/go/gen/user/v1/user.pb.go
Normal file
412
pkg/go/gen/user/v1/user.pb.go
Normal file
|
@ -0,0 +1,412 @@
|
||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.28.1
|
||||||
|
// protoc (unknown)
|
||||||
|
// source: user/v1/user.proto
|
||||||
|
|
||||||
|
package userv1
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
_ "google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Role int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
Role_ROLE_SPECTATOR_UNSPECIFIED Role = 0
|
||||||
|
Role_ROLE_PARTICIPANT Role = 1
|
||||||
|
Role_ROLE_MODERATOR Role = 2
|
||||||
|
Role_ROLE_ADMIN Role = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enum value maps for Role.
|
||||||
|
var (
|
||||||
|
Role_name = map[int32]string{
|
||||||
|
0: "ROLE_SPECTATOR_UNSPECIFIED",
|
||||||
|
1: "ROLE_PARTICIPANT",
|
||||||
|
2: "ROLE_MODERATOR",
|
||||||
|
3: "ROLE_ADMIN",
|
||||||
|
}
|
||||||
|
Role_value = map[string]int32{
|
||||||
|
"ROLE_SPECTATOR_UNSPECIFIED": 0,
|
||||||
|
"ROLE_PARTICIPANT": 1,
|
||||||
|
"ROLE_MODERATOR": 2,
|
||||||
|
"ROLE_ADMIN": 3,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (x Role) Enum() *Role {
|
||||||
|
p := new(Role)
|
||||||
|
*p = x
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Role) String() string {
|
||||||
|
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Role) Descriptor() protoreflect.EnumDescriptor {
|
||||||
|
return file_user_v1_user_proto_enumTypes[0].Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Role) Type() protoreflect.EnumType {
|
||||||
|
return &file_user_v1_user_proto_enumTypes[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Role) Number() protoreflect.EnumNumber {
|
||||||
|
return protoreflect.EnumNumber(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Role.Descriptor instead.
|
||||||
|
func (Role) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return file_user_v1_user_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateUserRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
|
||||||
|
User *CreateUserRequest_User `protobuf:"bytes,2,opt,name=user,proto3" json:"user,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateUserRequest) Reset() {
|
||||||
|
*x = CreateUserRequest{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_user_v1_user_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateUserRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*CreateUserRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *CreateUserRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_user_v1_user_proto_msgTypes[0]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use CreateUserRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*CreateUserRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_user_v1_user_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateUserRequest) GetToken() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Token
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateUserRequest) GetUser() *CreateUserRequest_User {
|
||||||
|
if x != nil {
|
||||||
|
return x.User
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateUserResponse struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateUserResponse) Reset() {
|
||||||
|
*x = CreateUserResponse{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_user_v1_user_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateUserResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*CreateUserResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *CreateUserResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_user_v1_user_proto_msgTypes[1]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use CreateUserResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*CreateUserResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_user_v1_user_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateUserResponse) GetId() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Id
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateUserRequest_User struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"`
|
||||||
|
Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
|
||||||
|
Email *string `protobuf:"bytes,3,opt,name=email,proto3,oneof" json:"email,omitempty"`
|
||||||
|
ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=expires_at,json=expiresAt,proto3,oneof" json:"expires_at,omitempty"`
|
||||||
|
Role *Role `protobuf:"varint,5,opt,name=role,proto3,enum=proto.user.v1.Role,oneof" json:"role,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateUserRequest_User) Reset() {
|
||||||
|
*x = CreateUserRequest_User{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_user_v1_user_proto_msgTypes[2]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateUserRequest_User) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*CreateUserRequest_User) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *CreateUserRequest_User) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_user_v1_user_proto_msgTypes[2]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use CreateUserRequest_User.ProtoReflect.Descriptor instead.
|
||||||
|
func (*CreateUserRequest_User) Descriptor() ([]byte, []int) {
|
||||||
|
return file_user_v1_user_proto_rawDescGZIP(), []int{0, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateUserRequest_User) GetUsername() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Username
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateUserRequest_User) GetPassword() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Password
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateUserRequest_User) GetEmail() string {
|
||||||
|
if x != nil && x.Email != nil {
|
||||||
|
return *x.Email
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateUserRequest_User) GetExpiresAt() *timestamppb.Timestamp {
|
||||||
|
if x != nil {
|
||||||
|
return x.ExpiresAt
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateUserRequest_User) GetRole() Role {
|
||||||
|
if x != nil && x.Role != nil {
|
||||||
|
return *x.Role
|
||||||
|
}
|
||||||
|
return Role_ROLE_SPECTATOR_UNSPECIFIED
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_user_v1_user_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_user_v1_user_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x12, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70,
|
||||||
|
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x75, 0x73, 0x65, 0x72,
|
||||||
|
0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70,
|
||||||
|
0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x22, 0xd0, 0x02, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72,
|
||||||
|
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e,
|
||||||
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x39, 0x0a,
|
||||||
|
0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70, 0x72,
|
||||||
|
0x6f, 0x74, 0x6f, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61,
|
||||||
|
0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x55, 0x73,
|
||||||
|
0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x1a, 0xe9, 0x01, 0x0a, 0x04, 0x55, 0x73, 0x65,
|
||||||
|
0x72, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20,
|
||||||
|
0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a,
|
||||||
|
0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
|
0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x19, 0x0a, 0x05, 0x65, 0x6d, 0x61,
|
||||||
|
0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69,
|
||||||
|
0x6c, 0x88, 0x01, 0x01, 0x12, 0x3e, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f,
|
||||||
|
0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||||
|
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73,
|
||||||
|
0x74, 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41,
|
||||||
|
0x74, 0x88, 0x01, 0x01, 0x12, 0x2c, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01,
|
||||||
|
0x28, 0x0e, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e,
|
||||||
|
0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x48, 0x02, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x88,
|
||||||
|
0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x42, 0x0d, 0x0a, 0x0b,
|
||||||
|
0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x42, 0x07, 0x0a, 0x05, 0x5f,
|
||||||
|
0x72, 0x6f, 0x6c, 0x65, 0x22, 0x24, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73,
|
||||||
|
0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
|
||||||
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x2a, 0x60, 0x0a, 0x04, 0x52, 0x6f,
|
||||||
|
0x6c, 0x65, 0x12, 0x1e, 0x0a, 0x1a, 0x52, 0x4f, 0x4c, 0x45, 0x5f, 0x53, 0x50, 0x45, 0x43, 0x54,
|
||||||
|
0x41, 0x54, 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44,
|
||||||
|
0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x4f, 0x4c, 0x45, 0x5f, 0x50, 0x41, 0x52, 0x54, 0x49,
|
||||||
|
0x43, 0x49, 0x50, 0x41, 0x4e, 0x54, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x4f, 0x4c, 0x45,
|
||||||
|
0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a,
|
||||||
|
0x52, 0x4f, 0x4c, 0x45, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x03, 0x32, 0x60, 0x0a, 0x0b,
|
||||||
|
0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x51, 0x0a, 0x0a, 0x43,
|
||||||
|
0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
|
||||||
|
0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x72,
|
||||||
|
0x6f, 0x74, 0x6f, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61,
|
||||||
|
0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x97,
|
||||||
|
0x01, 0x0a, 0x11, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x75, 0x73, 0x65,
|
||||||
|
0x72, 0x2e, 0x76, 0x31, 0x42, 0x09, 0x55, 0x73, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50,
|
||||||
|
0x01, 0x5a, 0x21, 0x6d, 0x73, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67,
|
||||||
|
0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x73,
|
||||||
|
0x65, 0x72, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x50, 0x55, 0x58, 0xaa, 0x02, 0x0d, 0x50, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0d, 0x50, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x5c, 0x55, 0x73, 0x65, 0x72, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x19, 0x50, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x5c, 0x55, 0x73, 0x65, 0x72, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65,
|
||||||
|
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x3a, 0x3a,
|
||||||
|
0x55, 0x73, 0x65, 0x72, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_user_v1_user_proto_rawDescOnce sync.Once
|
||||||
|
file_user_v1_user_proto_rawDescData = file_user_v1_user_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_user_v1_user_proto_rawDescGZIP() []byte {
|
||||||
|
file_user_v1_user_proto_rawDescOnce.Do(func() {
|
||||||
|
file_user_v1_user_proto_rawDescData = protoimpl.X.CompressGZIP(file_user_v1_user_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_user_v1_user_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_user_v1_user_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||||
|
var file_user_v1_user_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||||
|
var file_user_v1_user_proto_goTypes = []interface{}{
|
||||||
|
(Role)(0), // 0: proto.user.v1.Role
|
||||||
|
(*CreateUserRequest)(nil), // 1: proto.user.v1.CreateUserRequest
|
||||||
|
(*CreateUserResponse)(nil), // 2: proto.user.v1.CreateUserResponse
|
||||||
|
(*CreateUserRequest_User)(nil), // 3: proto.user.v1.CreateUserRequest.User
|
||||||
|
(*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp
|
||||||
|
}
|
||||||
|
var file_user_v1_user_proto_depIdxs = []int32{
|
||||||
|
3, // 0: proto.user.v1.CreateUserRequest.user:type_name -> proto.user.v1.CreateUserRequest.User
|
||||||
|
4, // 1: proto.user.v1.CreateUserRequest.User.expires_at:type_name -> google.protobuf.Timestamp
|
||||||
|
0, // 2: proto.user.v1.CreateUserRequest.User.role:type_name -> proto.user.v1.Role
|
||||||
|
1, // 3: proto.user.v1.UserService.CreateUser:input_type -> proto.user.v1.CreateUserRequest
|
||||||
|
2, // 4: proto.user.v1.UserService.CreateUser:output_type -> proto.user.v1.CreateUserResponse
|
||||||
|
4, // [4:5] is the sub-list for method output_type
|
||||||
|
3, // [3:4] is the sub-list for method input_type
|
||||||
|
3, // [3:3] is the sub-list for extension type_name
|
||||||
|
3, // [3:3] is the sub-list for extension extendee
|
||||||
|
0, // [0:3] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_user_v1_user_proto_init() }
|
||||||
|
func file_user_v1_user_proto_init() {
|
||||||
|
if File_user_v1_user_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_user_v1_user_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*CreateUserRequest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_user_v1_user_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*CreateUserResponse); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_user_v1_user_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*CreateUserRequest_User); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_user_v1_user_proto_msgTypes[2].OneofWrappers = []interface{}{}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_user_v1_user_proto_rawDesc,
|
||||||
|
NumEnums: 1,
|
||||||
|
NumMessages: 3,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 1,
|
||||||
|
},
|
||||||
|
GoTypes: file_user_v1_user_proto_goTypes,
|
||||||
|
DependencyIndexes: file_user_v1_user_proto_depIdxs,
|
||||||
|
EnumInfos: file_user_v1_user_proto_enumTypes,
|
||||||
|
MessageInfos: file_user_v1_user_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_user_v1_user_proto = out.File
|
||||||
|
file_user_v1_user_proto_rawDesc = nil
|
||||||
|
file_user_v1_user_proto_goTypes = nil
|
||||||
|
file_user_v1_user_proto_depIdxs = nil
|
||||||
|
}
|
105
pkg/go/gen/user/v1/user_grpc.pb.go
Normal file
105
pkg/go/gen/user/v1/user_grpc.pb.go
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// - protoc-gen-go-grpc v1.2.0
|
||||||
|
// - protoc (unknown)
|
||||||
|
// source: user/v1/user.proto
|
||||||
|
|
||||||
|
package userv1
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
// Requires gRPC-Go v1.32.0 or later.
|
||||||
|
const _ = grpc.SupportPackageIsVersion7
|
||||||
|
|
||||||
|
// UserServiceClient is the client API for UserService service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
type UserServiceClient interface {
|
||||||
|
CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*CreateUserResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type userServiceClient struct {
|
||||||
|
cc grpc.ClientConnInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserServiceClient(cc grpc.ClientConnInterface) UserServiceClient {
|
||||||
|
return &userServiceClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *userServiceClient) CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*CreateUserResponse, error) {
|
||||||
|
out := new(CreateUserResponse)
|
||||||
|
err := c.cc.Invoke(ctx, "/proto.user.v1.UserService/CreateUser", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserServiceServer is the server API for UserService service.
|
||||||
|
// All implementations must embed UnimplementedUserServiceServer
|
||||||
|
// for forward compatibility
|
||||||
|
type UserServiceServer interface {
|
||||||
|
CreateUser(context.Context, *CreateUserRequest) (*CreateUserResponse, error)
|
||||||
|
mustEmbedUnimplementedUserServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedUserServiceServer must be embedded to have forward compatible implementations.
|
||||||
|
type UnimplementedUserServiceServer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedUserServiceServer) CreateUser(context.Context, *CreateUserRequest) (*CreateUserResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method CreateUser not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedUserServiceServer) mustEmbedUnimplementedUserServiceServer() {}
|
||||||
|
|
||||||
|
// UnsafeUserServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
// Use of this interface is not recommended, as added methods to UserServiceServer will
|
||||||
|
// result in compilation errors.
|
||||||
|
type UnsafeUserServiceServer interface {
|
||||||
|
mustEmbedUnimplementedUserServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterUserServiceServer(s grpc.ServiceRegistrar, srv UserServiceServer) {
|
||||||
|
s.RegisterService(&UserService_ServiceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _UserService_CreateUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(CreateUserRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(UserServiceServer).CreateUser(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/proto.user.v1.UserService/CreateUser",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(UserServiceServer).CreateUser(ctx, req.(*CreateUserRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserService_ServiceDesc is the grpc.ServiceDesc for UserService service.
|
||||||
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
var UserService_ServiceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "proto.user.v1.UserService",
|
||||||
|
HandlerType: (*UserServiceServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "CreateUser",
|
||||||
|
Handler: _UserService_CreateUser_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{},
|
||||||
|
Metadata: "user/v1/user.proto",
|
||||||
|
}
|
33
proto/user/v1/user.proto
Normal file
33
proto/user/v1/user.proto
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package proto.user.v1;
|
||||||
|
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
|
||||||
|
service UserService {
|
||||||
|
rpc CreateUser (CreateUserRequest) returns (CreateUserResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Role {
|
||||||
|
ROLE_SPECTATOR_UNSPECIFIED = 0;
|
||||||
|
ROLE_PARTICIPANT = 1;
|
||||||
|
ROLE_MODERATOR = 2;
|
||||||
|
ROLE_ADMIN = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateUserRequest {
|
||||||
|
message User {
|
||||||
|
string username = 1;
|
||||||
|
string password = 2;
|
||||||
|
optional string email = 3;
|
||||||
|
optional google.protobuf.Timestamp expires_at = 4;
|
||||||
|
optional Role role = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
string token = 1;
|
||||||
|
User user = 2;
|
||||||
|
}
|
||||||
|
message CreateUserResponse {
|
||||||
|
int32 id = 1;
|
||||||
|
}
|
Loading…
Reference in a new issue