diff --git a/Migrate.md b/Migrate.md new file mode 100644 index 0000000..66282d5 --- /dev/null +++ b/Migrate.md @@ -0,0 +1,54 @@ +## Migrating with Atlas + +### Overview + +Atlas provides a streamlined way to manage and migrate database schemas effortlessly. Follow the steps below to set up and migrate your database using Atlas. + +### Step-by-Step Guide + +#### 1. Installation + +Firstly, install Atlas using the provided script: + +```bash +curl -sSf https://atlasgo.sh | sh +``` + +This will ensure you have the necessary tools to inspect and apply schema changes seamlessly. + +#### 2. Inspecting the Database and Generating Schema + +Whenever you make changes to your database schema, you must update your schema definition. To do this, inspect your current database setup and generate a `schema.sql` file: + +```bash +atlas schema inspect \ + --url "postgres://postgres:123456@127.0.0.1:5432/postgres?search_path=public&sslmode=disable" \ + --format "{{ sql . }}" > schema.sql +``` + +This command fetches the current schema structure and outputs it to a `schema.sql` file, ensuring you have an up-to-date representation of your database schema. + +#### 3. Applying Schema Changes + +Once you've made the necessary updates to the `schema.sql` file, you can apply these changes to your database: + +```bash +atlas schema apply \ + --url "postgres://postgres:123456@127.0.0.1:5432/postgres?&sslmode=disable" \ + --to "file://schema.sql" \ + --dev-url "docker://postgres/15" +``` + +Here's what each parameter does: + +- `--url`: Specifies the connection URL to your target database where changes will be applied. +- `--to`: Indicates the path to the `schema.sql` file containing the schema changes. +- `--dev-url`: Provides a development URL for rolling back changes if necessary, ensuring a safe migration process. + +#### 4. Confirm and Apply + +After executing the migration command, review the changes to ensure everything aligns with your expectations. If satisfied, proceed with the migration to finalize the schema changes in your database. + +--- + +This improved documentation offers a structured approach, providing clarity on each step and its purpose. \ No newline at end of file diff --git a/apperror/error.go b/apperror/error.go index 216ee94..a0cbfb8 100644 --- a/apperror/error.go +++ b/apperror/error.go @@ -21,4 +21,5 @@ var ( InvalidToken = &AppError{"invalid-token", http.StatusUnauthorized} InvalidEmail = &AppError{"invalid-email", http.StatusNotFound} DuplicateEmail = &AppError{"duplicate-email", http.StatusConflict} + Unauthorized = &AppError{"unauthorized", http.StatusUnauthorized} ) diff --git a/cmd/main.go b/cmd/main.go index bd0981b..aab2be4 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -20,7 +20,6 @@ func main() { r := gin.Default() r.GET("/_hc", container.HcHandler.HealthCheck) - r.GET("/featureflag/live", container.FeatureflagHandler.GetLivestreamInfo) r.GET("/live", container.FeatureflagHandler.GetLivestreamInfo) r.GET("/events", container.EventHandler.GetAllEvents) r.GET("/events/:eventId", container.EventHandler.GetEventById) diff --git a/di/wire.go b/di/wire.go index 97aec40..8a080f0 100644 --- a/di/wire.go +++ b/di/wire.go @@ -8,10 +8,10 @@ import ( "github.com/isd-sgcu/oph66-backend/cache" "github.com/isd-sgcu/oph66-backend/cfgldr" "github.com/isd-sgcu/oph66-backend/database" + auth "github.com/isd-sgcu/oph66-backend/internal/auth" event "github.com/isd-sgcu/oph66-backend/internal/event" featureflag "github.com/isd-sgcu/oph66-backend/internal/feature_flag" healthcheck "github.com/isd-sgcu/oph66-backend/internal/health_check" - auth "github.com/isd-sgcu/oph66-backend/internal/auth" "github.com/isd-sgcu/oph66-backend/logger" "go.uber.org/zap" ) diff --git a/di/wire_gen.go b/di/wire_gen.go index a00128f..bc65a40 100644 --- a/di/wire_gen.go +++ b/di/wire_gen.go @@ -44,8 +44,8 @@ func Init() (Container, error) { featureflagCache := featureflag.NewCache(client, zapLogger) featureflagHandler := featureflag.NewHandler(featureflagService, featureflagCache) authRepository := auth.NewRepository(db) - authService := auth.NewService(authRepository, zapLogger) - authHandler := auth.NewHandler(authService, config, zapLogger) + authService := auth.NewService(authRepository, zapLogger, config) + authHandler := auth.NewHandler(authService, zapLogger) container := newContainer(handler, healthcheckHandler, featureflagHandler, authHandler, config, zapLogger) return container, nil } diff --git a/go.mod b/go.mod index 4274df6..3f11bc1 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,8 @@ require ( github.com/redis/go-redis/v9 v9.3.0 github.com/spf13/viper v1.18.1 go.uber.org/zap v1.26.0 + golang.org/x/oauth2 v0.15.0 + google.golang.org/api v0.153.0 gorm.io/driver/postgres v1.5.4 gorm.io/gorm v1.25.5 ) @@ -15,14 +17,8 @@ require ( require ( cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - github.com/coreos/go-oidc/v3 v3.9.0 // indirect - github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect - github.com/go-jose/go-jose/v3 v3.0.1 // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/pquerna/cachecontrol v0.2.0 // indirect - golang.org/x/oauth2 v0.15.0 // indirect google.golang.org/appengine v1.6.8 // indirect - gopkg.in/square/go-jose.v2 v2.6.0 // indirect ) require ( @@ -30,7 +26,6 @@ require ( github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/iasm v0.9.1 // indirect - github.com/coreos/go-oidc v2.2.1+incompatible github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect @@ -39,6 +34,9 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.16.0 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect @@ -63,6 +61,7 @@ require ( github.com/subosito/gotenv v1.6.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect + go.opencensus.io v0.24.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.6.0 // indirect golang.org/x/crypto v0.16.0 // indirect @@ -70,6 +69,8 @@ require ( golang.org/x/net v0.19.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect + google.golang.org/grpc v1.59.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 50c289a..c8cf9da 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,9 @@ -cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= @@ -11,6 +12,7 @@ github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1 github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE= github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= @@ -20,18 +22,18 @@ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpV github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= -github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= -github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-oidc/v3 v3.9.0 h1:0J/ogVOd4y8P0f0xUh8l9t07xRP/d8tccvjHl2dcsSo= -github.com/coreos/go-oidc/v3 v3.9.0/go.mod h1:rTKz2PYwftcrtoCzV5g5kvfJoWcm0Mk8AF8y1iAQro4= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -42,8 +44,6 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= -github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -54,19 +54,46 @@ github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqR github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8= github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= @@ -91,8 +118,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -109,8 +134,7 @@ github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdU github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pquerna/cachecontrol v0.2.0 h1:vBXSNuE5MYP9IJ5kjsdo8uq+w41jSPgvba2DEnkRx9k= -github.com/pquerna/cachecontrol v0.2.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0= github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= @@ -133,7 +157,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -148,6 +171,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2 github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -158,26 +183,41 @@ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUu golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc= golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -194,14 +234,45 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.153.0 h1:N1AwGhielyKFaUqH07/ZSIQR3uNPcV7NVw0vj+j4iR4= +google.golang.org/api v0.153.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= @@ -211,8 +282,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= -gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= 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= @@ -220,5 +289,7 @@ gorm.io/driver/postgres v1.5.4 h1:Iyrp9Meh3GmbSuyIAGyjkN+n9K+GHX9b9MqsTL4EJCo= gorm.io/driver/postgres v1.5.4/go.mod h1:Bgo89+h0CRcdA33Y6frlaHHVuTdOf87pmyzwW9C/BH0= gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls= gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/internal/auth/auth.dto.go b/internal/auth/auth.dto.go new file mode 100644 index 0000000..6b5e48d --- /dev/null +++ b/internal/auth/auth.dto.go @@ -0,0 +1,20 @@ +package auth + +type RegisterDTO struct { + Gender string `json:"gender"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + Email string `json:"email"` + School string `json:"school"` + BirthDate string `json:"birth_date"` + Address string `json:"address"` + FromAbroad string `json:"from_abroad"` + Allergy string `json:"allergy"` + MedicalCondition string `json:"medical_condition"` + JoinCUReason string `json:"join_cu_reason"` + NewsSource string `json:"news_source"` + Status string `json:"status"` + Grade string `json:"grade"` + DesiredRounds []DesiredRounds `gorm:"foreignKey:UserID"` // One-to-many relationship + InterestedFaculties []InterestedFaculties `gorm:"foreignKey:UserID"` // One-to-many relationship +} \ No newline at end of file diff --git a/internal/auth/auth.handler.go b/internal/auth/auth.handler.go index 47c94ce..3343195 100644 --- a/internal/auth/auth.handler.go +++ b/internal/auth/auth.handler.go @@ -1,16 +1,11 @@ package auth import ( - "context" "net/http" - "strings" "github.com/gin-gonic/gin" - "github.com/isd-sgcu/oph66-backend/cfgldr" - "github.com/dgrijalva/jwt-go" - "golang.org/x/oauth2" - "golang.org/x/oauth2/google" "github.com/isd-sgcu/oph66-backend/apperror" + "github.com/isd-sgcu/oph66-backend/utils" "go.uber.org/zap" ) @@ -22,141 +17,70 @@ type Handler interface { } type handlerImpl struct { - svc Service - cfg *cfgldr.Config + svc Service logger *zap.Logger } -func NewHandler(svc Service, cfg *cfgldr.Config, logger *zap.Logger) Handler { +func NewHandler(svc Service, logger *zap.Logger) Handler { return &handlerImpl{ - svc: svc, - cfg: cfg, - logger: logger, + svc, + logger, } } func (h *handlerImpl) GoogleLogin(c *gin.Context) { - oauthConfig := h.initializeOAuthConfig() - - url := oauthConfig.AuthCodeURL("state", oauth2.AccessTypeOffline) + url, apperr := h.svc.GoogleLogin() + if apperr != nil { + utils.ReturnError(c, apperr) + return + } c.Redirect(http.StatusTemporaryRedirect, url) + c.JSON(http.StatusOK, gin.H{"message": "GoogleLogin successful"}) } func (h *handlerImpl) GoogleCallback(c *gin.Context) { code := c.Query("code") - oauthConfig := h.initializeOAuthConfig() - - token, err := oauthConfig.Exchange(context.Background(), code) - if err != nil { - h.logger.Error("Failed to exchange code for token", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to exchange code for token"}) - return - } - - idToken := token.Extra("id_token") - if idToken == nil { - h.logger.Error("ID token not found") - c.JSON(http.StatusInternalServerError, gin.H{"error": "ID token not found"}) + token , apperr := h.svc.GoogleCallback(code) + if apperr != nil { + utils.ReturnError(c, apperr) return } - - c.JSON(http.StatusOK, gin.H{ - "access_token": token.AccessToken, - "id_token": idToken.(string), - }) -} - -func (h *handlerImpl) initializeOAuthConfig() *oauth2.Config { - return &oauth2.Config{ - ClientID: h.cfg.OAuth2Config.ClientID, - ClientSecret: h.cfg.OAuth2Config.ClientSecret, - RedirectURL: h.cfg.OAuth2Config.RedirectURL, - Scopes: h.cfg.OAuth2Config.Scopes, - Endpoint: google.Endpoint, - } + c.JSON(http.StatusOK, gin.H{"message": "GoogleCallback successful", "token": token}) } func (h *handlerImpl) Register(c *gin.Context) { - var user User + var data RegisterDTO - if err := c.ShouldBindJSON(&user); err != nil { - h.logger.Error("Failed to bind JSON", zap.Error(err)) - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + if err := c.ShouldBindJSON(&data); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to bind JSON"}) return } - if err := h.svc.CreateUser(&user); err != nil { - h.logger.Error("Failed to create user", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create user"}) + user, apperr := h.svc.Register(&data) + if apperr != nil { + utils.ReturnError(c, apperr) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Registration failed"}) return } - c.JSON(http.StatusCreated, gin.H{"message": "User created successfully", "user": user, "id": user.ID}) + c.JSON(http.StatusCreated, gin.H{"message": "User registered successfully", "user": user}) } -func (h *handlerImpl) GetProfile(c *gin.Context) { - authHeader := c.GetHeader("Authorization") - if authHeader == "" { - h.logger.Error("Authorization header is missing") - c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header is missing"}) - return - } - - email, err := h.extractEmailFromJWTToken(authHeader) - if err != nil { - h.logger.Error("Invalid token", zap.Error(err)) - c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()}) // Return specific error message - return - } - user, err := h.svc.GetUserByEmail(email) - if err != nil { - h.logger.Error("Failed to get user", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get user"}) - return - } - - DesiredRounds, err := h.svc.GetDesiredRoundsByUserId(user.ID) - if err != nil { - h.logger.Error("Failed to get desired rounds", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get desired rounds"}) +func (h *handlerImpl) GetProfile(c *gin.Context) { + authHeader := c.GetHeader("Authorization") + if authHeader == "" { + utils.ReturnError(c, apperror.Unauthorized) + c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header is missing"}) return } - InterestedFaculties, err := h.svc.GetInterestedFacultiesByUserId(user.ID) - if err != nil { - h.logger.Error("Failed to get interested faculties", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get interested faculties"}) + user, apperr := h.svc.GetUserFromJWTToken(authHeader) + if apperr != nil { + utils.ReturnError(c, apperr) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get user from JWT token"}) return } - - - user.DesiredRounds = DesiredRounds - user.InterestedFaculties = InterestedFaculties c.JSON(http.StatusOK, gin.H{"user": user, "id": user.ID}) -} - -func (h *handlerImpl) extractEmailFromJWTToken(tokenString string) (string, *apperror.AppError) { - tokenString = strings.Replace(tokenString, "Bearer ", "", 1) - - token, _, err := new(jwt.Parser).ParseUnverified(tokenString, jwt.MapClaims{}) - if err != nil { - h.logger.Error("could not parse token", zap.Error(err)) - return "", apperror.InvalidToken - } - - claims, ok := token.Claims.(jwt.MapClaims) - if !ok { - h.logger.Error("invalid token claims") - return "", apperror.InvalidToken - } - - email, ok := claims["email"].(string) - if !ok || email == "" { - h.logger.Error("email not found in token claims") - return "", apperror.InvalidToken - } - - return email, nil } \ No newline at end of file diff --git a/internal/auth/auth.model.go b/internal/auth/auth.model.go index 7892c6a..ad1d6fd 100644 --- a/internal/auth/auth.model.go +++ b/internal/auth/auth.model.go @@ -1,7 +1,7 @@ package auth type User struct { - ID uint `gorm:"primaryKey;autoIncrement" json:"-"` + ID uint `gorm:"primaryKey;autoIncrement" json:"id"` Gender string `json:"gender"` FirstName string `json:"first_name"` LastName string `json:"last_name"` diff --git a/internal/auth/auth.repository.go b/internal/auth/auth.repository.go index c0a2db5..fa85e9f 100644 --- a/internal/auth/auth.repository.go +++ b/internal/auth/auth.repository.go @@ -5,10 +5,9 @@ import ( ) type Repository interface { - Create(result *User) error - GetByEmail(email string) (*User, error) - GetInterestedFacultiesByUserId(id uint) ([]InterestedFaculties, error) - GetDesiredRoundsByUserId(id uint) ([]DesiredRounds, error) + CreateUser(user *User) (*User, error) + UpdateUser(id uint, user *User) (*User, error) + GetUserByEmail(email string) (*User, error) } type repositoryImpl struct { @@ -20,30 +19,67 @@ func NewRepository(db *gorm.DB) Repository { db, } } -func (r *repositoryImpl) Create(user *User) error { - return r.db.Create(user).Error -} -func (r *repositoryImpl) GetByEmail(email string) (*User, error) { - var user User - if err := r.db.Where("email = ?", email).First(&user).Error; err != nil { +func (r *repositoryImpl) CreateUser(user *User) (*User, error) { + if err := r.db.Create(user).Error; err != nil { return nil, err } - return &user, nil + return user, nil } -func (r *repositoryImpl) GetInterestedFacultiesByUserId(id uint) ([]InterestedFaculties, error) { - var interestedFaculties []InterestedFaculties - if err := r.db.Where("user_id = ?", id).Find(&interestedFaculties).Error; err != nil { +func (r *repositoryImpl) UpdateUser(id uint, user *User) (*User, error) { + tx := r.db.Begin() + + var existingUser User + if err := tx.Where("id = ?", id).Preload("InterestedFaculties").Preload("DesiredRounds").First(&existingUser).Error; err != nil { + tx.Rollback() + return nil, err + } + + if err := tx.Where("user_id = ?", id).Delete(&InterestedFaculties{}).Error; err != nil { + tx.Rollback() return nil, err } - return interestedFaculties, nil -} -func (r *repositoryImpl) GetDesiredRoundsByUserId(id uint) ([]DesiredRounds, error) { - var desiredRounds []DesiredRounds - if err := r.db.Where("user_id = ?", id).Find(&desiredRounds).Error; err != nil { + if err := tx.Where("user_id = ?", id).Delete(&DesiredRounds{}).Error; err != nil { + tx.Rollback() return nil, err } - return desiredRounds, nil -} \ No newline at end of file + + existingUser.FirstName = user.FirstName + existingUser.LastName = user.LastName + existingUser.Email = user.Email + existingUser.School = user.School + existingUser.BirthDate = user.BirthDate + existingUser.Address = user.Address + existingUser.FromAbroad = user.FromAbroad + existingUser.Allergy = user.Allergy + existingUser.MedicalCondition = user.MedicalCondition + existingUser.JoinCUReason = user.JoinCUReason + existingUser.NewsSource = user.NewsSource + existingUser.Status = user.Status + existingUser.Grade = user.Grade + + existingUser.InterestedFaculties = user.InterestedFaculties + existingUser.DesiredRounds = user.DesiredRounds + + if err := tx.Save(&existingUser).Error; err != nil { + tx.Rollback() + return nil, err + } + + if err := tx.Commit().Error; err != nil { + return nil, err + } + + return &existingUser, nil +} + +func (r *repositoryImpl) GetUserByEmail(email string) (*User, error) { + var user *User + if err := r.db.Where("email = ?", email).Preload("InterestedFaculties").Preload("DesiredRounds").First(&user).Error; err != nil { + return nil, err + } + + return user, nil +} diff --git a/internal/auth/auth.service.go b/internal/auth/auth.service.go index 77e6ab3..c860724 100644 --- a/internal/auth/auth.service.go +++ b/internal/auth/auth.service.go @@ -1,62 +1,140 @@ package auth import ( + "context" + "strings" + "github.com/isd-sgcu/oph66-backend/apperror" + "github.com/isd-sgcu/oph66-backend/cfgldr" "go.uber.org/zap" + "golang.org/x/oauth2" + "google.golang.org/api/idtoken" + "golang.org/x/oauth2/google" ) type Service interface { - CreateUser(user *User) *apperror.AppError - GetUserByEmail(email string) (*User, *apperror.AppError) - GetInterestedFacultiesByUserId(id uint) ([]InterestedFaculties, *apperror.AppError) - GetDesiredRoundsByUserId(id uint) ([]DesiredRounds, *apperror.AppError) + GoogleLogin() (string, *apperror.AppError) + GoogleCallback(code string) (string, *apperror.AppError) + Register (data *RegisterDTO) (*User, *apperror.AppError) + GetUserFromJWTToken(tokenString string) (*User, *apperror.AppError) } -func NewService(repo Repository, logger *zap.Logger) Service { +func NewService(repo Repository, logger *zap.Logger, cfg *cfgldr.Config) Service { + oauthConfig := &oauth2.Config{ + ClientID: cfg.OAuth2Config.ClientID, + ClientSecret: cfg.OAuth2Config.ClientSecret, + RedirectURL: cfg.OAuth2Config.RedirectURL, + Scopes: cfg.OAuth2Config.Scopes, + Endpoint: google.Endpoint, + } return &serviceImpl{ repo, + cfg, logger, + oauthConfig, } } - type serviceImpl struct { - repo Repository - logger *zap.Logger + repo Repository + cfg *cfgldr.Config + logger *zap.Logger + oauthConfig *oauth2.Config } -func (s *serviceImpl) CreateUser(user *User) *apperror.AppError { - err := s.repo.Create(user) - if err!= nil { - s.logger.Error("could not create user in database", zap.Error(err)) - return apperror.InternalError +func (s *serviceImpl) GoogleLogin() (string, *apperror.AppError) { + url := s.oauthConfig.AuthCodeURL("state") + if url == "" { + s.logger.Error("Failed to get AuthCodeURL") + return "", apperror.InternalError } - return nil + return url , nil } -func (s *serviceImpl) GetUserByEmail(email string) (*User, *apperror.AppError) { - user , err := s.repo.GetByEmail(email) +func (s *serviceImpl) GoogleCallback(code string) (string, *apperror.AppError) { + token, err := s.oauthConfig.Exchange(context.Background(), code) if err != nil { - s.logger.Error("could not retrieve user from database", zap.Error(err)) - return nil, apperror.InternalError + s.logger.Error("Failed to exchange code for token", zap.Error(err)) + return "", apperror.InternalError } - return user, nil + + idToken := token.Extra("id_token") + if idToken == nil { + s.logger.Error("ID token not found") + return "", apperror.InternalError + } + + + return idToken.(string), nil +} + +func (s *serviceImpl) Register(data *RegisterDTO) (*User, *apperror.AppError) { + user, err := s.repo.GetUserByEmail(data.Email) + dataUser := ConvertRegisterDTOToUser(data) + + if err == nil { + user, err := s.repo.UpdateUser(user.ID, dataUser) + if err != nil { + s.logger.Error("Failed to update user", zap.Error(err)) + return nil, apperror.InternalError + } + return user, nil + } else { + user, err := s.repo.CreateUser(dataUser) + if err != nil { + s.logger.Error("Failed to create user", zap.Error(err)) + return nil, apperror.InternalError + } + return user, nil +} } -func (s *serviceImpl) GetInterestedFacultiesByUserId(id uint) ([]InterestedFaculties, *apperror.AppError) { - user , err := s.repo.GetInterestedFacultiesByUserId(id) +func (s *serviceImpl) GetUserFromJWTToken(tokenString string) (*User, *apperror.AppError) { + if !strings.HasPrefix(tokenString, "Bearer ") { + return nil, apperror.InvalidToken + } + + tokenString = strings.Replace(tokenString, "Bearer ", "", 1) + + token, err := idtoken.Validate(context.Background(), tokenString, s.cfg.OAuth2Config.ClientID) if err != nil { - s.logger.Error("could not retrieve interested faculties from database", zap.Error(err)) - return nil, apperror.InternalError + s.logger.Error("Failed to validate token", zap.Error(err)) + return nil, apperror.InvalidToken } - return user, nil -} -func (s *serviceImpl) GetDesiredRoundsByUserId(id uint) ([]DesiredRounds, *apperror.AppError) { - user , err := s.repo.GetDesiredRoundsByUserId(id) + email, ok := token.Claims["email"].(string) + if !ok || email == "" { + s.logger.Error("Email not found in token claims") + return nil, apperror.InvalidToken + } + + user, err := s.repo.GetUserByEmail(email) if err != nil { - s.logger.Error("could not retrieve desired rounds from database", zap.Error(err)) + s.logger.Error("Could not retrieve user from database", zap.Error(err)) return nil, apperror.InternalError } + return user, nil } + + +func ConvertRegisterDTOToUser(dto *RegisterDTO) *User { + return &User{ + Gender: dto.Gender, + FirstName: dto.FirstName, + LastName: dto.LastName, + Email: dto.Email, + School: dto.School, + BirthDate: dto.BirthDate, + Address: dto.Address, + FromAbroad: dto.FromAbroad, + Allergy: dto.Allergy, + MedicalCondition: dto.MedicalCondition, + JoinCUReason: dto.JoinCUReason, + NewsSource: dto.NewsSource, + Status: dto.Status, + Grade: dto.Grade, + DesiredRounds: dto.DesiredRounds, + InterestedFaculties: dto.InterestedFaculties, + } +} \ No newline at end of file diff --git a/internal/event/event.service.go b/internal/event/event.service.go index ba1a3b6..41e0f52 100644 --- a/internal/event/event.service.go +++ b/internal/event/event.service.go @@ -48,4 +48,4 @@ func (s *serviceImpl) GetEventById(eventId string) (Event, *apperror.AppError) { } return result, nil -} \ No newline at end of file +} diff --git a/migrate.sh b/migrate.sh index 0a73b1b..cba99a2 100755 --- a/migrate.sh +++ b/migrate.sh @@ -24,7 +24,7 @@ docker run \ INSERT INTO faculties (code, name_en, name_th) VALUES (23, 'Faculty of Science', 'คณะวิทยาศาสตร์'); INSERT INTO faculties (code, name_en, name_th) VALUES (34, 'Faculty of Law', 'คณะนิติศาสตร์'); - CREATE TABLE events ( + CREATE TABLE events ( id VARCHAR(128) PRIMARY KEY, name_en VARCHAR(128) NOT NULL, name_th VARCHAR(128) NOT NULL, @@ -118,12 +118,5 @@ docker run \ '2024-01-21 09:00:00+00' ); - CREATE TABLE desired_rounds ( - id SERIAL PRIMARY KEY, - round VARCHAR(128) NOT NULL, - user_id BIGINT REFERENCES users(id) -); - - \" | psql postgres://postgres:123456@host.docker.internal:5432/postgres " \ No newline at end of file diff --git a/schema.sql b/schema.sql new file mode 100644 index 0000000..251ae35 --- /dev/null +++ b/schema.sql @@ -0,0 +1,85 @@ +-- Create "feature_flags" table +CREATE TABLE "feature_flags" ( + "key" character varying(50) NOT NULL, + "value" boolean NOT NULL, + "cache_duration" integer NOT NULL, + PRIMARY KEY ("key") +); + +-- Create "users" table +CREATE TABLE "users" ( + "id" bigserial NOT NULL, + "gender" text NULL, + "first_name" text NULL, + "last_name" text NULL, + "email" text NULL, + "school" text NULL, + "birth_date" text NULL, + "address" text NULL, + "from_abroad" text NULL, + "allergy" text NULL, + "medical_condition" text NULL, + "join_cu_reason" text NULL, + "news_source" text NULL, + "status" text NULL, + "grade" text NULL, + PRIMARY KEY ("id") +); + +-- Create "desired_rounds" table +CREATE TABLE "desired_rounds" ( + "id" serial NOT NULL, + "round" character varying(128) NOT NULL, + "user_id" bigint NULL, + PRIMARY KEY ("id"), + CONSTRAINT "desired_rounds_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION +); + +-- Create "faculties" table +CREATE TABLE "faculties" ( + "code" smallint NOT NULL, + "name_en" character varying(128) NOT NULL, + "name_th" character varying(128) NOT NULL, + PRIMARY KEY ("code") +); + +-- Create "events" table +CREATE TABLE "events" ( + "id" character varying(128) NOT NULL, + "name_en" character varying(128) NOT NULL, + "name_th" character varying(128) NOT NULL, + "faculty_code" smallint NOT NULL, + "department_en" character varying(128) NOT NULL, + "department_th" character varying(128) NOT NULL, + "require_registration" boolean NOT NULL, + "max_capacity" integer NULL, + "location_en" character varying(128) NOT NULL, + "location_th" character varying(128) NOT NULL, + "description_en" character varying(2048) NULL, + "description_th" character varying(2048) NULL, + PRIMARY KEY ("id"), + CONSTRAINT "events_faculty_code_fkey" FOREIGN KEY ("faculty_code") REFERENCES "faculties" ("code") ON UPDATE NO ACTION ON DELETE NO ACTION +); + +-- Create "interested_faculties" table +CREATE TABLE "interested_faculties" ( + "id" bigserial NOT NULL, + "faculty" text NULL, + "department" text NULL, + "section" text NULL, + "user_id" bigint NULL, + PRIMARY KEY ("id"), + CONSTRAINT "fk_users_interested_faculties" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION +); + +-- Create index "idx_interested_faculties_user_id" to table: "interested_faculties" +CREATE INDEX "idx_interested_faculties_user_id" ON "interested_faculties" ("user_id"); + +-- Create "schedules" table +CREATE TABLE "schedules" ( + "event_id" character varying(128) NOT NULL, + "starts_at" timestamptz NOT NULL, + "ends_at" timestamptz NOT NULL, + PRIMARY KEY ("event_id", "starts_at", "ends_at"), + CONSTRAINT "schedules_event_id_fkey" FOREIGN KEY ("event_id") REFERENCES "events" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION +); \ No newline at end of file