From 0e7f0a2b684ab5836130ca4bf3de0f2bd2445804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A7=E9=A9=8A?= Date: Wed, 12 Feb 2025 09:51:46 +0800 Subject: [PATCH] feat: add token repository --- .gitignore | 2 +- build/Dockerfile | 22 +- chart/readme.md | 1 - generate/protobuf/permission.proto | 2 +- go.mod | 69 +- go.sum | 277 +++ internal/config/config.go | 17 +- internal/domain/const.go | 18 - internal/domain/errors.go | 89 - internal/domain/permission.go | 45 - internal/domain/repository/error.go | 66 - internal/domain/repository/member_status.go | 16 - internal/domain/repository/token.go | 31 - internal/domain/usecase/bitmap.go | 16 - internal/domain/usecase/opa.go | 47 - internal/domain/usecase/permission_tree.go | 33 - internal/entity/claims.go | 8 - internal/entity/token.go | 50 - internal/lib/metric/app.go | 30 - .../cancel_one_time_token_logic.go | 26 +- .../cancel_token_by_device_id_logic.go | 24 +- .../logic/tokenservice/cancel_token_logic.go | 47 +- .../logic/tokenservice/cancel_tokens_logic.go | 32 +- .../get_user_tokens_by_device_id_logic.go | 38 +- .../get_user_tokens_by_uid_logic.go | 38 +- .../tokenservice/new_one_time_token_logic.go | 50 +- .../logic/tokenservice/new_token_logic.go | 118 +- .../logic/tokenservice/refresh_token_logic.go | 87 +- internal/logic/tokenservice/utils_claims.go | 55 - internal/logic/tokenservice/utils_jwt.go | 105 - .../tokenservice/validation_token_logic.go | 49 +- internal/mock/model/permission_model.go | 115 -- internal/mock/model/permission_model_gen.go | 115 -- internal/mock/model/role_model.go | 130 -- internal/mock/model/role_model_gen.go | 130 -- internal/mock/model/role_permission_model.go | 100 - .../mock/model/role_permission_model_gen.go | 100 - internal/mock/model/user_role_model.go | 115 -- internal/mock/model/user_role_model_gen.go | 115 -- internal/model/permission_model.go | 27 - internal/model/permission_model_gen.go | 157 -- internal/model/role_model.go | 27 - internal/model/role_model_gen.go | 179 -- internal/model/role_permission_model.go | 27 - internal/model/role_permission_model_gen.go | 118 -- internal/model/user_role_model.go | 27 - internal/model/user_role_model_gen.go | 155 -- internal/model/vars.go | 5 - internal/repository/member_status.go | 104 - internal/repository/token.go | 312 --- .../tokenservice/token_service_server.go | 29 +- internal/svc/service_context.go | 33 +- internal/usecase/bitmap.go | 117 -- internal/usecase/bitmap_benchmark_test.go | 69 - internal/usecase/bitmap_test.go | 78 - permission.go | 10 +- pkg/domain/entity/ticket.go | 6 + pkg/domain/entity/token.go | 56 + pkg/domain/entity/token_test.go | 163 ++ pkg/domain/error.go | 1 + {internal => pkg}/domain/redis.go | 19 +- pkg/domain/repository/token.go | 49 + pkg/domain/usecase/token.go | 40 + pkg/repository/token.go | 331 ++++ pkg/repository/token_test.go | 1749 +++++++++++++++++ 65 files changed, 2795 insertions(+), 3521 deletions(-) delete mode 100644 chart/readme.md create mode 100644 go.sum delete mode 100644 internal/domain/const.go delete mode 100644 internal/domain/errors.go delete mode 100644 internal/domain/permission.go delete mode 100644 internal/domain/repository/error.go delete mode 100644 internal/domain/repository/member_status.go delete mode 100644 internal/domain/repository/token.go delete mode 100644 internal/domain/usecase/bitmap.go delete mode 100644 internal/domain/usecase/opa.go delete mode 100644 internal/domain/usecase/permission_tree.go delete mode 100644 internal/entity/claims.go delete mode 100644 internal/entity/token.go delete mode 100644 internal/lib/metric/app.go delete mode 100644 internal/logic/tokenservice/utils_claims.go delete mode 100644 internal/logic/tokenservice/utils_jwt.go delete mode 100644 internal/mock/model/permission_model.go delete mode 100644 internal/mock/model/permission_model_gen.go delete mode 100644 internal/mock/model/role_model.go delete mode 100644 internal/mock/model/role_model_gen.go delete mode 100644 internal/mock/model/role_permission_model.go delete mode 100644 internal/mock/model/role_permission_model_gen.go delete mode 100644 internal/mock/model/user_role_model.go delete mode 100644 internal/mock/model/user_role_model_gen.go delete mode 100755 internal/model/permission_model.go delete mode 100755 internal/model/permission_model_gen.go delete mode 100755 internal/model/role_model.go delete mode 100755 internal/model/role_model_gen.go delete mode 100755 internal/model/role_permission_model.go delete mode 100755 internal/model/role_permission_model_gen.go delete mode 100755 internal/model/user_role_model.go delete mode 100755 internal/model/user_role_model_gen.go delete mode 100644 internal/model/vars.go delete mode 100644 internal/repository/member_status.go delete mode 100644 internal/repository/token.go delete mode 100644 internal/usecase/bitmap.go delete mode 100644 internal/usecase/bitmap_benchmark_test.go delete mode 100644 internal/usecase/bitmap_test.go create mode 100644 pkg/domain/entity/ticket.go create mode 100644 pkg/domain/entity/token.go create mode 100644 pkg/domain/entity/token_test.go create mode 100644 pkg/domain/error.go rename {internal => pkg}/domain/redis.go (79%) create mode 100644 pkg/domain/repository/token.go create mode 100644 pkg/domain/usecase/token.go create mode 100644 pkg/repository/token.go create mode 100644 pkg/repository/token_test.go diff --git a/.gitignore b/.gitignore index 602c99c..7f24146 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .idea/ -go.sum +old/go.sum account/ gen_result/ etc/permission.yaml diff --git a/build/Dockerfile b/build/Dockerfile index 78cd457..844dc71 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -2,12 +2,11 @@ # BUILDER # ########### -FROM golang:1.22.3 as builder +FROM golang:1.23.4 AS builder ARG VERSION ARG BUILT ARG GIT_COMMIT -ARG SSH_PRV_KEY # private go packages ENV GOPRIVATE=code.30cm.net @@ -17,22 +16,19 @@ COPY . . RUN apt-get update && \ - apt-get install git + apt-get install -y git && \ + mkdir /root/.ssh # Make the root foler for our ssh -RUN mkdir -p /root/.ssh && \ - chmod 0700 /root/.ssh && \ - ssh-keyscan git.30cm.net > /root/.ssh/known_hosts && \ - echo "$SSH_PRV_KEY" > /root/.ssh/id_rsa && \ - chmod 600 /root/.ssh/id_rsa - +RUN --mount=type=secret,id=ssh_key,dst=/root/.ssh/id_rsa \ + ssh-keyscan git.30cm.net >> /root/.ssh/known_hosts RUN --mount=type=ssh go mod download RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ -ldflags "$FLAG" \ - -o service + -o permission ########## ## FINAL # @@ -41,7 +37,7 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ FROM gcr.io/distroless/static-debian11 WORKDIR /app -COPY --from=builder /app/service /app/service -COPY --from=builder /app/etc/service.yaml /app/etc/service.yaml +COPY --from=builder /app/permission /app/permission +COPY --from=builder /app/etc/permission.yaml /app/etc/permission.yaml EXPOSE 8080 -CMD ["/app/service"] \ No newline at end of file +CMD ["/app/permission"] \ No newline at end of file diff --git a/chart/readme.md b/chart/readme.md deleted file mode 100644 index 33c4f67..0000000 --- a/chart/readme.md +++ /dev/null @@ -1 +0,0 @@ -// TODO 未來要放 helm 的地方 \ No newline at end of file diff --git a/generate/protobuf/permission.proto b/generate/protobuf/permission.proto index 62f547e..148c629 100644 --- a/generate/protobuf/permission.proto +++ b/generate/protobuf/permission.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package permission; -option go_package="./permission"; +option go_package="./app-cloudep-permission-server"; // OKResp message OKResp {} diff --git a/go.mod b/go.mod index d04251f..b0bd8e2 100644 --- a/go.mod +++ b/go.mod @@ -1,22 +1,17 @@ -module app-cloudep-permission-server +module code.30cm.net/digimon/app-cloudep-permission-server -go 1.22.3 +go 1.23.4 require ( - code.30cm.net/digimon/library-go/errors v1.0.1 - code.30cm.net/digimon/library-go/utils/invited_code v1.0.2 - code.30cm.net/digimon/library-go/validator v1.0.0 - github.com/golang-jwt/jwt/v4 v4.5.0 - github.com/google/uuid v1.6.0 - github.com/stretchr/testify v1.9.0 - github.com/zeromicro/go-zero v1.7.0 - go.uber.org/mock v0.4.0 - google.golang.org/grpc v1.65.0 - google.golang.org/protobuf v1.34.2 + github.com/alicebob/miniredis/v2 v2.34.0 + github.com/stretchr/testify v1.10.0 + github.com/zeromicro/go-zero v1.8.0 + google.golang.org/grpc v1.70.0 + google.golang.org/protobuf v1.36.5 ) require ( - filippo.io/edwards25519 v1.1.0 // indirect + github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect @@ -25,27 +20,23 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/fatih/color v1.17.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.4 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.22.0 // indirect - github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/leodido/go-urn v1.4.0 // indirect + github.com/klauspost/compress v1.17.9 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -55,39 +46,39 @@ require ( github.com/openzipkin/zipkin-go v0.4.3 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.19.1 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.48.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect - github.com/redis/go-redis/v9 v9.6.1 // indirect + github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/redis/go-redis/v9 v9.7.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/yuin/gopher-lua v1.1.1 // indirect go.etcd.io/etcd/api/v3 v3.5.15 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect go.etcd.io/etcd/client/v3 v3.5.15 // indirect - go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel v1.32.0 // indirect go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 // indirect go.opentelemetry.io/otel/exporters/zipkin v1.24.0 // indirect - go.opentelemetry.io/otel/metric v1.24.0 // indirect - go.opentelemetry.io/otel/sdk v1.24.0 // indirect - go.opentelemetry.io/otel/trace v1.24.0 // indirect + go.opentelemetry.io/otel/metric v1.32.0 // indirect + go.opentelemetry.io/otel/sdk v1.32.0 // indirect + go.opentelemetry.io/otel/trace v1.32.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/atomic v1.10.0 // indirect - go.uber.org/automaxprocs v1.5.3 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.9.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/crypto v0.25.0 // indirect - golang.org/x/net v0.27.0 // indirect - golang.org/x/oauth2 v0.20.0 // indirect - golang.org/x/sys v0.22.0 // indirect - golang.org/x/term v0.22.0 // indirect - golang.org/x/text v0.16.0 // indirect - golang.org/x/time v0.5.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + golang.org/x/net v0.34.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/term v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect + golang.org/x/time v0.9.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..6c845c5 --- /dev/null +++ b/go.sum @@ -0,0 +1,277 @@ +github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE= +github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= +github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0= +github.com/alicebob/miniredis/v2 v2.34.0/go.mod h1:kWShP4b58T1CW0Y5dViCd5ztzrDqRWqM3nksiyXk5s8= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/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= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= +github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +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/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/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +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/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= +github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= +github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= +github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= +github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +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/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +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= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= +github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= +github.com/zeromicro/go-zero v1.8.0 h1:4g/8VW+fOyM51HZYPeI3mXIZdEX+Fl6SsdYX2H5PYw4= +github.com/zeromicro/go-zero v1.8.0/go.mod h1:xDBF+/iDzj30zPvu6HNUIbpz1J6+/g3Sx9D/DytJfss= +go.etcd.io/etcd/api/v3 v3.5.15 h1:3KpLJir1ZEBrYuV2v+Twaa/e2MdDCEZ/70H+lzEiwsk= +go.etcd.io/etcd/api/v3 v3.5.15/go.mod h1:N9EhGzXq58WuMllgH9ZvnEr7SI9pS0k0+DHZezGp7jM= +go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5fWlA= +go.etcd.io/etcd/client/pkg/v3 v3.5.15/go.mod h1:mXDI4NAOwEiszrHCb0aqfAYNCrZP4e9hRca3d1YK8EU= +go.etcd.io/etcd/client/v3 v3.5.15 h1:23M0eY4Fd/inNv1ZfU3AxrbbOdW79r9V9Rl62Nm6ip4= +go.etcd.io/etcd/client/v3 v3.5.15/go.mod h1:CLSJxrYjvLtHsrPKsy7LmZEE+DK2ktfd2bN4RhBMwlU= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4= +go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 h1:s0PHtIkN+3xrbDOpt2M8OTG92cWqUESvzh2MxiR5xY8= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0/go.mod h1:hZlFbDbRt++MMPCCfSJfmhkGIWnX1h3XjkfxZUjLrIA= +go.opentelemetry.io/otel/exporters/zipkin v1.24.0 h1:3evrL5poBuh1KF51D9gO/S+N/1msnm4DaBqs/rpXUqY= +go.opentelemetry.io/otel/exporters/zipkin v1.24.0/go.mod h1:0EHgD8R0+8yRhUYJOGR8Hfg2dpiJQxDOszd5smVO9wM= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a h1:OAiGFfOiA0v9MRYsSidp3ubZaBnteRUyn3xB2ZQ5G/E= +google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a/go.mod h1:jehYqy3+AhJU9ve55aNOaSml7wUXjF9x6z2LcCfpAhY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +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/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= +gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +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= +k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw= +k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80= +k8s.io/apimachinery v0.29.4 h1:RaFdJiDmuKs/8cm1M6Dh1Kvyh59YQFDcFuFTSmXes6Q= +k8s.io/apimachinery v0.29.4/go.mod h1:i3FJVwhvSp/6n8Fl4K97PJEP8C+MM+aoDq4+ZJBf70Y= +k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg= +k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0= +k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= +k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/internal/config/config.go b/internal/config/config.go index 95a9aab..c1f85b9 100755 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,22 +1,7 @@ package config -import ( - "time" - - "github.com/zeromicro/go-zero/core/stores/redis" - "github.com/zeromicro/go-zero/zrpc" -) +import "github.com/zeromicro/go-zero/zrpc" type Config struct { zrpc.RpcServerConf - RedisCluster redis.RedisConf - Token struct { - RefreshExpires time.Duration - Expired time.Duration - Secret string - } - // 加上DB結構體 - DB struct { - DsnString string - } } diff --git a/internal/domain/const.go b/internal/domain/const.go deleted file mode 100644 index 82ac1b8..0000000 --- a/internal/domain/const.go +++ /dev/null @@ -1,18 +0,0 @@ -package domain - -type GrantType string - -const ( - PasswordCredentials GrantType = "password" - ClientCredentials GrantType = "client_credentials" - Refreshing GrantType = "refresh_token" -) - -const ( - // DefaultRole 預設role - DefaultRole = "user" -) - -const ( - TokenTypeBearer = "Bearer" -) diff --git a/internal/domain/errors.go b/internal/domain/errors.go deleted file mode 100644 index 14bb3ba..0000000 --- a/internal/domain/errors.go +++ /dev/null @@ -1,89 +0,0 @@ -package domain - -import ( - mts "app-cloudep-permission-server/internal/lib/metric" - - ers "code.30cm.net/digimon/library-go/errors" - "code.30cm.net/digimon/library-go/errors/code" -) - -// 12 represents Scope -// 100 represents Category -// 9 represents Detail error code -// full code 12009 只會有 系統以及錯誤碼,category 是給系統判定用的 -// 目前 Scope 以及分類要系統共用,係向的錯誤各自服務實作就好 - -// token error 方面 -const ( - TokenUnexpectedSigningErrorCode = iota + 1 - TokenValidateErrorCode - TokenClaimErrorCode -) - -const ( - RedisDelErrorCode = iota + 20 - RedisPipLineErrorCode - RedisErrorCode -) - -const ( - PermissionNotFoundCode = iota + 30 - PermissionGetDataErrorCode -) - -// TokenUnexpectedSigningErr 30001 Token 簽名錯誤 -func TokenUnexpectedSigningErr(msg string) *ers.LibError { - mts.AppErrorMetrics.AddFailure("token", "token_unexpected_sign") - - return ers.NewErr(code.CloudEPPermission, code.CatInput, TokenUnexpectedSigningErrorCode, msg) -} - -// TokenTokenValidateErr 30002 Token 驗證錯誤 -func TokenTokenValidateErr(msg string) *ers.LibError { - mts.AppErrorMetrics.AddFailure("token", "token_validate_ilegal") - - return ers.NewErr(code.CloudEPPermission, code.CatInput, TokenValidateErrorCode, msg) -} - -// TokenClaimError 30003 Token 驗證錯誤 -func TokenClaimError(msg string) *ers.LibError { - mts.AppErrorMetrics.AddFailure("token", "token_claim_error") - - return ers.NewErr(code.CloudEPPermission, code.CatInput, TokenClaimErrorCode, msg) -} - -// RedisDelError 30020 Redis 刪除錯誤 -func RedisDelError(msg string) *ers.LibError { - // 看需要建立哪些 Metrics - mts.AppErrorMetrics.AddFailure("redis", "del_error") - - return ers.NewErr(code.CloudEPPermission, code.CatDB, RedisDelErrorCode, msg) -} - -// RedisPipLineError 30021 Redis PipLine 錯誤 -func RedisPipLineError(msg string) *ers.LibError { - // 看需要建立哪些 Metrics - mts.AppErrorMetrics.AddFailure("redis", "pip_line_error") - - return ers.NewErr(code.CloudEPPermission, code.CatInput, RedisPipLineErrorCode, msg) -} - -// RedisError 30022 Redis 錯誤 -func RedisError(msg string) *ers.LibError { - // 看需要建立哪些 Metrics - mts.AppErrorMetrics.AddFailure("redis", "error") - - return ers.NewErr(code.CloudEPPermission, code.CatInput, RedisErrorCode, msg) -} - -// PermissionNotFoundError 30030 權限錯誤 -func PermissionNotFoundError(msg string) *ers.LibError { - // 看需要建立哪些 Metrics - return ers.NewErr(code.CloudEPPermission, code.Forbidden, PermissionNotFoundCode, msg) -} - -// PermissionGetDataError 30031 解析權限時錯誤 -func PermissionGetDataError(msg string) *ers.LibError { - // 看需要建立哪些 Metrics - return ers.NewErr(code.CloudEPPermission, code.InvalidFormat, PermissionGetDataErrorCode, msg) -} diff --git a/internal/domain/permission.go b/internal/domain/permission.go deleted file mode 100644 index c4cac51..0000000 --- a/internal/domain/permission.go +++ /dev/null @@ -1,45 +0,0 @@ -package domain - -type PermissionType int8 - -const ( - PermissionTypeBackendUser PermissionType = iota + 1 - PermissionTypeFrontendUser -) - -type PermissionTypeCode string - -const ( - PermissionTypeBackCode PermissionTypeCode = "back" - PermissionTypeFrontCode PermissionTypeCode = "front" -) - -var permissionMap = map[int64]PermissionTypeCode{ - 1: PermissionTypeFrontCode, - 2: PermissionTypeBackCode, -} - -func ToPermissionTypeCode(code int64) (PermissionTypeCode, bool) { - result, ok := permissionMap[code] - if !ok { - return "", false - } - - return result, true -} - -func (t *PermissionTypeCode) ToString() string { - return string(*t) -} - -type PermissionStatus string -type Permissions map[string]PermissionStatus - -const ( - PermissionStatusOpenCode PermissionStatus = "open" - PermissionStatusCloseCode PermissionStatus = "close" -) - -const ( - AdminRoleID = "GodDog!@#" -) diff --git a/internal/domain/repository/error.go b/internal/domain/repository/error.go deleted file mode 100644 index 82bdda6..0000000 --- a/internal/domain/repository/error.go +++ /dev/null @@ -1,66 +0,0 @@ -package repository - -import ( - mts "app-cloudep-permission-server/internal/lib/metric" - - ers "code.30cm.net/digimon/library-go/errors" - "code.30cm.net/digimon/library-go/errors/code" -) - -// token error 方面 -const ( - TokenUnexpectedSigningErrorCode = iota + 1 - TokenValidateErrorCode - TokenClaimErrorCode -) - -const ( - RedisDelErrorCode = iota + 20 - RedisPipLineErrorCode - RedisErrorCode -) - -// TokenUnexpectedSigningErr 30001 Token 簽名錯誤 -func TokenUnexpectedSigningErr(msg string) *ers.LibError { - mts.AppErrorMetrics.AddFailure("token", "token_unexpected_sign") - - return ers.NewErr(code.CloudEPPermission, code.CatInput, TokenUnexpectedSigningErrorCode, msg) -} - -// TokenTokenValidateErr 30002 Token 驗證錯誤 -func TokenTokenValidateErr(msg string) *ers.LibError { - mts.AppErrorMetrics.AddFailure("token", "token_validate_ilegal") - - return ers.NewErr(code.CloudEPPermission, code.CatInput, TokenValidateErrorCode, msg) -} - -// TokenClaimError 30003 Token 驗證錯誤 -func TokenClaimError(msg string) *ers.LibError { - mts.AppErrorMetrics.AddFailure("token", "token_claim_error") - - return ers.NewErr(code.CloudEPPermission, code.CatInput, TokenClaimErrorCode, msg) -} - -// RedisDelError 30020 Redis 刪除錯誤 -func RedisDelError(msg string) *ers.LibError { - // 看需要建立哪些 Metrics - mts.AppErrorMetrics.AddFailure("redis", "del_error") - - return ers.NewErr(code.CloudEPPermission, code.CatDB, RedisDelErrorCode, msg) -} - -// RedisPipLineError 30021 Redis PipLine 錯誤 -func RedisPipLineError(msg string) *ers.LibError { - // 看需要建立哪些 Metrics - mts.AppErrorMetrics.AddFailure("redis", "pip_line_error") - - return ers.NewErr(code.CloudEPPermission, code.CatInput, RedisPipLineErrorCode, msg) -} - -// RedisError 30022 Redis 錯誤 -func RedisError(msg string) *ers.LibError { - // 看需要建立哪些 Metrics - mts.AppErrorMetrics.AddFailure("redis", "error") - - return ers.NewErr(code.CloudEPPermission, code.CatInput, RedisErrorCode, msg) -} diff --git a/internal/domain/repository/member_status.go b/internal/domain/repository/member_status.go deleted file mode 100644 index 74de198..0000000 --- a/internal/domain/repository/member_status.go +++ /dev/null @@ -1,16 +0,0 @@ -package repository - -import "context" - -// MemberOnlineStatusRepository 會員上限狀態,使用Bitmap -type MemberOnlineStatusRepository interface { - SetMemberOnline(ctx context.Context, uid string) (bool, error) - SetMemberOffline(ctx context.Context, uid string) (bool, error) - IsMemberOnline(ctx context.Context, uid string) (bool, error) - QueryMemberOnlineList(ctx context.Context, uids []string) ([]MemberOnlineStatusResp, error) -} - -type MemberOnlineStatusResp struct { - UID string - Status bool -} diff --git a/internal/domain/repository/token.go b/internal/domain/repository/token.go deleted file mode 100644 index 0d33421..0000000 --- a/internal/domain/repository/token.go +++ /dev/null @@ -1,31 +0,0 @@ -package repository - -import ( - "app-cloudep-permission-server/internal/entity" - "context" - "time" -) - -// TokenRepository token 的 redis 操作 -type TokenRepository interface { - // Create 建立Token - Create(ctx context.Context, token entity.Token) error - // CreateOneTimeToken 建立臨時 Token - CreateOneTimeToken(ctx context.Context, key string, ticket entity.Ticket, dt time.Duration) error - GetAccessTokenByByOneTimeToken(ctx context.Context, oneTimeToken string) (entity.Token, error) - GetAccessTokenByID(ctx context.Context, id string) (entity.Token, error) - GetAccessTokensByUID(ctx context.Context, uid string) ([]entity.Token, error) - GetAccessTokenCountByUID(uid string) (int, error) - GetAccessTokensByDeviceID(ctx context.Context, deviceID string) ([]entity.Token, error) - GetAccessTokenCountByDeviceID(deviceID string) (int, error) - Delete(ctx context.Context, token entity.Token) error - DeleteOneTimeToken(ctx context.Context, ids []string, tokens []entity.Token) error - DeleteAccessTokenByID(ctx context.Context, ids []string) error - DeleteAccessTokensByUID(ctx context.Context, uid string) error - DeleteAccessTokensByDeviceID(ctx context.Context, deviceID string) error -} - -type DeviceToken struct { - DeviceID string - TokenID string -} diff --git a/internal/domain/usecase/bitmap.go b/internal/domain/usecase/bitmap.go deleted file mode 100644 index 05379b8..0000000 --- a/internal/domain/usecase/bitmap.go +++ /dev/null @@ -1,16 +0,0 @@ -package usecase - -type BitMapUseCase interface { - // SetTrue 設定該 Bit 狀態為 true - SetTrue(bitPos uint32) - // SetFalse 設定該Bit 狀態為 false - SetFalse(bitPos uint32) - // IsTrue 確認是否為真 - IsTrue(bitPos uint32) bool - // Reset 重設 BitMap - Reset() - // ByteSize 最大 Byte 數 - ByteSize() int - // BitSize 最大 Byte * 8 - BitSize() int -} diff --git a/internal/domain/usecase/opa.go b/internal/domain/usecase/opa.go deleted file mode 100644 index 1a1f3a1..0000000 --- a/internal/domain/usecase/opa.go +++ /dev/null @@ -1,47 +0,0 @@ -package usecase - -import ( - "context" -) - -type OpaUseCase interface { - // CheckRBACPermission 確認有無權限 - CheckRBACPermission(ctx context.Context, req CheckReq) (CheckOPAResp, error) - // LoadPolicy 將 Policy 從其他地方加載到 opa 的 policy 當中 - LoadPolicy(ctx context.Context, input []Policy) error - GetPolicy(ctx context.Context) []map[string]any -} - -type CheckReq struct { - ID string - Roles []string - Path string - Method string -} - -type Grant struct { - ID string - Path string - Method string -} - -type Policy struct { - Methods []string `json:"methods"` - Name string `json:"name"` - Path string `json:"path"` - Role string `json:"role"` -} - -type RuleRequest struct { - Method string `json:"method"` - Path string `json:"path"` - Policies []Policy `json:"policies"` - Roles []string `json:"roles"` -} - -type CheckOPAResp struct { - Allow bool `json:"allow"` - PolicyName string `json:"policy_name"` - PlainCode bool `json:"plain_code"` // 是否為明碼顯示 - Request RuleRequest `json:"request"` -} diff --git a/internal/domain/usecase/permission_tree.go b/internal/domain/usecase/permission_tree.go deleted file mode 100644 index 7b13a5d..0000000 --- a/internal/domain/usecase/permission_tree.go +++ /dev/null @@ -1,33 +0,0 @@ -package usecase - -// // PermissionTreeManager 定義一組操作權限樹的接口 -// // 這個名稱說明它是專門負責管理和操作權限樹的管理器 -// type PermissionTreeManager interface { -// // AddPermission 將一個新的權限節點插入到樹中 -// // key 是父節點的ID,value 是要插入的 Permission 資料 -// // 此方法應該能處理節點是否存在於父節點下的情況 -// AddPermission(parentID int64, permission entity.Permission) error -// // FindPermissionByID 根據權限 ID 查詢樹中的某個節點 -// // 如果節點存在,返回對應的 Permission 資料,否則返回 nil -// FindPermissionByID(permissionID int64) (*Permission, error) -// // GetAllParentPermissionIDs 根據傳入的 permissions 列表 -// // 找出每個權限的完整父節點權限 ID 路徑 -// // 例如,如果 B 的父權限是 A,並且給了 B 權限,則返回 A 和 B 的權限 ID -// GetAllParentPermissionIDs(permissions domain.Permissions) ([]int64, error) -// // GetAllParentPermissionStatuses 返回給定權限下的所有完整父節點權限狀態 -// // 例如,若給 B 權限,該方法將返回所有與 B 相關的父權限的狀態 -// GetAllParentPermissionStatuses(permissions domain.Permissions) (domain.Permissions, error) -// // GetRolePermissionTree 根據角色權限找出所有父節點和子節點權限狀態 -// // 角色權限是傳入的一個列表,該方法會根據每個角色的權限,返回所有相關的權限狀態 -// GetRolePermissionTree(rolePermissions []entity.RolePermission) domain.Permissions -// } -// -// type Permission struct { -// ID int64 `json:"-"` -// Name string `json:"name"` -// HTTPMethod string `json:"http_method"` -// HTTPPath string `json:"http_path"` -// Parent *Permission `json:"-"` -// Children []*Permission `json:"children"` -// PathIDs []int64 `json:"-"` // full path id -// } diff --git a/internal/entity/claims.go b/internal/entity/claims.go deleted file mode 100644 index 9271795..0000000 --- a/internal/entity/claims.go +++ /dev/null @@ -1,8 +0,0 @@ -package entity - -import "github.com/golang-jwt/jwt/v4" - -type Claims struct { - jwt.RegisteredClaims - Data interface{} `json:"data"` -} diff --git a/internal/entity/token.go b/internal/entity/token.go deleted file mode 100644 index 791395a..0000000 --- a/internal/entity/token.go +++ /dev/null @@ -1,50 +0,0 @@ -package entity - -import "time" - -type Token struct { - ID string `json:"id"` - UID string `json:"uid"` - DeviceID string `json:"device_id"` - AccessToken string `json:"access_token"` - ExpiresIn int `json:"expires_in"` - AccessCreateAt time.Time `json:"access_create_at"` - RefreshToken string `json:"refresh_token"` - RefreshExpiresIn int `json:"refresh_expires_in"` - RefreshCreateAt time.Time `json:"refresh_create_at"` -} - -func (t *Token) AccessTokenExpires() time.Duration { - return time.Duration(t.ExpiresIn) * time.Second -} - -func (t *Token) RefreshTokenExpires() time.Duration { - return time.Duration(t.RefreshExpiresIn) * time.Second -} - -func (t *Token) RefreshTokenExpiresUnix() int64 { - return time.Now().Add(t.RefreshTokenExpires()).Unix() -} - -func (t *Token) IsExpires() bool { - return t.AccessCreateAt.Add(t.AccessTokenExpires()).Before(time.Now()) -} - -func (t *Token) RedisExpiredSec() int64 { - sec := time.Unix(int64(t.ExpiresIn), 0).Sub(time.Now().UTC()) - - return int64(sec.Seconds()) -} - -func (t *Token) RedisRefreshExpiredSec() int64 { - sec := time.Unix(int64(t.RefreshExpiresIn), 0).Sub(time.Now().UTC()) - - return int64(sec.Seconds()) -} - -type UIDToken map[string]int64 - -type Ticket struct { - Data any `json:"data"` - Token Token `json:"token"` -} diff --git a/internal/lib/metric/app.go b/internal/lib/metric/app.go deleted file mode 100644 index 59da7ef..0000000 --- a/internal/lib/metric/app.go +++ /dev/null @@ -1,30 +0,0 @@ -package metric - -import ( - "github.com/zeromicro/go-zero/core/metric" -) - -var AppErrorMetrics = NewAppErrMetrics() - -type appErrMetrics struct { - metric.CounterVec -} - -type Metrics interface { - AddFailure(source, reason string) -} - -// NewAppErrMetrics initiate metrics and register to prometheus -func NewAppErrMetrics() Metrics { - return &appErrMetrics{metric.NewCounterVec(&metric.CounterVecOpts{ - Namespace: "ark", - Subsystem: "permission", - Name: "permission_app_error_total", - Help: "App defined failure total.", - Labels: []string{"source", "reason"}, - })} -} - -func (m *appErrMetrics) AddFailure(source, reason string) { - m.Inc(source, reason) -} diff --git a/internal/logic/tokenservice/cancel_one_time_token_logic.go b/internal/logic/tokenservice/cancel_one_time_token_logic.go index 9bb38bf..9bd1b34 100644 --- a/internal/logic/tokenservice/cancel_one_time_token_logic.go +++ b/internal/logic/tokenservice/cancel_one_time_token_logic.go @@ -3,10 +3,8 @@ package tokenservicelogic import ( "context" - ers "code.30cm.net/digimon/library-go/errors" - - "app-cloudep-permission-server/gen_result/pb/permission" - "app-cloudep-permission-server/internal/svc" + "code.30cm.net/digimon/app-cloudep-permission-server/gen_result/pb/app-cloudep-permission-server" + "code.30cm.net/digimon/app-cloudep-permission-server/internal/svc" "github.com/zeromicro/go-zero/core/logx" ) @@ -25,23 +23,9 @@ func NewCancelOneTimeTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) } } -type cancelOneTimeTokenReq struct { - Token []string `json:"token" validate:"required"` -} - // CancelOneTimeToken 取消一次性使用 -func (l *CancelOneTimeTokenLogic) CancelOneTimeToken(in *permission.CancelOneTimeTokenReq) (*permission.OKResp, error) { - // 驗證所需 - if err := l.svcCtx.Validate.ValidateAll(&cancelOneTimeTokenReq{ - Token: in.GetToken(), - }); err != nil { - return nil, ers.InvalidFormat(err.Error()) - } +func (l *CancelOneTimeTokenLogic) CancelOneTimeToken(in *app_cloudep_permission_server.CancelOneTimeTokenReq) (*app_cloudep_permission_server.OKResp, error) { + // todo: add your logic here and delete this line - err := l.svcCtx.TokenRedisRepo.DeleteOneTimeToken(l.ctx, in.GetToken(), nil) - if err != nil { - return nil, err - } - - return &permission.OKResp{}, nil + return &app_cloudep_permission_server.OKResp{}, nil } diff --git a/internal/logic/tokenservice/cancel_token_by_device_id_logic.go b/internal/logic/tokenservice/cancel_token_by_device_id_logic.go index 471eb5e..fb97add 100644 --- a/internal/logic/tokenservice/cancel_token_by_device_id_logic.go +++ b/internal/logic/tokenservice/cancel_token_by_device_id_logic.go @@ -3,10 +3,8 @@ package tokenservicelogic import ( "context" - ers "code.30cm.net/digimon/library-go/errors" - - "app-cloudep-permission-server/gen_result/pb/permission" - "app-cloudep-permission-server/internal/svc" + "code.30cm.net/digimon/app-cloudep-permission-server/gen_result/pb/app-cloudep-permission-server" + "code.30cm.net/digimon/app-cloudep-permission-server/internal/svc" "github.com/zeromicro/go-zero/core/logx" ) @@ -26,20 +24,8 @@ func NewCancelTokenByDeviceIdLogic(ctx context.Context, svcCtx *svc.ServiceConte } // CancelTokenByDeviceId 取消 Token, 從 Device 視角出發,可以選,登出這個Device 下所有 token ,登出這個Device 下指定token -func (l *CancelTokenByDeviceIdLogic) CancelTokenByDeviceId(in *permission.DoTokenByDeviceIDReq) (*permission.OKResp, error) { - if err := l.svcCtx.Validate.ValidateAll(&getUserTokensByDeviceIdReq{ - DeviceID: in.GetDeviceId(), - }); err != nil { - return nil, ers.InvalidFormat(err.Error()) - } +func (l *CancelTokenByDeviceIdLogic) CancelTokenByDeviceId(in *app_cloudep_permission_server.DoTokenByDeviceIDReq) (*app_cloudep_permission_server.OKResp, error) { + // todo: add your logic here and delete this line - err := l.svcCtx.TokenRedisRepo.DeleteAccessTokensByDeviceID(l.ctx, in.GetDeviceId()) - if err != nil { - logx.WithCallerSkip(1).WithFields( - logx.Field("func", "TokenRedisRepo.DeleteAccessTokensByDeviceID"), - logx.Field("DeviceID", in.GetDeviceId()), - ).Error(err.Error()) - return nil, err - } - return &permission.OKResp{}, nil + return &app_cloudep_permission_server.OKResp{}, nil } diff --git a/internal/logic/tokenservice/cancel_token_logic.go b/internal/logic/tokenservice/cancel_token_logic.go index 1bbe893..1e0cee0 100644 --- a/internal/logic/tokenservice/cancel_token_logic.go +++ b/internal/logic/tokenservice/cancel_token_logic.go @@ -3,10 +3,8 @@ package tokenservicelogic import ( "context" - ers "code.30cm.net/digimon/library-go/errors" - - "app-cloudep-permission-server/gen_result/pb/permission" - "app-cloudep-permission-server/internal/svc" + "code.30cm.net/digimon/app-cloudep-permission-server/gen_result/pb/app-cloudep-permission-server" + "code.30cm.net/digimon/app-cloudep-permission-server/internal/svc" "github.com/zeromicro/go-zero/core/logx" ) @@ -25,44 +23,9 @@ func NewCancelTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Cance } } -type cancelTokenReq struct { - Token string `json:"token" validate:"required"` -} - // CancelToken 取消 Token,也包含他裡面的 One Time Toke -func (l *CancelTokenLogic) CancelToken(in *permission.CancelTokenReq) (*permission.OKResp, error) { - // 驗證所需 - if err := l.svcCtx.Validate.ValidateAll(&cancelTokenReq{ - Token: in.GetToken(), - }); err != nil { - return nil, ers.InvalidFormat(err.Error()) - } +func (l *CancelTokenLogic) CancelToken(in *app_cloudep_permission_server.CancelTokenReq) (*app_cloudep_permission_server.OKResp, error) { + // todo: add your logic here and delete this line - claims, err := parseClaims(in.GetToken(), l.svcCtx.Config.Token.Secret, false) - if err != nil { - logx.WithCallerSkip(1).WithFields( - logx.Field("func", "parseClaims"), - ).Error(err.Error()) - return nil, err - } - - token, err := l.svcCtx.TokenRedisRepo.GetAccessTokenByID(l.ctx, claims.ID()) - if err != nil { - logx.WithCallerSkip(1).WithFields( - logx.Field("func", "TokenRedisRepo.GetByAccess"), - logx.Field("claims", claims), - ).Error(err.Error()) - return nil, err - } - - err = l.svcCtx.TokenRedisRepo.Delete(l.ctx, token) - if err != nil { - logx.WithCallerSkip(1).WithFields( - logx.Field("func", "TokenRedisRepo.Delete"), - logx.Field("req", token), - ).Error(err.Error()) - return nil, err - } - - return &permission.OKResp{}, nil + return &app_cloudep_permission_server.OKResp{}, nil } diff --git a/internal/logic/tokenservice/cancel_tokens_logic.go b/internal/logic/tokenservice/cancel_tokens_logic.go index d1bfd9d..323224e 100644 --- a/internal/logic/tokenservice/cancel_tokens_logic.go +++ b/internal/logic/tokenservice/cancel_tokens_logic.go @@ -3,10 +3,8 @@ package tokenservicelogic import ( "context" - ers "code.30cm.net/digimon/library-go/errors" - - "app-cloudep-permission-server/gen_result/pb/permission" - "app-cloudep-permission-server/internal/svc" + "code.30cm.net/digimon/app-cloudep-permission-server/gen_result/pb/app-cloudep-permission-server" + "code.30cm.net/digimon/app-cloudep-permission-server/internal/svc" "github.com/zeromicro/go-zero/core/logx" ) @@ -26,28 +24,8 @@ func NewCancelTokensLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Canc } // CancelTokens 取消 Token 從UID 視角,以及 token id 視角出發, UID 登出,底下所有 Device ID 也要登出, Token ID 登出, 所有 UID + Device 都要登出 -func (l *CancelTokensLogic) CancelTokens(in *permission.DoTokenByUIDReq) (*permission.OKResp, error) { - if in.GetUid() != "" { - err := l.svcCtx.TokenRedisRepo.DeleteAccessTokensByUID(l.ctx, in.GetUid()) - if err != nil { - logx.WithCallerSkip(1).WithFields( - logx.Field("func", "TokenRedisRepo.DeleteAccessTokensByUID"), - logx.Field("uid", in.GetUid()), - ).Error(err.Error()) - return nil, ers.ResourceInsufficient(err.Error()) - } - } +func (l *CancelTokensLogic) CancelTokens(in *app_cloudep_permission_server.DoTokenByUIDReq) (*app_cloudep_permission_server.OKResp, error) { + // todo: add your logic here and delete this line - if len(in.GetIds()) > 0 { - err := l.svcCtx.TokenRedisRepo.DeleteAccessTokenByID(l.ctx, in.GetIds()) - if err != nil { - logx.WithCallerSkip(1).WithFields( - logx.Field("func", "TokenRedisRepo.DeleteAccessTokenByID"), - logx.Field("ids", in.GetIds()), - ).Error(err.Error()) - return nil, ers.ResourceInsufficient(err.Error()) - } - } - - return &permission.OKResp{}, nil + return &app_cloudep_permission_server.OKResp{}, nil } diff --git a/internal/logic/tokenservice/get_user_tokens_by_device_id_logic.go b/internal/logic/tokenservice/get_user_tokens_by_device_id_logic.go index 2281b21..134d249 100644 --- a/internal/logic/tokenservice/get_user_tokens_by_device_id_logic.go +++ b/internal/logic/tokenservice/get_user_tokens_by_device_id_logic.go @@ -1,13 +1,10 @@ package tokenservicelogic import ( - "app-cloudep-permission-server/internal/domain" "context" - ers "code.30cm.net/digimon/library-go/errors" - - "app-cloudep-permission-server/gen_result/pb/permission" - "app-cloudep-permission-server/internal/svc" + "code.30cm.net/digimon/app-cloudep-permission-server/gen_result/pb/app-cloudep-permission-server" + "code.30cm.net/digimon/app-cloudep-permission-server/internal/svc" "github.com/zeromicro/go-zero/core/logx" ) @@ -26,34 +23,9 @@ func NewGetUserTokensByDeviceIdLogic(ctx context.Context, svcCtx *svc.ServiceCon } } -type getUserTokensByDeviceIdReq struct { - DeviceID string `json:"device_id" validate:"required"` -} - // GetUserTokensByDeviceId 取得目前所對應的 DeviceID 所存在的 Tokens -func (l *GetUserTokensByDeviceIdLogic) GetUserTokensByDeviceId(in *permission.DoTokenByDeviceIDReq) (*permission.Tokens, error) { - if err := l.svcCtx.Validate.ValidateAll(&getUserTokensByDeviceIdReq{ - DeviceID: in.GetDeviceId(), - }); err != nil { - return nil, ers.InvalidFormat(err.Error()) - } +func (l *GetUserTokensByDeviceIdLogic) GetUserTokensByDeviceId(in *app_cloudep_permission_server.DoTokenByDeviceIDReq) (*app_cloudep_permission_server.Tokens, error) { + // todo: add your logic here and delete this line - uidTokens, err := l.svcCtx.TokenRedisRepo.GetAccessTokensByDeviceID(l.ctx, in.GetDeviceId()) - if err != nil { - return nil, err - } - - tokens := make([]*permission.TokenResp, 0, len(uidTokens)) - for _, v := range uidTokens { - tokens = append(tokens, &permission.TokenResp{ - AccessToken: v.AccessToken, - TokenType: domain.TokenTypeBearer, - ExpiresIn: int32(v.ExpiresIn), - RefreshToken: v.RefreshToken, - }) - } - - return &permission.Tokens{ - Token: tokens, - }, nil + return &app_cloudep_permission_server.Tokens{}, nil } diff --git a/internal/logic/tokenservice/get_user_tokens_by_uid_logic.go b/internal/logic/tokenservice/get_user_tokens_by_uid_logic.go index f0435a9..758b407 100644 --- a/internal/logic/tokenservice/get_user_tokens_by_uid_logic.go +++ b/internal/logic/tokenservice/get_user_tokens_by_uid_logic.go @@ -1,13 +1,10 @@ package tokenservicelogic import ( - "app-cloudep-permission-server/internal/domain" "context" - ers "code.30cm.net/digimon/library-go/errors" - - "app-cloudep-permission-server/gen_result/pb/permission" - "app-cloudep-permission-server/internal/svc" + "code.30cm.net/digimon/app-cloudep-permission-server/gen_result/pb/app-cloudep-permission-server" + "code.30cm.net/digimon/app-cloudep-permission-server/internal/svc" "github.com/zeromicro/go-zero/core/logx" ) @@ -26,34 +23,9 @@ func NewGetUserTokensByUidLogic(ctx context.Context, svcCtx *svc.ServiceContext) } } -type getUserTokensByUidReq struct { - UID string `json:"uid" validate:"required"` -} - // GetUserTokensByUid 取得目前所對應的 UID 所存在的 Tokens -func (l *GetUserTokensByUidLogic) GetUserTokensByUid(in *permission.QueryTokenByUIDReq) (*permission.Tokens, error) { - if err := l.svcCtx.Validate.ValidateAll(&getUserTokensByUidReq{ - UID: in.GetUid(), - }); err != nil { - return nil, ers.InvalidFormat(err.Error()) - } +func (l *GetUserTokensByUidLogic) GetUserTokensByUid(in *app_cloudep_permission_server.QueryTokenByUIDReq) (*app_cloudep_permission_server.Tokens, error) { + // todo: add your logic here and delete this line - uidTokens, err := l.svcCtx.TokenRedisRepo.GetAccessTokensByUID(l.ctx, in.GetUid()) - if err != nil { - return nil, err - } - - tokens := make([]*permission.TokenResp, 0, len(uidTokens)) - for _, v := range uidTokens { - tokens = append(tokens, &permission.TokenResp{ - AccessToken: v.AccessToken, - TokenType: domain.TokenTypeBearer, - ExpiresIn: int32(v.ExpiresIn), - RefreshToken: v.RefreshToken, - }) - } - - return &permission.Tokens{ - Token: tokens, - }, nil + return &app_cloudep_permission_server.Tokens{}, nil } diff --git a/internal/logic/tokenservice/new_one_time_token_logic.go b/internal/logic/tokenservice/new_one_time_token_logic.go index 3480c00..d4736be 100644 --- a/internal/logic/tokenservice/new_one_time_token_logic.go +++ b/internal/logic/tokenservice/new_one_time_token_logic.go @@ -1,16 +1,10 @@ package tokenservicelogic import ( - "app-cloudep-permission-server/internal/domain" - "app-cloudep-permission-server/internal/entity" "context" - "time" - ers "code.30cm.net/digimon/library-go/errors" - "github.com/google/uuid" - - "app-cloudep-permission-server/gen_result/pb/permission" - "app-cloudep-permission-server/internal/svc" + "code.30cm.net/digimon/app-cloudep-permission-server/gen_result/pb/app-cloudep-permission-server" + "code.30cm.net/digimon/app-cloudep-permission-server/internal/svc" "github.com/zeromicro/go-zero/core/logx" ) @@ -30,42 +24,8 @@ func NewNewOneTimeTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *N } // NewOneTimeToken 建立一次性使用,例如:RefreshToken -func (l *NewOneTimeTokenLogic) NewOneTimeToken(in *permission.CreateOneTimeTokenReq) (*permission.CreateOneTimeTokenResp, error) { - // 驗證所需 - if err := l.svcCtx.Validate.ValidateAll(&refreshTokenReq{ - Token: in.GetToken(), - }); err != nil { - return nil, ers.InvalidFormat(err.Error()) - } +func (l *NewOneTimeTokenLogic) NewOneTimeToken(in *app_cloudep_permission_server.CreateOneTimeTokenReq) (*app_cloudep_permission_server.CreateOneTimeTokenResp, error) { + // todo: add your logic here and delete this line - // 驗證Token - claims, err := parseClaims(in.GetToken(), l.svcCtx.Config.Token.Secret, false) - if err != nil { - logx.WithCallerSkip(1).WithFields( - logx.Field("func", "parseClaims"), - ).Error(err.Error()) - return nil, err - } - - token, err := l.svcCtx.TokenRedisRepo.GetAccessTokenByID(l.ctx, claims.ID()) - if err != nil { - logx.WithCallerSkip(1).WithFields( - logx.Field("func", "TokenRedisRepo.GetByAccess"), - logx.Field("claims", claims), - ).Error(err.Error()) - return nil, err - } - - oneTimeToken := generateRefreshToken(uuid.Must(uuid.NewRandom()).String()) - key := domain.TicketKeyPrefix + oneTimeToken - if err = l.svcCtx.TokenRedisRepo.CreateOneTimeToken(l.ctx, key, entity.Ticket{ - Data: claims, - Token: token, - }, time.Minute); err != nil { - return &permission.CreateOneTimeTokenResp{}, err - } - - return &permission.CreateOneTimeTokenResp{ - OneTimeToken: oneTimeToken, - }, nil + return &app_cloudep_permission_server.CreateOneTimeTokenResp{}, nil } diff --git a/internal/logic/tokenservice/new_token_logic.go b/internal/logic/tokenservice/new_token_logic.go index 52bd853..bbab347 100644 --- a/internal/logic/tokenservice/new_token_logic.go +++ b/internal/logic/tokenservice/new_token_logic.go @@ -1,17 +1,10 @@ package tokenservicelogic import ( - "app-cloudep-permission-server/internal/config" - "app-cloudep-permission-server/internal/domain" - "app-cloudep-permission-server/internal/entity" "context" - "time" - ers "code.30cm.net/digimon/library-go/errors" - "github.com/google/uuid" - - "app-cloudep-permission-server/gen_result/pb/permission" - "app-cloudep-permission-server/internal/svc" + "code.30cm.net/digimon/app-cloudep-permission-server/gen_result/pb/app-cloudep-permission-server" + "code.30cm.net/digimon/app-cloudep-permission-server/internal/svc" "github.com/zeromicro/go-zero/core/logx" ) @@ -30,110 +23,9 @@ func NewNewTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *NewToken } } -// https://datatracker.ietf.org/doc/html/rfc6749#section-3.3 -type authorizationReq struct { - GrantType domain.GrantType `json:"grant_type" validate:"required,oneof=password client_credentials refresh_token"` - DeviceID string `json:"device_id"` - Scope string `json:"scope" validate:"required"` - Data map[string]string `json:"data"` - Expires int `json:"expires"` - IsRefreshToken bool `json:"is_refresh_token"` -} - // NewToken 建立一個新的 Token,例如:AccessToken -func (l *NewTokenLogic) NewToken(in *permission.AuthorizationReq) (*permission.TokenResp, error) { - data := authorizationReq{ - GrantType: domain.GrantType(in.GetGrantType()), - Scope: in.GetScope(), - DeviceID: in.GetDeviceId(), - Data: in.GetData(), - Expires: int(in.GetExpires()), - IsRefreshToken: in.GetIsRefreshToken(), - } - // 驗證所需 - if err := l.svcCtx.Validate.ValidateAll(&data); err != nil { - return nil, ers.InvalidFormat(err.Error()) - } - token, err := newToken(data, l.svcCtx.Config) - if err != nil { - return nil, err - } +func (l *NewTokenLogic) NewToken(in *app_cloudep_permission_server.AuthorizationReq) (*app_cloudep_permission_server.TokenResp, error) { + // todo: add your logic here and delete this line - err = l.svcCtx.TokenRedisRepo.Create(l.ctx, *token) - if err != nil { - logx.WithCallerSkip(1).WithFields( - logx.Field("func", "TokenRedisRepo.Create"), - logx.Field("token", token), - ).Error(err.Error()) - return nil, err - } - - return &permission.TokenResp{ - AccessToken: token.AccessToken, - TokenType: domain.TokenTypeBearer, - ExpiresIn: int32(token.ExpiresIn), - RefreshToken: token.RefreshToken, - }, nil -} - -func newToken(authReq authorizationReq, cfg config.Config) (*entity.Token, error) { - // 準備建立 Token 所需 - now := time.Now().UTC() - expires := authReq.Expires - refreshExpires := authReq.Expires - if expires <= 0 { - // 將時間加上 300 秒 - sec := time.Duration(cfg.Token.Expired.Seconds()) * time.Second - newTime := now.Add(sec) - // 獲取 Unix 時間戳 - timestamp := newTime.Unix() - expires = int(timestamp) - refreshExpires = expires - } - - // 如果這是一個 Refresh Token 過期時間要比普通的Token 長 - if authReq.IsRefreshToken { - // 將時間加上 300 秒 - sec := time.Duration(cfg.Token.RefreshExpires.Seconds()) * time.Second - newTime := now.Add(sec) - // 獲取 Unix 時間戳 - timestamp := newTime.Unix() - refreshExpires = int(timestamp) - } - - token := entity.Token{ - ID: uuid.Must(uuid.NewRandom()).String(), - DeviceID: authReq.DeviceID, - ExpiresIn: expires, - RefreshExpiresIn: refreshExpires, - AccessCreateAt: now, - RefreshCreateAt: now, - } - - claims := claims(authReq.Data) - claims.SetRole(domain.DefaultRole) - claims.SetID(token.ID) - claims.SetScope(authReq.Scope) - - token.UID = claims.UID() - - if authReq.DeviceID != "" { - claims.SetDeviceID(authReq.DeviceID) - } - - var err error - token.AccessToken, err = generateAccessTokenFunc(token, claims, cfg.Token.Secret) - if err != nil { - logx.WithCallerSkip(1).WithFields( - logx.Field("func", "generateAccessTokenFunc"), - logx.Field("claims", claims), - ).Error(err.Error()) - return nil, err - } - - if authReq.IsRefreshToken { - token.RefreshToken = generateRefreshTokenFunc(token.AccessToken) - } - - return &token, nil + return &app_cloudep_permission_server.TokenResp{}, nil } diff --git a/internal/logic/tokenservice/refresh_token_logic.go b/internal/logic/tokenservice/refresh_token_logic.go index 1378989..22042f4 100644 --- a/internal/logic/tokenservice/refresh_token_logic.go +++ b/internal/logic/tokenservice/refresh_token_logic.go @@ -1,13 +1,10 @@ package tokenservicelogic import ( - "app-cloudep-permission-server/internal/domain" "context" - ers "code.30cm.net/digimon/library-go/errors" - - "app-cloudep-permission-server/gen_result/pb/permission" - "app-cloudep-permission-server/internal/svc" + "code.30cm.net/digimon/app-cloudep-permission-server/gen_result/pb/app-cloudep-permission-server" + "code.30cm.net/digimon/app-cloudep-permission-server/internal/svc" "github.com/zeromicro/go-zero/core/logx" ) @@ -26,83 +23,9 @@ func NewRefreshTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Refr } } -type refreshReq struct { - RefreshToken string `json:"grant_type" validate:"required"` - DeviceID string `json:"device_id" validate:"required"` - Scope string `json:"scope" validate:"required"` -} - // RefreshToken 更新目前的token 以及裡面包含的一次性 Token -func (l *RefreshTokenLogic) RefreshToken(in *permission.RefreshTokenReq) (*permission.RefreshTokenResp, error) { - // 驗證所需 - if err := l.svcCtx.Validate.ValidateAll(&refreshReq{ - RefreshToken: in.GetToken(), - Scope: in.GetScope(), - DeviceID: in.GetDeviceId(), - }); err != nil { - return nil, ers.InvalidFormat(err.Error()) - } +func (l *RefreshTokenLogic) RefreshToken(in *app_cloudep_permission_server.RefreshTokenReq) (*app_cloudep_permission_server.RefreshTokenResp, error) { + // todo: add your logic here and delete this line - // step 1 拿看看有沒有這個 refresh token - token, err := l.svcCtx.TokenRedisRepo.GetAccessTokenByByOneTimeToken(l.ctx, in.Token) - if err != nil { - logx.WithCallerSkip(1).WithFields( - logx.Field("func", "TokenRedisRepo.GetByRefresh"), - logx.Field("req", in), - ).Error(err.Error()) - return nil, err - } - - // 取得 Data - c, err := parseClaims(token.AccessToken, l.svcCtx.Config.Token.Secret, false) - if err != nil { - logx.WithCallerSkip(1).WithFields( - logx.Field("func", "parseClaims"), - logx.Field("token", token), - ).Error(err.Error()) - return nil, err - } - - // step 2 建立新 token - nt, err := newToken(authorizationReq{ - GrantType: domain.ClientCredentials, - Scope: in.GetScope(), - DeviceID: in.GetDeviceId(), - Data: c, - Expires: int(in.GetExpires()), - IsRefreshToken: true, - }, l.svcCtx.Config) - if err != nil { - logx.WithCallerSkip(1).WithFields( - logx.Field("func", "newToken"), - logx.Field("req", in), - ).Error(err.Error()) - return nil, err - } - - // 刪除掉舊的 token - err = l.svcCtx.TokenRedisRepo.Delete(l.ctx, token) - if err != nil { - logx.WithCallerSkip(1).WithFields( - logx.Field("func", "TokenRedisRepo.Delete"), - logx.Field("req", token), - ).Error(err.Error()) - return nil, err - } - - err = l.svcCtx.TokenRedisRepo.Create(l.ctx, *nt) - if err != nil { - logx.WithCallerSkip(1).WithFields( - logx.Field("func", "TokenRedisRepo.Create"), - logx.Field("token", token), - ).Error(err.Error()) - return nil, err - } - - return &permission.RefreshTokenResp{ - Token: nt.AccessToken, - OneTimeToken: nt.RefreshToken, - ExpiresIn: int64(nt.ExpiresIn), - TokenType: domain.TokenTypeBearer, - }, nil + return &app_cloudep_permission_server.RefreshTokenResp{}, nil } diff --git a/internal/logic/tokenservice/utils_claims.go b/internal/logic/tokenservice/utils_claims.go deleted file mode 100644 index 2d59b66..0000000 --- a/internal/logic/tokenservice/utils_claims.go +++ /dev/null @@ -1,55 +0,0 @@ -package tokenservicelogic - -type claims map[string]string - -func (c claims) SetID(id string) { - c["id"] = id -} - -func (c claims) SetRole(role string) { - c["role"] = role -} - -func (c claims) SetDeviceID(deviceID string) { - c["device_id"] = deviceID -} - -func (c claims) SetScope(scope string) { - c["scope"] = scope -} - -func (c claims) Role() string { - role, ok := c["role"] - if !ok { - return "" - } - - return role -} - -func (c claims) ID() string { - id, ok := c["id"] - if !ok { - return "" - } - - return id -} - -func (c claims) DeviceID() string { - deviceID, ok := c["device_id"] - if !ok { - return "" - } - - return deviceID -} - -func (c claims) UID() string { - uid, ok := c["uid"] - if !ok { - return "" - } - - return uid -} diff --git a/internal/logic/tokenservice/utils_jwt.go b/internal/logic/tokenservice/utils_jwt.go deleted file mode 100644 index 57e8315..0000000 --- a/internal/logic/tokenservice/utils_jwt.go +++ /dev/null @@ -1,105 +0,0 @@ -package tokenservicelogic - -import ( - "app-cloudep-permission-server/internal/domain" - "app-cloudep-permission-server/internal/entity" - "bytes" - "crypto/sha256" - "encoding/hex" - "fmt" - "time" - - "github.com/golang-jwt/jwt/v4" -) - -var generateAccessTokenFunc = generateAccessToken -var generateRefreshTokenFunc = generateRefreshToken - -func generateAccessToken(token entity.Token, data any, sign string) (string, error) { - claim := entity.Claims{ - Data: data, - RegisteredClaims: jwt.RegisteredClaims{ - ID: token.ID, - ExpiresAt: jwt.NewNumericDate(time.Unix(int64(token.ExpiresIn), 0)), - Issuer: "permission", - }, - } - - accessToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claim). - SignedString([]byte(sign)) - if err != nil { - return "", domain.TokenClaimError(err.Error()) - } - - return accessToken, nil -} - -func generateRefreshToken(accessToken string) string { - buf := bytes.NewBufferString(accessToken) - h := sha256.New() - _, _ = h.Write(buf.Bytes()) - - return hex.EncodeToString(h.Sum(nil)) -} - -func parseToken(accessToken string, secret string, validate bool) (jwt.MapClaims, error) { - // 跳過驗證的解析 - var token *jwt.Token - var err error - - if validate { - token, err = jwt.Parse(accessToken, func(token *jwt.Token) (interface{}, error) { - if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, domain.TokenUnexpectedSigningErr(fmt.Sprintf("token unexpected signing method: %v", token.Header["alg"])) - } - return []byte(secret), nil - }) - if err != nil { - return jwt.MapClaims{}, err - } - } else { - parser := jwt.NewParser(jwt.WithoutClaimsValidation()) - token, err = parser.Parse(accessToken, func(token *jwt.Token) (interface{}, error) { - return []byte(secret), nil - }) - if err != nil { - return jwt.MapClaims{}, err - } - } - - claims, ok := token.Claims.(jwt.MapClaims) - if !ok && token.Valid { - return jwt.MapClaims{}, domain.TokenTokenValidateErr("token valid error") - } - - return claims, nil -} - -func parseClaims(accessToken string, secret string, validate bool) (claims, error) { - claimMap, err := parseToken(accessToken, secret, validate) - if err != nil { - return claims{}, err - } - - claimsData, ok := claimMap["data"].(map[string]any) - if ok { - return convertMap(claimsData), nil - } - - return claims{}, domain.TokenClaimError("get data from claim map error") -} - -func convertMap(input map[string]interface{}) map[string]string { - output := make(map[string]string) - for key, value := range input { - switch v := value.(type) { - case string: - output[key] = v - case fmt.Stringer: - output[key] = v.String() - default: - output[key] = fmt.Sprintf("%v", value) - } - } - return output -} diff --git a/internal/logic/tokenservice/validation_token_logic.go b/internal/logic/tokenservice/validation_token_logic.go index 3717f1c..0f682e1 100644 --- a/internal/logic/tokenservice/validation_token_logic.go +++ b/internal/logic/tokenservice/validation_token_logic.go @@ -3,10 +3,8 @@ package tokenservicelogic import ( "context" - ers "code.30cm.net/digimon/library-go/errors" - - "app-cloudep-permission-server/gen_result/pb/permission" - "app-cloudep-permission-server/internal/svc" + "code.30cm.net/digimon/app-cloudep-permission-server/gen_result/pb/app-cloudep-permission-server" + "code.30cm.net/digimon/app-cloudep-permission-server/internal/svc" "github.com/zeromicro/go-zero/core/logx" ) @@ -25,46 +23,9 @@ func NewValidationTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *V } } -type refreshTokenReq struct { - Token string `json:"token" validate:"required"` -} - // ValidationToken 驗證這個 Token 有沒有效 -func (l *ValidationTokenLogic) ValidationToken(in *permission.ValidationTokenReq) (*permission.ValidationTokenResp, error) { - // 驗證所需 - if err := l.svcCtx.Validate.ValidateAll(&refreshTokenReq{ - Token: in.GetToken(), - }); err != nil { - return nil, ers.InvalidFormat(err.Error()) - } - claims, err := parseClaims(in.GetToken(), l.svcCtx.Config.Token.Secret, true) - if err != nil { - logx.WithCallerSkip(1).WithFields( - logx.Field("func", "parseClaims"), - ).Info(err.Error()) - return nil, err - } - token, err := l.svcCtx.TokenRedisRepo.GetAccessTokenByID(l.ctx, claims.ID()) - if err != nil { - logx.WithCallerSkip(1).WithFields( - logx.Field("func", "TokenRedisRepo.GetByAccess"), - logx.Field("claims", claims), - ).Error(err.Error()) - return nil, err - } +func (l *ValidationTokenLogic) ValidationToken(in *app_cloudep_permission_server.ValidationTokenReq) (*app_cloudep_permission_server.ValidationTokenResp, error) { + // todo: add your logic here and delete this line - return &permission.ValidationTokenResp{ - Token: &permission.Token{ - Id: token.ID, - Uid: token.UID, - DeviceId: token.DeviceID, - AccessCreateAt: token.AccessCreateAt.Unix(), - AccessToken: token.AccessToken, - ExpiresIn: int32(token.ExpiresIn), - RefreshToken: token.RefreshToken, - RefreshExpiresIn: int32(token.RefreshExpiresIn), - RefreshCreateAt: token.RefreshCreateAt.Unix(), - }, - Data: claims, - }, nil + return &app_cloudep_permission_server.ValidationTokenResp{}, nil } diff --git a/internal/mock/model/permission_model.go b/internal/mock/model/permission_model.go deleted file mode 100644 index 47a7c11..0000000 --- a/internal/mock/model/permission_model.go +++ /dev/null @@ -1,115 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./internal/model/permission_model.go -// -// Generated by this command: -// -// mockgen -source=./internal/model/permission_model.go -destination=./internal/mock/model/permission_model.go -package=mock -// - -// Package mock is a generated GoMock package. -package mock - -import ( - model "app-cloudep-permission-server/internal/model" - context "context" - sql "database/sql" - reflect "reflect" - - gomock "go.uber.org/mock/gomock" -) - -// MockPermissionModel is a mock of PermissionModel interface. -type MockPermissionModel struct { - ctrl *gomock.Controller - recorder *MockPermissionModelMockRecorder -} - -// MockPermissionModelMockRecorder is the mock recorder for MockPermissionModel. -type MockPermissionModelMockRecorder struct { - mock *MockPermissionModel -} - -// NewMockPermissionModel creates a new mock instance. -func NewMockPermissionModel(ctrl *gomock.Controller) *MockPermissionModel { - mock := &MockPermissionModel{ctrl: ctrl} - mock.recorder = &MockPermissionModelMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockPermissionModel) EXPECT() *MockPermissionModelMockRecorder { - return m.recorder -} - -// Delete mocks base method. -func (m *MockPermissionModel) Delete(ctx context.Context, id int64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", ctx, id) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockPermissionModelMockRecorder) Delete(ctx, id any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockPermissionModel)(nil).Delete), ctx, id) -} - -// FindOne mocks base method. -func (m *MockPermissionModel) FindOne(ctx context.Context, id int64) (*model.Permission, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindOne", ctx, id) - ret0, _ := ret[0].(*model.Permission) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindOne indicates an expected call of FindOne. -func (mr *MockPermissionModelMockRecorder) FindOne(ctx, id any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockPermissionModel)(nil).FindOne), ctx, id) -} - -// FindOneByName mocks base method. -func (m *MockPermissionModel) FindOneByName(ctx context.Context, name string) (*model.Permission, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindOneByName", ctx, name) - ret0, _ := ret[0].(*model.Permission) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindOneByName indicates an expected call of FindOneByName. -func (mr *MockPermissionModelMockRecorder) FindOneByName(ctx, name any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOneByName", reflect.TypeOf((*MockPermissionModel)(nil).FindOneByName), ctx, name) -} - -// Insert mocks base method. -func (m *MockPermissionModel) Insert(ctx context.Context, data *model.Permission) (sql.Result, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Insert", ctx, data) - ret0, _ := ret[0].(sql.Result) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Insert indicates an expected call of Insert. -func (mr *MockPermissionModelMockRecorder) Insert(ctx, data any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockPermissionModel)(nil).Insert), ctx, data) -} - -// Update mocks base method. -func (m *MockPermissionModel) Update(ctx context.Context, data *model.Permission) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", ctx, data) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockPermissionModelMockRecorder) Update(ctx, data any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockPermissionModel)(nil).Update), ctx, data) -} diff --git a/internal/mock/model/permission_model_gen.go b/internal/mock/model/permission_model_gen.go deleted file mode 100644 index fcb4d57..0000000 --- a/internal/mock/model/permission_model_gen.go +++ /dev/null @@ -1,115 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./internal/model/permission_model_gen.go -// -// Generated by this command: -// -// mockgen -source=./internal/model/permission_model_gen.go -destination=./internal/mock/model/permission_model_gen.go -package=mock -// - -// Package mock is a generated GoMock package. -package mock - -import ( - model "app-cloudep-permission-server/internal/model" - context "context" - sql "database/sql" - reflect "reflect" - - gomock "go.uber.org/mock/gomock" -) - -// MockpermissionModel is a mock of permissionModel interface. -type MockpermissionModel struct { - ctrl *gomock.Controller - recorder *MockpermissionModelMockRecorder -} - -// MockpermissionModelMockRecorder is the mock recorder for MockpermissionModel. -type MockpermissionModelMockRecorder struct { - mock *MockpermissionModel -} - -// NewMockpermissionModel creates a new mock instance. -func NewMockpermissionModel(ctrl *gomock.Controller) *MockpermissionModel { - mock := &MockpermissionModel{ctrl: ctrl} - mock.recorder = &MockpermissionModelMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockpermissionModel) EXPECT() *MockpermissionModelMockRecorder { - return m.recorder -} - -// Delete mocks base method. -func (m *MockpermissionModel) Delete(ctx context.Context, id int64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", ctx, id) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockpermissionModelMockRecorder) Delete(ctx, id any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockpermissionModel)(nil).Delete), ctx, id) -} - -// FindOne mocks base method. -func (m *MockpermissionModel) FindOne(ctx context.Context, id int64) (*model.Permission, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindOne", ctx, id) - ret0, _ := ret[0].(*model.Permission) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindOne indicates an expected call of FindOne. -func (mr *MockpermissionModelMockRecorder) FindOne(ctx, id any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockpermissionModel)(nil).FindOne), ctx, id) -} - -// FindOneByName mocks base method. -func (m *MockpermissionModel) FindOneByName(ctx context.Context, name string) (*model.Permission, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindOneByName", ctx, name) - ret0, _ := ret[0].(*model.Permission) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindOneByName indicates an expected call of FindOneByName. -func (mr *MockpermissionModelMockRecorder) FindOneByName(ctx, name any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOneByName", reflect.TypeOf((*MockpermissionModel)(nil).FindOneByName), ctx, name) -} - -// Insert mocks base method. -func (m *MockpermissionModel) Insert(ctx context.Context, data *model.Permission) (sql.Result, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Insert", ctx, data) - ret0, _ := ret[0].(sql.Result) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Insert indicates an expected call of Insert. -func (mr *MockpermissionModelMockRecorder) Insert(ctx, data any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockpermissionModel)(nil).Insert), ctx, data) -} - -// Update mocks base method. -func (m *MockpermissionModel) Update(ctx context.Context, data *model.Permission) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", ctx, data) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockpermissionModelMockRecorder) Update(ctx, data any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockpermissionModel)(nil).Update), ctx, data) -} diff --git a/internal/mock/model/role_model.go b/internal/mock/model/role_model.go deleted file mode 100644 index aeb86cd..0000000 --- a/internal/mock/model/role_model.go +++ /dev/null @@ -1,130 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./internal/model/role_model.go -// -// Generated by this command: -// -// mockgen -source=./internal/model/role_model.go -destination=./internal/mock/model/role_model.go -package=mock -// - -// Package mock is a generated GoMock package. -package mock - -import ( - model "app-cloudep-permission-server/internal/model" - context "context" - sql "database/sql" - reflect "reflect" - - gomock "go.uber.org/mock/gomock" -) - -// MockRoleModel is a mock of RoleModel interface. -type MockRoleModel struct { - ctrl *gomock.Controller - recorder *MockRoleModelMockRecorder -} - -// MockRoleModelMockRecorder is the mock recorder for MockRoleModel. -type MockRoleModelMockRecorder struct { - mock *MockRoleModel -} - -// NewMockRoleModel creates a new mock instance. -func NewMockRoleModel(ctrl *gomock.Controller) *MockRoleModel { - mock := &MockRoleModel{ctrl: ctrl} - mock.recorder = &MockRoleModelMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockRoleModel) EXPECT() *MockRoleModelMockRecorder { - return m.recorder -} - -// Delete mocks base method. -func (m *MockRoleModel) Delete(ctx context.Context, id int64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", ctx, id) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockRoleModelMockRecorder) Delete(ctx, id any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockRoleModel)(nil).Delete), ctx, id) -} - -// FindOne mocks base method. -func (m *MockRoleModel) FindOne(ctx context.Context, id int64) (*model.Role, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindOne", ctx, id) - ret0, _ := ret[0].(*model.Role) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindOne indicates an expected call of FindOne. -func (mr *MockRoleModelMockRecorder) FindOne(ctx, id any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockRoleModel)(nil).FindOne), ctx, id) -} - -// FindOneByDisplayName mocks base method. -func (m *MockRoleModel) FindOneByDisplayName(ctx context.Context, displayName string) (*model.Role, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindOneByDisplayName", ctx, displayName) - ret0, _ := ret[0].(*model.Role) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindOneByDisplayName indicates an expected call of FindOneByDisplayName. -func (mr *MockRoleModelMockRecorder) FindOneByDisplayName(ctx, displayName any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOneByDisplayName", reflect.TypeOf((*MockRoleModel)(nil).FindOneByDisplayName), ctx, displayName) -} - -// FindOneByRoleId mocks base method. -func (m *MockRoleModel) FindOneByRoleId(ctx context.Context, roleId string) (*model.Role, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindOneByRoleId", ctx, roleId) - ret0, _ := ret[0].(*model.Role) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindOneByRoleId indicates an expected call of FindOneByRoleId. -func (mr *MockRoleModelMockRecorder) FindOneByRoleId(ctx, roleId any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOneByRoleId", reflect.TypeOf((*MockRoleModel)(nil).FindOneByRoleId), ctx, roleId) -} - -// Insert mocks base method. -func (m *MockRoleModel) Insert(ctx context.Context, data *model.Role) (sql.Result, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Insert", ctx, data) - ret0, _ := ret[0].(sql.Result) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Insert indicates an expected call of Insert. -func (mr *MockRoleModelMockRecorder) Insert(ctx, data any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockRoleModel)(nil).Insert), ctx, data) -} - -// Update mocks base method. -func (m *MockRoleModel) Update(ctx context.Context, data *model.Role) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", ctx, data) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockRoleModelMockRecorder) Update(ctx, data any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockRoleModel)(nil).Update), ctx, data) -} diff --git a/internal/mock/model/role_model_gen.go b/internal/mock/model/role_model_gen.go deleted file mode 100644 index 75f34a2..0000000 --- a/internal/mock/model/role_model_gen.go +++ /dev/null @@ -1,130 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./internal/model/role_model_gen.go -// -// Generated by this command: -// -// mockgen -source=./internal/model/role_model_gen.go -destination=./internal/mock/model/role_model_gen.go -package=mock -// - -// Package mock is a generated GoMock package. -package mock - -import ( - model "app-cloudep-permission-server/internal/model" - context "context" - sql "database/sql" - reflect "reflect" - - gomock "go.uber.org/mock/gomock" -) - -// MockroleModel is a mock of roleModel interface. -type MockroleModel struct { - ctrl *gomock.Controller - recorder *MockroleModelMockRecorder -} - -// MockroleModelMockRecorder is the mock recorder for MockroleModel. -type MockroleModelMockRecorder struct { - mock *MockroleModel -} - -// NewMockroleModel creates a new mock instance. -func NewMockroleModel(ctrl *gomock.Controller) *MockroleModel { - mock := &MockroleModel{ctrl: ctrl} - mock.recorder = &MockroleModelMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockroleModel) EXPECT() *MockroleModelMockRecorder { - return m.recorder -} - -// Delete mocks base method. -func (m *MockroleModel) Delete(ctx context.Context, id int64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", ctx, id) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockroleModelMockRecorder) Delete(ctx, id any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockroleModel)(nil).Delete), ctx, id) -} - -// FindOne mocks base method. -func (m *MockroleModel) FindOne(ctx context.Context, id int64) (*model.Role, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindOne", ctx, id) - ret0, _ := ret[0].(*model.Role) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindOne indicates an expected call of FindOne. -func (mr *MockroleModelMockRecorder) FindOne(ctx, id any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockroleModel)(nil).FindOne), ctx, id) -} - -// FindOneByDisplayName mocks base method. -func (m *MockroleModel) FindOneByDisplayName(ctx context.Context, displayName string) (*model.Role, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindOneByDisplayName", ctx, displayName) - ret0, _ := ret[0].(*model.Role) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindOneByDisplayName indicates an expected call of FindOneByDisplayName. -func (mr *MockroleModelMockRecorder) FindOneByDisplayName(ctx, displayName any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOneByDisplayName", reflect.TypeOf((*MockroleModel)(nil).FindOneByDisplayName), ctx, displayName) -} - -// FindOneByRoleId mocks base method. -func (m *MockroleModel) FindOneByRoleId(ctx context.Context, roleId string) (*model.Role, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindOneByRoleId", ctx, roleId) - ret0, _ := ret[0].(*model.Role) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindOneByRoleId indicates an expected call of FindOneByRoleId. -func (mr *MockroleModelMockRecorder) FindOneByRoleId(ctx, roleId any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOneByRoleId", reflect.TypeOf((*MockroleModel)(nil).FindOneByRoleId), ctx, roleId) -} - -// Insert mocks base method. -func (m *MockroleModel) Insert(ctx context.Context, data *model.Role) (sql.Result, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Insert", ctx, data) - ret0, _ := ret[0].(sql.Result) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Insert indicates an expected call of Insert. -func (mr *MockroleModelMockRecorder) Insert(ctx, data any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockroleModel)(nil).Insert), ctx, data) -} - -// Update mocks base method. -func (m *MockroleModel) Update(ctx context.Context, data *model.Role) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", ctx, data) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockroleModelMockRecorder) Update(ctx, data any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockroleModel)(nil).Update), ctx, data) -} diff --git a/internal/mock/model/role_permission_model.go b/internal/mock/model/role_permission_model.go deleted file mode 100644 index b5301c8..0000000 --- a/internal/mock/model/role_permission_model.go +++ /dev/null @@ -1,100 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./internal/model/role_permission_model.go -// -// Generated by this command: -// -// mockgen -source=./internal/model/role_permission_model.go -destination=./internal/mock/model/role_permission_model.go -package=mock -// - -// Package mock is a generated GoMock package. -package mock - -import ( - model "app-cloudep-permission-server/internal/model" - context "context" - sql "database/sql" - reflect "reflect" - - gomock "go.uber.org/mock/gomock" -) - -// MockRolePermissionModel is a mock of RolePermissionModel interface. -type MockRolePermissionModel struct { - ctrl *gomock.Controller - recorder *MockRolePermissionModelMockRecorder -} - -// MockRolePermissionModelMockRecorder is the mock recorder for MockRolePermissionModel. -type MockRolePermissionModelMockRecorder struct { - mock *MockRolePermissionModel -} - -// NewMockRolePermissionModel creates a new mock instance. -func NewMockRolePermissionModel(ctrl *gomock.Controller) *MockRolePermissionModel { - mock := &MockRolePermissionModel{ctrl: ctrl} - mock.recorder = &MockRolePermissionModelMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockRolePermissionModel) EXPECT() *MockRolePermissionModelMockRecorder { - return m.recorder -} - -// Delete mocks base method. -func (m *MockRolePermissionModel) Delete(ctx context.Context, id int64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", ctx, id) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockRolePermissionModelMockRecorder) Delete(ctx, id any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockRolePermissionModel)(nil).Delete), ctx, id) -} - -// FindOne mocks base method. -func (m *MockRolePermissionModel) FindOne(ctx context.Context, id int64) (*model.RolePermission, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindOne", ctx, id) - ret0, _ := ret[0].(*model.RolePermission) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindOne indicates an expected call of FindOne. -func (mr *MockRolePermissionModelMockRecorder) FindOne(ctx, id any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockRolePermissionModel)(nil).FindOne), ctx, id) -} - -// Insert mocks base method. -func (m *MockRolePermissionModel) Insert(ctx context.Context, data *model.RolePermission) (sql.Result, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Insert", ctx, data) - ret0, _ := ret[0].(sql.Result) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Insert indicates an expected call of Insert. -func (mr *MockRolePermissionModelMockRecorder) Insert(ctx, data any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockRolePermissionModel)(nil).Insert), ctx, data) -} - -// Update mocks base method. -func (m *MockRolePermissionModel) Update(ctx context.Context, data *model.RolePermission) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", ctx, data) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockRolePermissionModelMockRecorder) Update(ctx, data any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockRolePermissionModel)(nil).Update), ctx, data) -} diff --git a/internal/mock/model/role_permission_model_gen.go b/internal/mock/model/role_permission_model_gen.go deleted file mode 100644 index 2c620fd..0000000 --- a/internal/mock/model/role_permission_model_gen.go +++ /dev/null @@ -1,100 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./internal/model/role_permission_model_gen.go -// -// Generated by this command: -// -// mockgen -source=./internal/model/role_permission_model_gen.go -destination=./internal/mock/model/role_permission_model_gen.go -package=mock -// - -// Package mock is a generated GoMock package. -package mock - -import ( - model "app-cloudep-permission-server/internal/model" - context "context" - sql "database/sql" - reflect "reflect" - - gomock "go.uber.org/mock/gomock" -) - -// MockrolePermissionModel is a mock of rolePermissionModel interface. -type MockrolePermissionModel struct { - ctrl *gomock.Controller - recorder *MockrolePermissionModelMockRecorder -} - -// MockrolePermissionModelMockRecorder is the mock recorder for MockrolePermissionModel. -type MockrolePermissionModelMockRecorder struct { - mock *MockrolePermissionModel -} - -// NewMockrolePermissionModel creates a new mock instance. -func NewMockrolePermissionModel(ctrl *gomock.Controller) *MockrolePermissionModel { - mock := &MockrolePermissionModel{ctrl: ctrl} - mock.recorder = &MockrolePermissionModelMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockrolePermissionModel) EXPECT() *MockrolePermissionModelMockRecorder { - return m.recorder -} - -// Delete mocks base method. -func (m *MockrolePermissionModel) Delete(ctx context.Context, id int64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", ctx, id) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockrolePermissionModelMockRecorder) Delete(ctx, id any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockrolePermissionModel)(nil).Delete), ctx, id) -} - -// FindOne mocks base method. -func (m *MockrolePermissionModel) FindOne(ctx context.Context, id int64) (*model.RolePermission, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindOne", ctx, id) - ret0, _ := ret[0].(*model.RolePermission) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindOne indicates an expected call of FindOne. -func (mr *MockrolePermissionModelMockRecorder) FindOne(ctx, id any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockrolePermissionModel)(nil).FindOne), ctx, id) -} - -// Insert mocks base method. -func (m *MockrolePermissionModel) Insert(ctx context.Context, data *model.RolePermission) (sql.Result, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Insert", ctx, data) - ret0, _ := ret[0].(sql.Result) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Insert indicates an expected call of Insert. -func (mr *MockrolePermissionModelMockRecorder) Insert(ctx, data any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockrolePermissionModel)(nil).Insert), ctx, data) -} - -// Update mocks base method. -func (m *MockrolePermissionModel) Update(ctx context.Context, data *model.RolePermission) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", ctx, data) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockrolePermissionModelMockRecorder) Update(ctx, data any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockrolePermissionModel)(nil).Update), ctx, data) -} diff --git a/internal/mock/model/user_role_model.go b/internal/mock/model/user_role_model.go deleted file mode 100644 index 194927e..0000000 --- a/internal/mock/model/user_role_model.go +++ /dev/null @@ -1,115 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./internal/model/user_role_model.go -// -// Generated by this command: -// -// mockgen -source=./internal/model/user_role_model.go -destination=./internal/mock/model/user_role_model.go -package=mock -// - -// Package mock is a generated GoMock package. -package mock - -import ( - model "app-cloudep-permission-server/internal/model" - context "context" - sql "database/sql" - reflect "reflect" - - gomock "go.uber.org/mock/gomock" -) - -// MockUserRoleModel is a mock of UserRoleModel interface. -type MockUserRoleModel struct { - ctrl *gomock.Controller - recorder *MockUserRoleModelMockRecorder -} - -// MockUserRoleModelMockRecorder is the mock recorder for MockUserRoleModel. -type MockUserRoleModelMockRecorder struct { - mock *MockUserRoleModel -} - -// NewMockUserRoleModel creates a new mock instance. -func NewMockUserRoleModel(ctrl *gomock.Controller) *MockUserRoleModel { - mock := &MockUserRoleModel{ctrl: ctrl} - mock.recorder = &MockUserRoleModelMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockUserRoleModel) EXPECT() *MockUserRoleModelMockRecorder { - return m.recorder -} - -// Delete mocks base method. -func (m *MockUserRoleModel) Delete(ctx context.Context, id int64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", ctx, id) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockUserRoleModelMockRecorder) Delete(ctx, id any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockUserRoleModel)(nil).Delete), ctx, id) -} - -// FindOne mocks base method. -func (m *MockUserRoleModel) FindOne(ctx context.Context, id int64) (*model.UserRole, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindOne", ctx, id) - ret0, _ := ret[0].(*model.UserRole) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindOne indicates an expected call of FindOne. -func (mr *MockUserRoleModelMockRecorder) FindOne(ctx, id any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockUserRoleModel)(nil).FindOne), ctx, id) -} - -// FindOneByUid mocks base method. -func (m *MockUserRoleModel) FindOneByUid(ctx context.Context, uid string) (*model.UserRole, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindOneByUid", ctx, uid) - ret0, _ := ret[0].(*model.UserRole) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindOneByUid indicates an expected call of FindOneByUid. -func (mr *MockUserRoleModelMockRecorder) FindOneByUid(ctx, uid any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOneByUid", reflect.TypeOf((*MockUserRoleModel)(nil).FindOneByUid), ctx, uid) -} - -// Insert mocks base method. -func (m *MockUserRoleModel) Insert(ctx context.Context, data *model.UserRole) (sql.Result, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Insert", ctx, data) - ret0, _ := ret[0].(sql.Result) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Insert indicates an expected call of Insert. -func (mr *MockUserRoleModelMockRecorder) Insert(ctx, data any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockUserRoleModel)(nil).Insert), ctx, data) -} - -// Update mocks base method. -func (m *MockUserRoleModel) Update(ctx context.Context, data *model.UserRole) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", ctx, data) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockUserRoleModelMockRecorder) Update(ctx, data any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockUserRoleModel)(nil).Update), ctx, data) -} diff --git a/internal/mock/model/user_role_model_gen.go b/internal/mock/model/user_role_model_gen.go deleted file mode 100644 index 456c290..0000000 --- a/internal/mock/model/user_role_model_gen.go +++ /dev/null @@ -1,115 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./internal/model/user_role_model_gen.go -// -// Generated by this command: -// -// mockgen -source=./internal/model/user_role_model_gen.go -destination=./internal/mock/model/user_role_model_gen.go -package=mock -// - -// Package mock is a generated GoMock package. -package mock - -import ( - model "app-cloudep-permission-server/internal/model" - context "context" - sql "database/sql" - reflect "reflect" - - gomock "go.uber.org/mock/gomock" -) - -// MockuserRoleModel is a mock of userRoleModel interface. -type MockuserRoleModel struct { - ctrl *gomock.Controller - recorder *MockuserRoleModelMockRecorder -} - -// MockuserRoleModelMockRecorder is the mock recorder for MockuserRoleModel. -type MockuserRoleModelMockRecorder struct { - mock *MockuserRoleModel -} - -// NewMockuserRoleModel creates a new mock instance. -func NewMockuserRoleModel(ctrl *gomock.Controller) *MockuserRoleModel { - mock := &MockuserRoleModel{ctrl: ctrl} - mock.recorder = &MockuserRoleModelMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockuserRoleModel) EXPECT() *MockuserRoleModelMockRecorder { - return m.recorder -} - -// Delete mocks base method. -func (m *MockuserRoleModel) Delete(ctx context.Context, id int64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", ctx, id) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockuserRoleModelMockRecorder) Delete(ctx, id any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockuserRoleModel)(nil).Delete), ctx, id) -} - -// FindOne mocks base method. -func (m *MockuserRoleModel) FindOne(ctx context.Context, id int64) (*model.UserRole, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindOne", ctx, id) - ret0, _ := ret[0].(*model.UserRole) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindOne indicates an expected call of FindOne. -func (mr *MockuserRoleModelMockRecorder) FindOne(ctx, id any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockuserRoleModel)(nil).FindOne), ctx, id) -} - -// FindOneByUid mocks base method. -func (m *MockuserRoleModel) FindOneByUid(ctx context.Context, uid string) (*model.UserRole, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindOneByUid", ctx, uid) - ret0, _ := ret[0].(*model.UserRole) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindOneByUid indicates an expected call of FindOneByUid. -func (mr *MockuserRoleModelMockRecorder) FindOneByUid(ctx, uid any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOneByUid", reflect.TypeOf((*MockuserRoleModel)(nil).FindOneByUid), ctx, uid) -} - -// Insert mocks base method. -func (m *MockuserRoleModel) Insert(ctx context.Context, data *model.UserRole) (sql.Result, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Insert", ctx, data) - ret0, _ := ret[0].(sql.Result) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Insert indicates an expected call of Insert. -func (mr *MockuserRoleModelMockRecorder) Insert(ctx, data any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockuserRoleModel)(nil).Insert), ctx, data) -} - -// Update mocks base method. -func (m *MockuserRoleModel) Update(ctx context.Context, data *model.UserRole) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", ctx, data) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockuserRoleModelMockRecorder) Update(ctx, data any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockuserRoleModel)(nil).Update), ctx, data) -} diff --git a/internal/model/permission_model.go b/internal/model/permission_model.go deleted file mode 100755 index 719486a..0000000 --- a/internal/model/permission_model.go +++ /dev/null @@ -1,27 +0,0 @@ -package model - -import ( - "github.com/zeromicro/go-zero/core/stores/cache" - "github.com/zeromicro/go-zero/core/stores/sqlx" -) - -var _ PermissionModel = (*customPermissionModel)(nil) - -type ( - // PermissionModel is an interface to be customized, add more methods here, - // and implement the added methods in customPermissionModel. - PermissionModel interface { - permissionModel - } - - customPermissionModel struct { - *defaultPermissionModel - } -) - -// NewPermissionModel returns a model for the database table. -func NewPermissionModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) PermissionModel { - return &customPermissionModel{ - defaultPermissionModel: newPermissionModel(conn, c, opts...), - } -} diff --git a/internal/model/permission_model_gen.go b/internal/model/permission_model_gen.go deleted file mode 100755 index 74bd248..0000000 --- a/internal/model/permission_model_gen.go +++ /dev/null @@ -1,157 +0,0 @@ -// Code generated by goctl. DO NOT EDIT. - -package model - -import ( - "context" - "database/sql" - "fmt" - "strings" - - "github.com/zeromicro/go-zero/core/stores/builder" - "github.com/zeromicro/go-zero/core/stores/cache" - "github.com/zeromicro/go-zero/core/stores/sqlc" - "github.com/zeromicro/go-zero/core/stores/sqlx" - "github.com/zeromicro/go-zero/core/stringx" -) - -var ( - permissionFieldNames = builder.RawFieldNames(&Permission{}) - permissionRows = strings.Join(permissionFieldNames, ",") - permissionRowsExpectAutoSet = strings.Join(stringx.Remove(permissionFieldNames, "`id`"), ",") - permissionRowsWithPlaceHolder = strings.Join(stringx.Remove(permissionFieldNames, "`id`"), "=?,") + "=?" - - cachePermissionIdPrefix = "cache:permission:id:" - cachePermissionNamePrefix = "cache:permission:name:" -) - -type ( - permissionModel interface { - Insert(ctx context.Context, data *Permission) (sql.Result, error) - FindOne(ctx context.Context, id int64) (*Permission, error) - FindOneByName(ctx context.Context, name string) (*Permission, error) - Update(ctx context.Context, data *Permission) error - Delete(ctx context.Context, id int64) error - } - - defaultPermissionModel struct { - sqlc.CachedConn - table string - } - - Permission struct { - Id int64 `db:"id"` // PK - Parent sql.NullInt64 `db:"parent"` - Name string `db:"name"` - HttpMethod string `db:"http_method"` - HttpPath string `db:"http_path"` - Status int64 `db:"status"` // 狀態 1: 啟用, 2: 關閉 - Type int64 `db:"type"` // 狀態 1: 後台, 2: 前台 - CreateTime int64 `db:"create_time"` // 創建時間 - UpdateTime int64 `db:"update_time"` // 更新時間 - } -) - -func newPermissionModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) *defaultPermissionModel { - return &defaultPermissionModel{ - CachedConn: sqlc.NewConn(conn, c, opts...), - table: "`permission`", - } -} - -func (m *defaultPermissionModel) withSession(session sqlx.Session) *defaultPermissionModel { - return &defaultPermissionModel{ - CachedConn: m.CachedConn.WithSession(session), - table: "`permission`", - } -} - -func (m *defaultPermissionModel) Delete(ctx context.Context, id int64) error { - data, err := m.FindOne(ctx, id) - if err != nil { - return err - } - - permissionIdKey := fmt.Sprintf("%s%v", cachePermissionIdPrefix, id) - permissionNameKey := fmt.Sprintf("%s%v", cachePermissionNamePrefix, data.Name) - _, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { - query := fmt.Sprintf("delete from %s where `id` = ?", m.table) - return conn.ExecCtx(ctx, query, id) - }, permissionIdKey, permissionNameKey) - return err -} - -func (m *defaultPermissionModel) FindOne(ctx context.Context, id int64) (*Permission, error) { - permissionIdKey := fmt.Sprintf("%s%v", cachePermissionIdPrefix, id) - var resp Permission - err := m.QueryRowCtx(ctx, &resp, permissionIdKey, func(ctx context.Context, conn sqlx.SqlConn, v any) error { - query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", permissionRows, m.table) - return conn.QueryRowCtx(ctx, v, query, id) - }) - switch err { - case nil: - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *defaultPermissionModel) FindOneByName(ctx context.Context, name string) (*Permission, error) { - permissionNameKey := fmt.Sprintf("%s%v", cachePermissionNamePrefix, name) - var resp Permission - err := m.QueryRowIndexCtx(ctx, &resp, permissionNameKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v any) (i any, e error) { - query := fmt.Sprintf("select %s from %s where `name` = ? limit 1", permissionRows, m.table) - if err := conn.QueryRowCtx(ctx, &resp, query, name); err != nil { - return nil, err - } - return resp.Id, nil - }, m.queryPrimary) - switch err { - case nil: - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *defaultPermissionModel) Insert(ctx context.Context, data *Permission) (sql.Result, error) { - permissionIdKey := fmt.Sprintf("%s%v", cachePermissionIdPrefix, data.Id) - permissionNameKey := fmt.Sprintf("%s%v", cachePermissionNamePrefix, data.Name) - ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { - query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?)", m.table, permissionRowsExpectAutoSet) - return conn.ExecCtx(ctx, query, data.Parent, data.Name, data.HttpMethod, data.HttpPath, data.Status, data.Type, data.CreateTime, data.UpdateTime) - }, permissionIdKey, permissionNameKey) - return ret, err -} - -func (m *defaultPermissionModel) Update(ctx context.Context, newData *Permission) error { - data, err := m.FindOne(ctx, newData.Id) - if err != nil { - return err - } - - permissionIdKey := fmt.Sprintf("%s%v", cachePermissionIdPrefix, data.Id) - permissionNameKey := fmt.Sprintf("%s%v", cachePermissionNamePrefix, data.Name) - _, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { - query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, permissionRowsWithPlaceHolder) - return conn.ExecCtx(ctx, query, newData.Parent, newData.Name, newData.HttpMethod, newData.HttpPath, newData.Status, newData.Type, newData.CreateTime, newData.UpdateTime, newData.Id) - }, permissionIdKey, permissionNameKey) - return err -} - -func (m *defaultPermissionModel) formatPrimary(primary any) string { - return fmt.Sprintf("%s%v", cachePermissionIdPrefix, primary) -} - -func (m *defaultPermissionModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary any) error { - query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", permissionRows, m.table) - return conn.QueryRowCtx(ctx, v, query, primary) -} - -func (m *defaultPermissionModel) tableName() string { - return m.table -} diff --git a/internal/model/role_model.go b/internal/model/role_model.go deleted file mode 100755 index 7c4c733..0000000 --- a/internal/model/role_model.go +++ /dev/null @@ -1,27 +0,0 @@ -package model - -import ( - "github.com/zeromicro/go-zero/core/stores/cache" - "github.com/zeromicro/go-zero/core/stores/sqlx" -) - -var _ RoleModel = (*customRoleModel)(nil) - -type ( - // RoleModel is an interface to be customized, add more methods here, - // and implement the added methods in customRoleModel. - RoleModel interface { - roleModel - } - - customRoleModel struct { - *defaultRoleModel - } -) - -// NewRoleModel returns a model for the database table. -func NewRoleModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) RoleModel { - return &customRoleModel{ - defaultRoleModel: newRoleModel(conn, c, opts...), - } -} diff --git a/internal/model/role_model_gen.go b/internal/model/role_model_gen.go deleted file mode 100755 index 681eb15..0000000 --- a/internal/model/role_model_gen.go +++ /dev/null @@ -1,179 +0,0 @@ -// Code generated by goctl. DO NOT EDIT. - -package model - -import ( - "context" - "database/sql" - "fmt" - "strings" - - "github.com/zeromicro/go-zero/core/stores/builder" - "github.com/zeromicro/go-zero/core/stores/cache" - "github.com/zeromicro/go-zero/core/stores/sqlc" - "github.com/zeromicro/go-zero/core/stores/sqlx" - "github.com/zeromicro/go-zero/core/stringx" -) - -var ( - roleFieldNames = builder.RawFieldNames(&Role{}) - roleRows = strings.Join(roleFieldNames, ",") - roleRowsExpectAutoSet = strings.Join(stringx.Remove(roleFieldNames, "`id`"), ",") - roleRowsWithPlaceHolder = strings.Join(stringx.Remove(roleFieldNames, "`id`"), "=?,") + "=?" - - cacheRoleIdPrefix = "cache:role:id:" - cacheRoleDisplayNamePrefix = "cache:role:displayName:" - cacheRoleRoleIdPrefix = "cache:role:roleId:" -) - -type ( - roleModel interface { - Insert(ctx context.Context, data *Role) (sql.Result, error) - FindOne(ctx context.Context, id int64) (*Role, error) - FindOneByDisplayName(ctx context.Context, displayName string) (*Role, error) - FindOneByRoleId(ctx context.Context, roleId string) (*Role, error) - Update(ctx context.Context, data *Role) error - Delete(ctx context.Context, id int64) error - } - - defaultRoleModel struct { - sqlc.CachedConn - table string - } - - Role struct { - Id int64 `db:"id"` // PK - RoleId string `db:"role_id"` - DisplayName string `db:"display_name"` // 名稱 - Status int64 `db:"status"` // 狀態 1: 啟用, 2: 禁用 - CreateTime int64 `db:"create_time"` // 創建時間 - UpdateTime int64 `db:"update_time"` // 更新時間 - } -) - -func newRoleModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) *defaultRoleModel { - return &defaultRoleModel{ - CachedConn: sqlc.NewConn(conn, c, opts...), - table: "`role`", - } -} - -func (m *defaultRoleModel) withSession(session sqlx.Session) *defaultRoleModel { - return &defaultRoleModel{ - CachedConn: m.CachedConn.WithSession(session), - table: "`role`", - } -} - -func (m *defaultRoleModel) Delete(ctx context.Context, id int64) error { - data, err := m.FindOne(ctx, id) - if err != nil { - return err - } - - roleDisplayNameKey := fmt.Sprintf("%s%v", cacheRoleDisplayNamePrefix, data.DisplayName) - roleIdKey := fmt.Sprintf("%s%v", cacheRoleIdPrefix, id) - roleRoleIdKey := fmt.Sprintf("%s%v", cacheRoleRoleIdPrefix, data.RoleId) - _, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { - query := fmt.Sprintf("delete from %s where `id` = ?", m.table) - return conn.ExecCtx(ctx, query, id) - }, roleDisplayNameKey, roleIdKey, roleRoleIdKey) - return err -} - -func (m *defaultRoleModel) FindOne(ctx context.Context, id int64) (*Role, error) { - roleIdKey := fmt.Sprintf("%s%v", cacheRoleIdPrefix, id) - var resp Role - err := m.QueryRowCtx(ctx, &resp, roleIdKey, func(ctx context.Context, conn sqlx.SqlConn, v any) error { - query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", roleRows, m.table) - return conn.QueryRowCtx(ctx, v, query, id) - }) - switch err { - case nil: - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *defaultRoleModel) FindOneByDisplayName(ctx context.Context, displayName string) (*Role, error) { - roleDisplayNameKey := fmt.Sprintf("%s%v", cacheRoleDisplayNamePrefix, displayName) - var resp Role - err := m.QueryRowIndexCtx(ctx, &resp, roleDisplayNameKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v any) (i any, e error) { - query := fmt.Sprintf("select %s from %s where `display_name` = ? limit 1", roleRows, m.table) - if err := conn.QueryRowCtx(ctx, &resp, query, displayName); err != nil { - return nil, err - } - return resp.Id, nil - }, m.queryPrimary) - switch err { - case nil: - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *defaultRoleModel) FindOneByRoleId(ctx context.Context, roleId string) (*Role, error) { - roleRoleIdKey := fmt.Sprintf("%s%v", cacheRoleRoleIdPrefix, roleId) - var resp Role - err := m.QueryRowIndexCtx(ctx, &resp, roleRoleIdKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v any) (i any, e error) { - query := fmt.Sprintf("select %s from %s where `role_id` = ? limit 1", roleRows, m.table) - if err := conn.QueryRowCtx(ctx, &resp, query, roleId); err != nil { - return nil, err - } - return resp.Id, nil - }, m.queryPrimary) - switch err { - case nil: - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *defaultRoleModel) Insert(ctx context.Context, data *Role) (sql.Result, error) { - roleDisplayNameKey := fmt.Sprintf("%s%v", cacheRoleDisplayNamePrefix, data.DisplayName) - roleIdKey := fmt.Sprintf("%s%v", cacheRoleIdPrefix, data.Id) - roleRoleIdKey := fmt.Sprintf("%s%v", cacheRoleRoleIdPrefix, data.RoleId) - ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { - query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?)", m.table, roleRowsExpectAutoSet) - return conn.ExecCtx(ctx, query, data.RoleId, data.DisplayName, data.Status, data.CreateTime, data.UpdateTime) - }, roleDisplayNameKey, roleIdKey, roleRoleIdKey) - return ret, err -} - -func (m *defaultRoleModel) Update(ctx context.Context, newData *Role) error { - data, err := m.FindOne(ctx, newData.Id) - if err != nil { - return err - } - - roleDisplayNameKey := fmt.Sprintf("%s%v", cacheRoleDisplayNamePrefix, data.DisplayName) - roleIdKey := fmt.Sprintf("%s%v", cacheRoleIdPrefix, data.Id) - roleRoleIdKey := fmt.Sprintf("%s%v", cacheRoleRoleIdPrefix, data.RoleId) - _, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { - query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, roleRowsWithPlaceHolder) - return conn.ExecCtx(ctx, query, newData.RoleId, newData.DisplayName, newData.Status, newData.CreateTime, newData.UpdateTime, newData.Id) - }, roleDisplayNameKey, roleIdKey, roleRoleIdKey) - return err -} - -func (m *defaultRoleModel) formatPrimary(primary any) string { - return fmt.Sprintf("%s%v", cacheRoleIdPrefix, primary) -} - -func (m *defaultRoleModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary any) error { - query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", roleRows, m.table) - return conn.QueryRowCtx(ctx, v, query, primary) -} - -func (m *defaultRoleModel) tableName() string { - return m.table -} diff --git a/internal/model/role_permission_model.go b/internal/model/role_permission_model.go deleted file mode 100755 index d1d8e30..0000000 --- a/internal/model/role_permission_model.go +++ /dev/null @@ -1,27 +0,0 @@ -package model - -import ( - "github.com/zeromicro/go-zero/core/stores/cache" - "github.com/zeromicro/go-zero/core/stores/sqlx" -) - -var _ RolePermissionModel = (*customRolePermissionModel)(nil) - -type ( - // RolePermissionModel is an interface to be customized, add more methods here, - // and implement the added methods in customRolePermissionModel. - RolePermissionModel interface { - rolePermissionModel - } - - customRolePermissionModel struct { - *defaultRolePermissionModel - } -) - -// NewRolePermissionModel returns a model for the database table. -func NewRolePermissionModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) RolePermissionModel { - return &customRolePermissionModel{ - defaultRolePermissionModel: newRolePermissionModel(conn, c, opts...), - } -} diff --git a/internal/model/role_permission_model_gen.go b/internal/model/role_permission_model_gen.go deleted file mode 100755 index 1a0dabe..0000000 --- a/internal/model/role_permission_model_gen.go +++ /dev/null @@ -1,118 +0,0 @@ -// Code generated by goctl. DO NOT EDIT. - -package model - -import ( - "context" - "database/sql" - "fmt" - "strings" - - "github.com/zeromicro/go-zero/core/stores/builder" - "github.com/zeromicro/go-zero/core/stores/cache" - "github.com/zeromicro/go-zero/core/stores/sqlc" - "github.com/zeromicro/go-zero/core/stores/sqlx" - "github.com/zeromicro/go-zero/core/stringx" -) - -var ( - rolePermissionFieldNames = builder.RawFieldNames(&RolePermission{}) - rolePermissionRows = strings.Join(rolePermissionFieldNames, ",") - rolePermissionRowsExpectAutoSet = strings.Join(stringx.Remove(rolePermissionFieldNames, "`id`"), ",") - rolePermissionRowsWithPlaceHolder = strings.Join(stringx.Remove(rolePermissionFieldNames, "`id`"), "=?,") + "=?" - - cacheRolePermissionIdPrefix = "cache:rolePermission:id:" -) - -type ( - rolePermissionModel interface { - Insert(ctx context.Context, data *RolePermission) (sql.Result, error) - FindOne(ctx context.Context, id int64) (*RolePermission, error) - Update(ctx context.Context, data *RolePermission) error - Delete(ctx context.Context, id int64) error - } - - defaultRolePermissionModel struct { - sqlc.CachedConn - table string - } - - RolePermission struct { - Id int64 `db:"id"` // PK - RoleId sql.NullInt64 `db:"role_id"` // role.id - PermissionId sql.NullInt64 `db:"permission_id"` // permission.id - CreateTime int64 `db:"create_time"` // 創建時間 - UpdateTime int64 `db:"update_time"` // 更新時間 - } -) - -func newRolePermissionModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) *defaultRolePermissionModel { - return &defaultRolePermissionModel{ - CachedConn: sqlc.NewConn(conn, c, opts...), - table: "`role_permission`", - } -} - -func (m *defaultRolePermissionModel) withSession(session sqlx.Session) *defaultRolePermissionModel { - return &defaultRolePermissionModel{ - CachedConn: m.CachedConn.WithSession(session), - table: "`role_permission`", - } -} - -func (m *defaultRolePermissionModel) Delete(ctx context.Context, id int64) error { - rolePermissionIdKey := fmt.Sprintf("%s%v", cacheRolePermissionIdPrefix, id) - _, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { - query := fmt.Sprintf("delete from %s where `id` = ?", m.table) - return conn.ExecCtx(ctx, query, id) - }, rolePermissionIdKey) - return err -} - -func (m *defaultRolePermissionModel) FindOne(ctx context.Context, id int64) (*RolePermission, error) { - rolePermissionIdKey := fmt.Sprintf("%s%v", cacheRolePermissionIdPrefix, id) - var resp RolePermission - err := m.QueryRowCtx(ctx, &resp, rolePermissionIdKey, func(ctx context.Context, conn sqlx.SqlConn, v any) error { - query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", rolePermissionRows, m.table) - return conn.QueryRowCtx(ctx, v, query, id) - }) - switch err { - case nil: - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *defaultRolePermissionModel) Insert(ctx context.Context, data *RolePermission) (sql.Result, error) { - rolePermissionIdKey := fmt.Sprintf("%s%v", cacheRolePermissionIdPrefix, data.Id) - ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { - query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?)", m.table, rolePermissionRowsExpectAutoSet) - return conn.ExecCtx(ctx, query, data.RoleId, data.PermissionId, data.CreateTime, data.UpdateTime) - }, rolePermissionIdKey) - return ret, err -} - -func (m *defaultRolePermissionModel) Update(ctx context.Context, data *RolePermission) error { - rolePermissionIdKey := fmt.Sprintf("%s%v", cacheRolePermissionIdPrefix, data.Id) - _, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { - query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, rolePermissionRowsWithPlaceHolder) - return conn.ExecCtx(ctx, query, data.RoleId, data.PermissionId, data.CreateTime, data.UpdateTime, data.Id) - }, rolePermissionIdKey) - return err -} - -func (m *defaultRolePermissionModel) formatPrimary(primary any) string { - return fmt.Sprintf("%s%v", cacheRolePermissionIdPrefix, primary) -} - -func (m *defaultRolePermissionModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary any) error { - query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", rolePermissionRows, m.table) - return conn.QueryRowCtx(ctx, v, query, primary) -} - -func (m *defaultRolePermissionModel) tableName() string { - return m.table -} diff --git a/internal/model/user_role_model.go b/internal/model/user_role_model.go deleted file mode 100755 index 4f2427f..0000000 --- a/internal/model/user_role_model.go +++ /dev/null @@ -1,27 +0,0 @@ -package model - -import ( - "github.com/zeromicro/go-zero/core/stores/cache" - "github.com/zeromicro/go-zero/core/stores/sqlx" -) - -var _ UserRoleModel = (*customUserRoleModel)(nil) - -type ( - // UserRoleModel is an interface to be customized, add more methods here, - // and implement the added methods in customUserRoleModel. - UserRoleModel interface { - userRoleModel - } - - customUserRoleModel struct { - *defaultUserRoleModel - } -) - -// NewUserRoleModel returns a model for the database table. -func NewUserRoleModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) UserRoleModel { - return &customUserRoleModel{ - defaultUserRoleModel: newUserRoleModel(conn, c, opts...), - } -} diff --git a/internal/model/user_role_model_gen.go b/internal/model/user_role_model_gen.go deleted file mode 100755 index 1a4698d..0000000 --- a/internal/model/user_role_model_gen.go +++ /dev/null @@ -1,155 +0,0 @@ -// Code generated by goctl. DO NOT EDIT. - -package model - -import ( - "context" - "database/sql" - "fmt" - "strings" - - "github.com/zeromicro/go-zero/core/stores/builder" - "github.com/zeromicro/go-zero/core/stores/cache" - "github.com/zeromicro/go-zero/core/stores/sqlc" - "github.com/zeromicro/go-zero/core/stores/sqlx" - "github.com/zeromicro/go-zero/core/stringx" -) - -var ( - userRoleFieldNames = builder.RawFieldNames(&UserRole{}) - userRoleRows = strings.Join(userRoleFieldNames, ",") - userRoleRowsExpectAutoSet = strings.Join(stringx.Remove(userRoleFieldNames, "`id`"), ",") - userRoleRowsWithPlaceHolder = strings.Join(stringx.Remove(userRoleFieldNames, "`id`"), "=?,") + "=?" - - cacheUserRoleIdPrefix = "cache:userRole:id:" - cacheUserRoleUidPrefix = "cache:userRole:uid:" -) - -type ( - userRoleModel interface { - Insert(ctx context.Context, data *UserRole) (sql.Result, error) - FindOne(ctx context.Context, id int64) (*UserRole, error) - FindOneByUid(ctx context.Context, uid string) (*UserRole, error) - Update(ctx context.Context, data *UserRole) error - Delete(ctx context.Context, id int64) error - } - - defaultUserRoleModel struct { - sqlc.CachedConn - table string - } - - UserRole struct { - Id int64 `db:"id"` // PK - Brand string `db:"brand"` - Uid string `db:"uid"` - RoleId string `db:"role_id"` - Status int64 `db:"status"` // 狀態 1: 啟用, 2: 禁用 - CreateTime int64 `db:"create_time"` // 創建時間 - UpdateTime int64 `db:"update_time"` // 更新時間 - } -) - -func newUserRoleModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) *defaultUserRoleModel { - return &defaultUserRoleModel{ - CachedConn: sqlc.NewConn(conn, c, opts...), - table: "`user_role`", - } -} - -func (m *defaultUserRoleModel) withSession(session sqlx.Session) *defaultUserRoleModel { - return &defaultUserRoleModel{ - CachedConn: m.CachedConn.WithSession(session), - table: "`user_role`", - } -} - -func (m *defaultUserRoleModel) Delete(ctx context.Context, id int64) error { - data, err := m.FindOne(ctx, id) - if err != nil { - return err - } - - userRoleIdKey := fmt.Sprintf("%s%v", cacheUserRoleIdPrefix, id) - userRoleUidKey := fmt.Sprintf("%s%v", cacheUserRoleUidPrefix, data.Uid) - _, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { - query := fmt.Sprintf("delete from %s where `id` = ?", m.table) - return conn.ExecCtx(ctx, query, id) - }, userRoleIdKey, userRoleUidKey) - return err -} - -func (m *defaultUserRoleModel) FindOne(ctx context.Context, id int64) (*UserRole, error) { - userRoleIdKey := fmt.Sprintf("%s%v", cacheUserRoleIdPrefix, id) - var resp UserRole - err := m.QueryRowCtx(ctx, &resp, userRoleIdKey, func(ctx context.Context, conn sqlx.SqlConn, v any) error { - query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", userRoleRows, m.table) - return conn.QueryRowCtx(ctx, v, query, id) - }) - switch err { - case nil: - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *defaultUserRoleModel) FindOneByUid(ctx context.Context, uid string) (*UserRole, error) { - userRoleUidKey := fmt.Sprintf("%s%v", cacheUserRoleUidPrefix, uid) - var resp UserRole - err := m.QueryRowIndexCtx(ctx, &resp, userRoleUidKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v any) (i any, e error) { - query := fmt.Sprintf("select %s from %s where `uid` = ? limit 1", userRoleRows, m.table) - if err := conn.QueryRowCtx(ctx, &resp, query, uid); err != nil { - return nil, err - } - return resp.Id, nil - }, m.queryPrimary) - switch err { - case nil: - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *defaultUserRoleModel) Insert(ctx context.Context, data *UserRole) (sql.Result, error) { - userRoleIdKey := fmt.Sprintf("%s%v", cacheUserRoleIdPrefix, data.Id) - userRoleUidKey := fmt.Sprintf("%s%v", cacheUserRoleUidPrefix, data.Uid) - ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { - query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?)", m.table, userRoleRowsExpectAutoSet) - return conn.ExecCtx(ctx, query, data.Brand, data.Uid, data.RoleId, data.Status, data.CreateTime, data.UpdateTime) - }, userRoleIdKey, userRoleUidKey) - return ret, err -} - -func (m *defaultUserRoleModel) Update(ctx context.Context, newData *UserRole) error { - data, err := m.FindOne(ctx, newData.Id) - if err != nil { - return err - } - - userRoleIdKey := fmt.Sprintf("%s%v", cacheUserRoleIdPrefix, data.Id) - userRoleUidKey := fmt.Sprintf("%s%v", cacheUserRoleUidPrefix, data.Uid) - _, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { - query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, userRoleRowsWithPlaceHolder) - return conn.ExecCtx(ctx, query, newData.Brand, newData.Uid, newData.RoleId, newData.Status, newData.CreateTime, newData.UpdateTime, newData.Id) - }, userRoleIdKey, userRoleUidKey) - return err -} - -func (m *defaultUserRoleModel) formatPrimary(primary any) string { - return fmt.Sprintf("%s%v", cacheUserRoleIdPrefix, primary) -} - -func (m *defaultUserRoleModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary any) error { - query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", userRoleRows, m.table) - return conn.QueryRowCtx(ctx, v, query, primary) -} - -func (m *defaultUserRoleModel) tableName() string { - return m.table -} diff --git a/internal/model/vars.go b/internal/model/vars.go deleted file mode 100644 index 69ca814..0000000 --- a/internal/model/vars.go +++ /dev/null @@ -1,5 +0,0 @@ -package model - -import "github.com/zeromicro/go-zero/core/stores/sqlx" - -var ErrNotFound = sqlx.ErrNotFound diff --git a/internal/repository/member_status.go b/internal/repository/member_status.go deleted file mode 100644 index adcf99e..0000000 --- a/internal/repository/member_status.go +++ /dev/null @@ -1,104 +0,0 @@ -package repository - -import ( - "app-cloudep-permission-server/internal/domain/repository" - "code.30cm.net/digimon/library-go/utils/invited_code" - "context" - "fmt" - "github.com/zeromicro/go-zero/core/stores/redis" -) - -type MemberOnlineStatusRepositoryParam struct { - Store *redis.Redis `name:"redis"` -} - -type memberOnlineStatusRepository struct { - store *redis.Redis -} - -// 使用 UID 計算 Bitmap 的 offset -func (t *memberOnlineStatusRepository) uidToOffset(uid string) (int64, error) { - converter := invited_code.MustConverter(10, invited_code.DefaultCodeLen, invited_code.ConvertTable) - - // 將 UID 轉換為整數型,並減去基礎 UID (1000000) - uidInt, err := converter.DecodeFromCode(uid) - if err != nil { - return 0, fmt.Errorf("invalid UID: %w", err) - } - // 以 1000000 作為基準 - baseUID := invited_code.InitAutoId - if uidInt < int64(baseUID) { - return 0, fmt.Errorf("UID smaller than base: %d", baseUID) - } - - return uidInt - int64(baseUID), nil -} - -func (t *memberOnlineStatusRepository) SetMemberOnline(ctx context.Context, uid string) (bool, error) { - offset, err := t.uidToOffset(uid) - if err != nil { - return false, err - } - - // 使用 SET BIT 設置對應的位為 1 - _, err = t.store.SetBit("member_status", offset, 1) - if err != nil { - return false, fmt.Errorf("failed to set member online: %w", err) - } - - return true, nil -} - -func (t *memberOnlineStatusRepository) SetMemberOffline(ctx context.Context, uid string) (bool, error) { - offset, err := t.uidToOffset(uid) - if err != nil { - return false, err - } - - // 使用 SET BIT 設置對應的位為 1 - _, err = t.store.SetBit("member_status", offset, 0) - if err != nil { - return false, fmt.Errorf("failed to set member offline: %w", err) - } - - return true, nil -} - -func (t *memberOnlineStatusRepository) IsMemberOnline(ctx context.Context, uid string) (bool, error) { - offset, err := t.uidToOffset(uid) - if err != nil { - return false, err - } - - // 使用 GET BIT 獲取對應的位,1 表示在線,0 表示離線 - status, err := t.store.GetBit("member_status", offset) - if err != nil { - return false, fmt.Errorf("failed to get member status: %w", err) - } - - return status == 1, nil -} - -func (t *memberOnlineStatusRepository) QueryMemberOnlineList(ctx context.Context, uids []string) ([]repository.MemberOnlineStatusResp, error) { - statusMap := make([]repository.MemberOnlineStatusResp, 0, len(uids)) - - // 遍歷所有用戶的 UID,查詢他們的在線狀態 - for _, uid := range uids { - isOnline, err := t.IsMemberOnline(ctx, uid) - if err != nil { - return nil, fmt.Errorf("failed to query member status for UID %s: %w", uid, err) - } - statusMap = append(statusMap, repository.MemberOnlineStatusResp{ - UID: uid, - Status: isOnline, - }) - } - - return statusMap, nil -} - -func NewMemberOnlineStatusRepository(param MemberOnlineStatusRepositoryParam) repository.MemberOnlineStatusRepository { - return &memberOnlineStatusRepository{ - store: param.Store, - } -} diff --git a/internal/repository/token.go b/internal/repository/token.go deleted file mode 100644 index 7af977a..0000000 --- a/internal/repository/token.go +++ /dev/null @@ -1,312 +0,0 @@ -package repository - -import ( - "app-cloudep-permission-server/internal/domain" - "app-cloudep-permission-server/internal/domain/repository" - "app-cloudep-permission-server/internal/entity" - "context" - "encoding/json" - "errors" - "fmt" - "time" - - ers "code.30cm.net/digimon/library-go/errors" - - "github.com/zeromicro/go-zero/core/stores/redis" -) - -type TokenRepositoryParam struct { - Store *redis.Redis `name:"redis"` -} - -type tokenRepository struct { - store *redis.Redis -} - -func NewTokenRepository(param TokenRepositoryParam) repository.TokenRepository { - return &tokenRepository{ - store: param.Store, - } -} - -func (t *tokenRepository) Create(ctx context.Context, token entity.Token) error { - body, err := json.Marshal(token) - if err != nil { - return ers.ArkInternal("json.Marshal token error", err.Error()) - } - if err := t.store.Pipelined(func(tx redis.Pipeliner) error { - refreshTTL := time.Duration(token.RedisRefreshExpiredSec()) * time.Second - - if err := t.setToken(ctx, tx, token, body, refreshTTL); err != nil { - return err - } - - if err := t.setRefreshToken(ctx, tx, token, refreshTTL); err != nil { - return err - } - - return t.setRelation(ctx, tx, token.UID, token.DeviceID, token.ID, refreshTTL) - }); err != nil { - return repository.RedisPipLineError(err.Error()) - } - - return nil -} - -func (t *tokenRepository) Delete(ctx context.Context, token entity.Token) error { - keys := []string{ - domain.GetAccessTokenRedisKey(token.ID), - domain.RefreshTokenRedisKey.With(token.RefreshToken).ToString(), - } - - if err := t.deleteKeys(ctx, keys...); err != nil { - return repository.RedisPipLineError(err.Error()) - } - - _, _ = t.store.Srem(domain.DeviceTokenRedisKey.With(token.DeviceID).ToString(), token.ID) - _, _ = t.store.Srem(domain.UIDTokenRedisKey.With(token.UID).ToString(), token.ID) - - return nil -} - -func (t *tokenRepository) GetAccessTokenByID(ctx context.Context, id string) (entity.Token, error) { - token, err := t.get(ctx, domain.GetAccessTokenRedisKey(id)) - if err != nil { - return entity.Token{}, err - } - - return token, nil -} - -func (t *tokenRepository) DeleteAccessTokensByUID(ctx context.Context, uid string) error { - tokens, err := t.GetAccessTokensByUID(ctx, uid) - if err != nil { - return err - } - - for _, token := range tokens { - if err := t.Delete(ctx, token); err != nil { - return err - } - } - - return nil -} - -func (t *tokenRepository) DeleteAccessTokenByID(ctx context.Context, ids []string) error { - for _, tokenID := range ids { - token, err := t.GetAccessTokenByID(ctx, tokenID) - if err != nil { - continue - } - - keys := []string{ - domain.GetAccessTokenRedisKey(token.ID), - domain.RefreshTokenRedisKey.With(token.RefreshToken).ToString(), - } - - if err := t.deleteKeys(ctx, keys...); err != nil { - continue - } - - _, _ = t.store.Srem(domain.DeviceTokenRedisKey.With(token.DeviceID).ToString(), token.ID) - _, _ = t.store.Srem(domain.UIDTokenRedisKey.With(token.UID).ToString(), token.ID) - } - - return nil -} - -func (t *tokenRepository) GetAccessTokensByUID(ctx context.Context, uid string) ([]entity.Token, error) { - return t.getTokensBySet(ctx, domain.GetUIDTokenRedisKey(uid)) -} - -func (t *tokenRepository) GetAccessTokensByDeviceID(ctx context.Context, deviceID string) ([]entity.Token, error) { - return t.getTokensBySet(ctx, domain.DeviceTokenRedisKey.With(deviceID).ToString()) -} - -func (t *tokenRepository) DeleteAccessTokensByDeviceID(ctx context.Context, deviceID string) error { - - tokens, err := t.GetAccessTokensByDeviceID(ctx, deviceID) - if err != nil { - return repository.RedisDelError(fmt.Sprintf("GetAccessTokensByDeviceID error: %v", err)) - } - - var keys []string - for _, token := range tokens { - keys = append(keys, domain.GetAccessTokenRedisKey(token.ID)) - keys = append(keys, domain.RefreshTokenRedisKey.With(token.RefreshToken).ToString()) - - } - - err = t.store.Pipelined(func(tx redis.Pipeliner) error { - for _, token := range tokens { - _, _ = t.store.Srem(domain.UIDTokenRedisKey.With(token.UID).ToString(), token.ID) - } - return nil - }) - if err != nil { - return err - } - - if err := t.deleteKeys(ctx, keys...); err != nil { - return err - } - - _, err = t.store.Del(domain.DeviceTokenRedisKey.With(deviceID).ToString()) - return err -} - -func (t *tokenRepository) GetAccessTokenCountByDeviceID(deviceID string) (int, error) { - return t.getCountBySet(domain.DeviceTokenRedisKey.With(deviceID).ToString()) -} - -func (t *tokenRepository) GetAccessTokenCountByUID(uid string) (int, error) { - return t.getCountBySet(domain.UIDTokenRedisKey.With(uid).ToString()) -} - -func (t *tokenRepository) GetAccessTokenByByOneTimeToken(ctx context.Context, oneTimeToken string) (entity.Token, error) { - id, err := t.store.Get(domain.RefreshTokenRedisKey.With(oneTimeToken).ToString()) - if err != nil { - return entity.Token{}, repository.RedisError(fmt.Sprintf("GetAccessTokenByByOneTimeToken store.Get error: %s", err.Error())) - } - - if id == "" { - return entity.Token{}, ers.ResourceNotFound("token key not found in redis", domain.RefreshTokenRedisKey.With(oneTimeToken).ToString()) - } - - return t.GetAccessTokenByID(ctx, id) -} - -func (t *tokenRepository) DeleteOneTimeToken(ctx context.Context, ids []string, tokens []entity.Token) error { - var keys []string - - for _, id := range ids { - keys = append(keys, domain.RefreshTokenRedisKey.With(id).ToString()) - } - - for _, token := range tokens { - keys = append(keys, domain.RefreshTokenRedisKey.With(token.RefreshToken).ToString()) - } - - return t.deleteKeys(ctx, keys...) -} - -func (t *tokenRepository) CreateOneTimeToken(ctx context.Context, key string, ticket entity.Ticket, expires time.Duration) error { - body, err := json.Marshal(ticket) - if err != nil { - return ers.InvalidFormat("CreateOneTimeToken json.Marshal error", err.Error()) - } - - _, err = t.store.SetnxEx(domain.RefreshTokenRedisKey.With(key).ToString(), string(body), int(expires.Seconds())) - if err != nil { - return repository.RedisError(fmt.Sprintf("CreateOneTimeToken store.SetnxEx error: %s", err.Error())) - } - - return nil -} - -// -------------------- Private area -------------------- - -func (t *tokenRepository) get(ctx context.Context, key string) (entity.Token, error) { - body, err := t.store.GetCtx(ctx, key) - if err != nil { - return entity.Token{}, repository.RedisError(fmt.Sprintf("token %s not found in redis: %s", key, err.Error())) - } - - if body == "" { - return entity.Token{}, ers.ResourceNotFound("this token not found") - } - - var token entity.Token - if err := json.Unmarshal([]byte(body), &token); err != nil { - return entity.Token{}, ers.ArkInternal("json.Unmarshal token error", err.Error()) - } - - return token, nil -} - -func (t *tokenRepository) setToken(ctx context.Context, tx redis.Pipeliner, token entity.Token, body []byte, ttl time.Duration) error { - return tx.Set(ctx, domain.GetAccessTokenRedisKey(token.ID), body, ttl).Err() -} - -func (t *tokenRepository) setRefreshToken(ctx context.Context, tx redis.Pipeliner, token entity.Token, ttl time.Duration) error { - if token.RefreshToken != "" { - return tx.Set(ctx, domain.RefreshTokenRedisKey.With(token.RefreshToken).ToString(), token.ID, ttl).Err() - } - return nil -} - -func (t *tokenRepository) setRelation(ctx context.Context, tx redis.Pipeliner, uid, deviceID, tokenID string, ttl time.Duration) error { - if err := tx.SAdd(ctx, domain.UIDTokenRedisKey.With(uid).ToString(), tokenID).Err(); err != nil { - return err - } - - // 設置 UID 鍵的過期時間 - if err := tx.Expire(ctx, domain.UIDTokenRedisKey.With(uid).ToString(), ttl).Err(); err != nil { - return err - } - - if err := tx.SAdd(ctx, domain.DeviceTokenRedisKey.With(deviceID).ToString(), tokenID).Err(); err != nil { - return err - } - - // 設置 deviceID 鍵的過期時間 - if err := tx.Expire(ctx, domain.DeviceTokenRedisKey.With(deviceID).ToString(), ttl).Err(); err != nil { - return err - } - - return nil -} - -func (t *tokenRepository) deleteKeys(ctx context.Context, keys ...string) error { - return t.store.Pipelined(func(tx redis.Pipeliner) error { - for _, key := range keys { - if err := tx.Del(ctx, key).Err(); err != nil { - return repository.RedisDelError(fmt.Sprintf("store.Del key error: %v", err)) - } - } - return nil - }) -} - -func (t *tokenRepository) getTokensBySet(ctx context.Context, setKey string) ([]entity.Token, error) { - ids, err := t.store.Smembers(setKey) - if err != nil { - if errors.Is(err, redis.Nil) { - return nil, nil - } - return nil, repository.RedisError(fmt.Sprintf("getTokensBySet store.Get %s error: %v", setKey, err.Error())) - } - - var tokens []entity.Token - var deleteTokens []string - now := time.Now().Unix() - for _, id := range ids { - token, err := t.get(ctx, domain.GetAccessTokenRedisKey(id)) - if err != nil { - deleteTokens = append(deleteTokens, id) - continue - } - - if int64(token.ExpiresIn) < now { - deleteTokens = append(deleteTokens, id) - continue - } - - tokens = append(tokens, token) - } - - if len(deleteTokens) > 0 { - _ = t.DeleteAccessTokenByID(ctx, deleteTokens) - } - - return tokens, nil -} - -func (t *tokenRepository) getCountBySet(setKey string) (int, error) { - count, err := t.store.Scard(setKey) - if err != nil { - return 0, err - } - return int(count), nil -} diff --git a/internal/server/tokenservice/token_service_server.go b/internal/server/tokenservice/token_service_server.go index c6d0352..b662fa8 100644 --- a/internal/server/tokenservice/token_service_server.go +++ b/internal/server/tokenservice/token_service_server.go @@ -1,4 +1,5 @@ // Code generated by goctl. DO NOT EDIT. +// goctl 1.7.3 // Source: permission.proto package server @@ -6,14 +7,14 @@ package server import ( "context" - "app-cloudep-permission-server/gen_result/pb/permission" - tokenservicelogic "app-cloudep-permission-server/internal/logic/tokenservice" - "app-cloudep-permission-server/internal/svc" + "code.30cm.net/digimon/app-cloudep-permission-server/gen_result/pb/app-cloudep-permission-server" + "code.30cm.net/digimon/app-cloudep-permission-server/internal/logic/tokenservice" + "code.30cm.net/digimon/app-cloudep-permission-server/internal/svc" ) type TokenServiceServer struct { svcCtx *svc.ServiceContext - permission.UnimplementedTokenServiceServer + app_cloudep_permission_server.UnimplementedTokenServiceServer } func NewTokenServiceServer(svcCtx *svc.ServiceContext) *TokenServiceServer { @@ -23,61 +24,61 @@ func NewTokenServiceServer(svcCtx *svc.ServiceContext) *TokenServiceServer { } // NewToken 建立一個新的 Token,例如:AccessToken -func (s *TokenServiceServer) NewToken(ctx context.Context, in *permission.AuthorizationReq) (*permission.TokenResp, error) { +func (s *TokenServiceServer) NewToken(ctx context.Context, in *app_cloudep_permission_server.AuthorizationReq) (*app_cloudep_permission_server.TokenResp, error) { l := tokenservicelogic.NewNewTokenLogic(ctx, s.svcCtx) return l.NewToken(in) } // RefreshToken 更新目前的token 以及裡面包含的一次性 Token -func (s *TokenServiceServer) RefreshToken(ctx context.Context, in *permission.RefreshTokenReq) (*permission.RefreshTokenResp, error) { +func (s *TokenServiceServer) RefreshToken(ctx context.Context, in *app_cloudep_permission_server.RefreshTokenReq) (*app_cloudep_permission_server.RefreshTokenResp, error) { l := tokenservicelogic.NewRefreshTokenLogic(ctx, s.svcCtx) return l.RefreshToken(in) } // CancelToken 取消 Token,也包含他裡面的 One Time Toke -func (s *TokenServiceServer) CancelToken(ctx context.Context, in *permission.CancelTokenReq) (*permission.OKResp, error) { +func (s *TokenServiceServer) CancelToken(ctx context.Context, in *app_cloudep_permission_server.CancelTokenReq) (*app_cloudep_permission_server.OKResp, error) { l := tokenservicelogic.NewCancelTokenLogic(ctx, s.svcCtx) return l.CancelToken(in) } // ValidationToken 驗證這個 Token 有沒有效 -func (s *TokenServiceServer) ValidationToken(ctx context.Context, in *permission.ValidationTokenReq) (*permission.ValidationTokenResp, error) { +func (s *TokenServiceServer) ValidationToken(ctx context.Context, in *app_cloudep_permission_server.ValidationTokenReq) (*app_cloudep_permission_server.ValidationTokenResp, error) { l := tokenservicelogic.NewValidationTokenLogic(ctx, s.svcCtx) return l.ValidationToken(in) } // CancelTokens 取消 Token 從UID 視角,以及 token id 視角出發, UID 登出,底下所有 Device ID 也要登出, Token ID 登出, 所有 UID + Device 都要登出 -func (s *TokenServiceServer) CancelTokens(ctx context.Context, in *permission.DoTokenByUIDReq) (*permission.OKResp, error) { +func (s *TokenServiceServer) CancelTokens(ctx context.Context, in *app_cloudep_permission_server.DoTokenByUIDReq) (*app_cloudep_permission_server.OKResp, error) { l := tokenservicelogic.NewCancelTokensLogic(ctx, s.svcCtx) return l.CancelTokens(in) } // CancelTokenByDeviceId 取消 Token, 從 Device 視角出發,可以選,登出這個Device 下所有 token ,登出這個Device 下指定token -func (s *TokenServiceServer) CancelTokenByDeviceId(ctx context.Context, in *permission.DoTokenByDeviceIDReq) (*permission.OKResp, error) { +func (s *TokenServiceServer) CancelTokenByDeviceId(ctx context.Context, in *app_cloudep_permission_server.DoTokenByDeviceIDReq) (*app_cloudep_permission_server.OKResp, error) { l := tokenservicelogic.NewCancelTokenByDeviceIdLogic(ctx, s.svcCtx) return l.CancelTokenByDeviceId(in) } // GetUserTokensByDeviceId 取得目前所對應的 DeviceID 所存在的 Tokens -func (s *TokenServiceServer) GetUserTokensByDeviceId(ctx context.Context, in *permission.DoTokenByDeviceIDReq) (*permission.Tokens, error) { +func (s *TokenServiceServer) GetUserTokensByDeviceId(ctx context.Context, in *app_cloudep_permission_server.DoTokenByDeviceIDReq) (*app_cloudep_permission_server.Tokens, error) { l := tokenservicelogic.NewGetUserTokensByDeviceIdLogic(ctx, s.svcCtx) return l.GetUserTokensByDeviceId(in) } // GetUserTokensByUid 取得目前所對應的 UID 所存在的 Tokens -func (s *TokenServiceServer) GetUserTokensByUid(ctx context.Context, in *permission.QueryTokenByUIDReq) (*permission.Tokens, error) { +func (s *TokenServiceServer) GetUserTokensByUid(ctx context.Context, in *app_cloudep_permission_server.QueryTokenByUIDReq) (*app_cloudep_permission_server.Tokens, error) { l := tokenservicelogic.NewGetUserTokensByUidLogic(ctx, s.svcCtx) return l.GetUserTokensByUid(in) } // NewOneTimeToken 建立一次性使用,例如:RefreshToken -func (s *TokenServiceServer) NewOneTimeToken(ctx context.Context, in *permission.CreateOneTimeTokenReq) (*permission.CreateOneTimeTokenResp, error) { +func (s *TokenServiceServer) NewOneTimeToken(ctx context.Context, in *app_cloudep_permission_server.CreateOneTimeTokenReq) (*app_cloudep_permission_server.CreateOneTimeTokenResp, error) { l := tokenservicelogic.NewNewOneTimeTokenLogic(ctx, s.svcCtx) return l.NewOneTimeToken(in) } // CancelOneTimeToken 取消一次性使用 -func (s *TokenServiceServer) CancelOneTimeToken(ctx context.Context, in *permission.CancelOneTimeTokenReq) (*permission.OKResp, error) { +func (s *TokenServiceServer) CancelOneTimeToken(ctx context.Context, in *app_cloudep_permission_server.CancelOneTimeTokenReq) (*app_cloudep_permission_server.OKResp, error) { l := tokenservicelogic.NewCancelOneTimeTokenLogic(ctx, s.svcCtx) return l.CancelOneTimeToken(in) } diff --git a/internal/svc/service_context.go b/internal/svc/service_context.go index 17c9abe..1e77b14 100644 --- a/internal/svc/service_context.go +++ b/internal/svc/service_context.go @@ -1,42 +1,13 @@ package svc -import ( - "app-cloudep-permission-server/internal/config" - "app-cloudep-permission-server/internal/domain/repository" - repo "app-cloudep-permission-server/internal/repository" - - ers "code.30cm.net/digimon/library-go/errors" - "code.30cm.net/digimon/library-go/errors/code" - vi "code.30cm.net/digimon/library-go/validator" - "github.com/zeromicro/go-zero/core/stores/redis" - "github.com/zeromicro/go-zero/core/stores/sqlx" -) +import "code.30cm.net/digimon/app-cloudep-permission-server/internal/config" type ServiceContext struct { Config config.Config - Conn sqlx.SqlConn - - Validate vi.Validate - Redis redis.Redis - TokenRedisRepo repository.TokenRepository } func NewServiceContext(c config.Config) *ServiceContext { - ers.Scope = code.CloudEPPermission - sqlConn := sqlx.NewMysql(c.DB.DsnString) - - newRedis, err := redis.NewRedis(c.RedisCluster, redis.Cluster()) - if err != nil { - panic(err) - } - return &ServiceContext{ - Conn: sqlConn, - Config: c, - Validate: vi.MustValidator(), - Redis: *newRedis, - TokenRedisRepo: repo.NewTokenRepository(repo.TokenRepositoryParam{ - Store: newRedis, - }), + Config: c, } } diff --git a/internal/usecase/bitmap.go b/internal/usecase/bitmap.go deleted file mode 100644 index 6660472..0000000 --- a/internal/usecase/bitmap.go +++ /dev/null @@ -1,117 +0,0 @@ -package usecase - -// Bitmap 基礎結構 -// Bitmap 是一個位圖結構,使用 byte slice 來表示大量的位(bit)。 -// 每個 byte 由 8 個位組成,因此可以高效地管理大量的開關狀態(true/false)。 -// Bitmap 的優點在於它能節省空間,尤其是在需要大量布爾值的場合。 -// 缺點是,如果需要動態擴充 Bitmap 的大小,會導致效率下降,因為需要重新分配和移動內存。 -// 因此,最好在初始化時就規劃好所需的位數大小,避免在之後頻繁擴充。 - -type Bitmap []byte - -// MakeBitmapWithBitSize 通過指定的 bit 數創建一個新的 Bitmap。 -// 參數 nBits 表示所需的位(bit)數。 -// 如果指定的位數少於 64,則默認將 Bitmap 初始化為 64 位(這是最低的限制)。 -// 此外,位數(nBits)會被自動調整為 8 的倍數,以適配 byte 的長度(每 8 位為一個 byte)。 -// 返回值是一個 Bitmap(byte slice),其大小根據位數確定。 -func MakeBitmapWithBitSize(nBits int) Bitmap { - // 如果指定的位數少於 64,則設置為 64 位(8 個 byte) - if nBits < 64 { - nBits = 64 - } - // 計算需要的 byte 數,確保每 8 位為一個 byte,並調整 nBits 以達到 8 的倍數 - return MustBitMap((nBits + 7) / 8) -} - -// MustBitMap 根據指定的 byte 數創建一個 Bitmap(byte slice)。 -// 參數 nBytes 表示所需的 byte 數。 -// 返回值是一個長度為 nBytes 的 Bitmap。 -func MustBitMap(nBytes int) Bitmap { - // 使用 make 函數創建一個 byte slice,大小為 nBytes。 - return make([]byte, nBytes) -} - -// SetTrue 設置指定位置的 bit 為 true(1)。 -// 參數 bitPos 是需要設置的位的位置(以 0 為基準的位索引)。 -// 這個操作會找到該 bit 所在的 byte,然後通過位運算將該位置的 bit 設置為 1。 -func (b Bitmap) SetTrue(bitPos uint32) { - // |= 是一種位運算的複合賦值運算符,表示將左邊的變數與右邊的值進行 位或運算(bitwise OR),並將結果賦值 - b[bitPos/8] |= 1 << (bitPos % 8) -} - -// SetFalse 設置指定位置的 bit 為 false(0)。 -// 參數 bitPos 是需要設置的位的位置(以 0 為基準的位索引)。 -// 這個操作會找到該 bit 所在的 byte,然後通過位運算將該位置的 bit 設置為 0。 -func (b Bitmap) SetFalse(bitPos uint32) { - // 取出對應 byte,使用位與和取反運算將對應的 bit 設置為 0 - // 假設我們有以下情況: - - // • b[1](即 b[bitPos/8])是 10101111(十進制 175)。 - // • bitPos = 10,也就是我們想清除第 10 位的值。 - // - // 操作步驟: - // - // 1. bitPos/8 = 1:所以我們要修改 b[1] 這個 byte。 - // 2. bitPos % 8 = 2:表示我們要清除的位是這個 byte 中的第 3 位(從右數起第 3 位)。 - // 3. 1 << (bitPos % 8) = 1 << 2 = 00000100:生成位掩碼 00000100。 - // 4. 取反:^(1 << 2) = ^00000100 = 11111011,這樣的掩碼表示除了第 3 位,其他位都是 1。 - // 5. 位與運算:10101111 & 11111011 = 10101011,結果將第 3 位清除,其餘位保持不變。即,b[1] 變成了 10101011(十進制 171)。 - // &= 是一種 位與運算的複合賦值運算符,表示將左邊的變數與右邊的值進行 位與運算(bitwise AND),然後將結果賦值給左邊的變數。 - b[bitPos/8] &= ^(1 << (bitPos % 8)) -} - -// IsTrue 檢查指定位置的 bit 是否為 true(1)。 -// 參數 bitPos 是要檢查的位的位置(以 0 為基準的位索引)。 -// 如果該 bit 是 1,則返回 true;否則返回 false。 -func (b Bitmap) IsTrue(bitPos uint32) bool { - /* - 這一行程式碼 b[bitPos/8]&(1<<(bitPos%8)) != 0 是用來檢查 指定位(bit) 是否為 true(1), - 它的核心是位運算。讓我們逐步拆解並解釋這一行程式碼: - - 1. 背景知識: - - • 位運算 是在二進制層面操作數字。每個 byte 有 8 個位(bit),所以位圖結構是以 byte 來表示位的集合。 - • b 是一個 Bitmap 結構,也就是 []byte,即 byte 的切片。 - • bitPos 是一個 uint32 類型的變數,表示我們想要檢查的位(bit)在整個位圖中的索引(位置)。 - 3. 完整流程舉例: - 假設: - • b = []byte{0b10101010, 0b01010101} (即二進制表示的兩個 byte,分別是 10101010 和 01010101)。 - • bitPos = 10(我們要檢查第 10 位是否為 1)。 - 操作順序: - - 1. 計算 bitPos/8 = 10/8 = 1,所以我們要檢查的是第二個 byte:0b01010101。 - 2. 計算 bitPos%8 = 10%8 = 2,所以我們要檢查的是該 byte 中的第 3 位(從右數起第 3 位)。 - 3. 位移:1 << 2 = 00000100。 - 4. 位與:0b01010101 & 0b00000100 = 0b00000100(因為該 byte 的第 3 位是 1,結果不等於 0)。 - 5. 判斷結果:結果不等於 0,因此第 10 位是 1(true)。 - - 4. 總結: - - • b[bitPos/8]&(1<<(bitPos%8)) != 0 是一個經典的位操作,用來檢查位圖中某一個位是否為 1。 - • bitPos/8 找到對應的 byte,bitPos % 8 找到該位在這個 byte 中的具體位置。 - • 最後的位與運算和比較用來確定該位的狀態是 true(1)還是 false(0)。 - */ - return b[bitPos/8]&(1<<(bitPos%8)) != 0 -} - -// Reset 重置 Bitmap,使所有的 bit 都設置為 false(0)。 -// 這個操作會將整個 Bitmap 的所有 byte 都設置為 0,從而達到重置的效果。 -func (b Bitmap) Reset() { - // 迭代 Bitmap 中的每個 byte,並將其設置為 0 - for i := range b { - b[i] = 0 - } -} - -// ByteSize 返回 Bitmap 的 byte 長度。 -// 這個函數返回 Bitmap 目前占用的 byte 數量,該值等於 Bitmap 的長度(slice 的長度)。 -func (b Bitmap) ByteSize() int { - return len(b) -} - -// BitSize 返回 Bitmap 的位(bit)長度。 -// 這個函數通過將 byte 長度乘以 8 來計算 Bitmap 中的總位數。 -func (b Bitmap) BitSize() int { - // 每個 byte 包含 8 個 bit,因此將 byte 長度乘以 8 - return len(b) * 8 -} diff --git a/internal/usecase/bitmap_benchmark_test.go b/internal/usecase/bitmap_benchmark_test.go deleted file mode 100644 index 03545b2..0000000 --- a/internal/usecase/bitmap_benchmark_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package usecase - -import "testing" - -// 基準測試 SetTrue 函數,測試在不同大小的 Bitmap 上設置位元為 true 的效能 -func BenchmarkBitmapSetTrue(b *testing.B) { - // 以 10^6 位元作為基準測試的 Bitmap 大小 - bitmap := MakeBitmapWithBitSize(1000000) - b.ResetTimer() // 重設計時器,排除初始化的時間 - - for i := 0; i < b.N; i++ { - bitmap.SetTrue(uint32(i % 1000000)) // 設置位元為 true - } -} - -// 基準測試 SetFalse 函數,測試在不同大小的 Bitmap 上清除位元為 false 的效能 -func BenchmarkBitmapSetFalse(b *testing.B) { - // 以 10^6 位元作為基準測試的 Bitmap 大小 - bitmap := MakeBitmapWithBitSize(1000000) - b.ResetTimer() // 重設計時器,排除初始化的時間 - - for i := 0; i < b.N; i++ { - bitmap.SetFalse(uint32(i % 1000000)) // 清除位元為 false - } -} - -// 基準測試 IsTrue 函數,測試在不同大小的 Bitmap 上檢查位元狀態的效能 -func BenchmarkBitmapIsTrue(b *testing.B) { - // 以 10^6 位元作為基準測試的 Bitmap 大小 - bitmap := MakeBitmapWithBitSize(1000000) - b.ResetTimer() // 重設計時器,排除初始化的時間 - - for i := 0; i < b.N; i++ { - _ = bitmap.IsTrue(uint32(i % 1000000)) // 檢查位元是否為 true - } -} - -// 基準測試 Reset 函數,測試重置不同大小的 Bitmap 的效能 -func BenchmarkBitmapReset(b *testing.B) { - // 以 10^6 位元作為基準測試的 Bitmap 大小 - bitmap := MakeBitmapWithBitSize(1000000) - b.ResetTimer() // 重設計時器,排除初始化的時間 - - for i := 0; i < b.N; i++ { - bitmap.Reset() // 重置 Bitmap - } -} - -// 基準測試 BitSize 函數,測試返回位圖的 bit 長度的效能 -func BenchmarkBitmapBitSize(b *testing.B) { - // 以 10^6 位元作為基準測試的 Bitmap 大小 - bitmap := MakeBitmapWithBitSize(1000000) - b.ResetTimer() // 重設計時器,排除初始化的時間 - - for i := 0; i < b.N; i++ { - _ = bitmap.BitSize(0) // 測試返回位圖的 bit 長度 - } -} - -// 基準測試 ByteSize 函數,測試返回位圖的 byte 長度的效能 -func BenchmarkBitmapByteSize(b *testing.B) { - // 以 10^6 位元作為基準測試的 Bitmap 大小 - bitmap := MakeBitmapWithBitSize(1000000) - b.ResetTimer() // 重設計時器,排除初始化的時間 - - for i := 0; i < b.N; i++ { - _ = bitmap.ByteSize(0) // 測試返回位圖的 byte 長度 - } -} diff --git a/internal/usecase/bitmap_test.go b/internal/usecase/bitmap_test.go deleted file mode 100644 index 544cf45..0000000 --- a/internal/usecase/bitmap_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package usecase - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestBitmap_SetTrueAndIsTrue(t *testing.T) { - tests := []struct { - name string - bitPos uint32 - expected bool - }{ - {"Set bit 0 to true", 0, true}, - {"Set bit 1 to true", 1, true}, - {"Set bit 63 to true", 63, true}, - {"Set bit 64 to true", 64, true}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - bitmap := MakeBitmapWithBitSize(128) // 初始化一個 128 位的 Bitmap - bitmap.SetTrue(tt.bitPos) - result := bitmap.IsTrue(tt.bitPos) - assert.Equal(t, tt.expected, result) - }) - } -} - -func TestBitmap_SetFalse(t *testing.T) { - tests := []struct { - name string - bitPos uint32 - expected bool - }{ - {"Set bit 0 to false", 0, false}, - {"Set bit 1 to false", 1, false}, - {"Set bit 63 to false", 63, false}, - {"Set bit 64 to false", 64, false}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - bitmap := MakeBitmapWithBitSize(128) // 初始化一個 128 位的 Bitmap - bitmap.SetTrue(tt.bitPos) // 先設置該 bit 為 true - bitmap.SetFalse(tt.bitPos) // 然後設置該 bit 為 false - result := bitmap.IsTrue(tt.bitPos) - assert.Equal(t, tt.expected, result) - }) - } -} - -func TestBitmap_Reset(t *testing.T) { - bitmap := MakeBitmapWithBitSize(128) // 初始化一個 128 位的 Bitmap - bitmap.SetTrue(0) - bitmap.SetTrue(64) - - // 確認 bit 0 和 bit 64 是 true - assert.True(t, bitmap.IsTrue(0)) - assert.True(t, bitmap.IsTrue(64)) - - bitmap.Reset() // 重置位圖 - - // 確認所有的位都已經重置為 false - assert.False(t, bitmap.IsTrue(0)) - assert.False(t, bitmap.IsTrue(64)) -} - -func TestBitmap_ByteSize(t *testing.T) { - bitmap := MakeBitmapWithBitSize(64) // 初始化一個 64 位的 Bitmap - assert.Equal(t, 8, bitmap.ByteSize(0)) // 64 位應該佔用 8 個 byte -} - -func TestBitmap_BitSize(t *testing.T) { - bitmap := MakeBitmapWithBitSize(128) // 初始化一個 128 位的 Bitmap - assert.Equal(t, 128, bitmap.BitSize(0)) // 128 位應該有 128 個 bit -} diff --git a/permission.go b/permission.go index 465f2da..0b614d3 100644 --- a/permission.go +++ b/permission.go @@ -4,10 +4,10 @@ import ( "flag" "fmt" - "app-cloudep-permission-server/gen_result/pb/permission" - "app-cloudep-permission-server/internal/config" - tokenserviceServer "app-cloudep-permission-server/internal/server/tokenservice" - "app-cloudep-permission-server/internal/svc" + "code.30cm.net/digimon/app-cloudep-permission-server/gen_result/pb/app-cloudep-permission-server" + "code.30cm.net/digimon/app-cloudep-permission-server/internal/config" + tokenserviceServer "code.30cm.net/digimon/app-cloudep-permission-server/internal/server/tokenservice" + "code.30cm.net/digimon/app-cloudep-permission-server/internal/svc" "github.com/zeromicro/go-zero/core/conf" "github.com/zeromicro/go-zero/core/service" @@ -26,7 +26,7 @@ func main() { ctx := svc.NewServiceContext(c) s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) { - permission.RegisterTokenServiceServer(grpcServer, tokenserviceServer.NewTokenServiceServer(ctx)) + app_cloudep_permission_server.RegisterTokenServiceServer(grpcServer, tokenserviceServer.NewTokenServiceServer(ctx)) if c.Mode == service.DevMode || c.Mode == service.TestMode { reflection.Register(grpcServer) diff --git a/pkg/domain/entity/ticket.go b/pkg/domain/entity/ticket.go new file mode 100644 index 0000000..6fa0fed --- /dev/null +++ b/pkg/domain/entity/ticket.go @@ -0,0 +1,6 @@ +package entity + +type Ticket struct { + Data interface{} `json:"data"` + Token Token `json:"token"` +} diff --git a/pkg/domain/entity/token.go b/pkg/domain/entity/token.go new file mode 100644 index 0000000..6de960a --- /dev/null +++ b/pkg/domain/entity/token.go @@ -0,0 +1,56 @@ +package entity + +import "time" + +// 時間都以 UnixNano 來做 + +// Token 定義 +type Token struct { + ID string `json:"id"` // Token 的唯一標識符 + UID string `json:"uid"` // UID + DeviceID string `json:"device_id"` // Device + AccessToken string `json:"access_token"` // Access Token + ExpiresIn int64 `json:"expires_in"` // 存取 Token 的有效時間 UnixNano e.g 過期時間的Expire unix nano + AccessCreateAt int64 `json:"access_create_at"` // 存取 Token 的創建時間 timestamp + RefreshToken string `json:"refresh_token"` // 刷新 Token one time token + RefreshExpiresIn int64 `json:"refresh_expires_in"` // 刷新 Token one time token 的有效時間 UnixNano e.g 過期時間的Expire unix nano + RefreshCreateAt int64 `json:"refresh_create_at"` // 刷新 Token one time token 的創建時間 +} + +// AccessTokenExpires 返回存取 Token 的有效期(以秒為單位)。 +func (t *Token) AccessTokenExpires() time.Duration { + return time.Duration(t.ExpiresIn) * time.Second +} + +// RefreshTokenExpires 返回刷新 Token 的有效期(以秒為單位)。 +func (t *Token) RefreshTokenExpires() time.Duration { + return time.Duration(t.RefreshExpiresIn) * time.Second +} + +// RefreshTokenExpiresUnix 返回刷新 Token 的到期時間(UnixNano 時間戳)。 +func (t *Token) RefreshTokenExpiresUnix() int64 { + return time.Now().Add(t.RefreshTokenExpires()).UnixNano() +} + +// IsExpires 檢查存取 Token 是否已過期。如果存取 Token 的創建時間加上其有效期早於當前時間,則返回 true。 +func (t *Token) IsExpires() bool { + accessCreateAt := time.Unix(0, t.AccessCreateAt) + + return accessCreateAt.Add(t.AccessTokenExpires()).Before(time.Now()) +} + +// RedisExpiredSec 返回存取 Token 在 Redis 中的剩餘有效時間(秒)。 +// 計算方法為:從到期時間的 Unix 時間戳減去當前時間。 +func (t *Token) RedisExpiredSec() int64 { + sec := time.Unix(0, t.ExpiresIn).Sub(time.Now().UTC()) + + return int64(sec.Seconds()) +} + +// RedisRefreshExpiredSec 返回刷新 Token 在 Redis 中的剩餘有效時間(秒)。 +// 計算方法為:從刷新到期時間的 Unix 時間戳減去當前時間。 +func (t *Token) RedisRefreshExpiredSec() int64 { + sec := time.Unix(0, t.RefreshExpiresIn).Sub(time.Now().UTC()) + + return int64(sec.Seconds()) +} diff --git a/pkg/domain/entity/token_test.go b/pkg/domain/entity/token_test.go new file mode 100644 index 0000000..6dc1742 --- /dev/null +++ b/pkg/domain/entity/token_test.go @@ -0,0 +1,163 @@ +package entity + +import ( + "testing" + "time" +) + +// TestAccessTokenExpires 測試 AccessTokenExpires 方法是否能正確將 ExpiresIn 轉換成 time.Duration(秒) +func TestAccessTokenExpires(t *testing.T) { + tests := []struct { + name string // 測試案例名稱 + expiresIn int64 // 輸入的 ExpiresIn 值,單位為秒 + want time.Duration // 預期返回的時間間隔 + }{ + {"zero expiration", 0, 0}, + {"1 second expiration", 1, 1 * time.Second}, + {"60 seconds expiration", 60, 60 * time.Second}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 建立一個 Token 實例,僅設置 ExpiresIn 欄位 + token := Token{ + ExpiresIn: tt.expiresIn, + } + // 呼叫 AccessTokenExpires 方法 + got := token.AccessTokenExpires() // ex 1m0s, 1s, 0s etc .... + // 檢查返回值是否與預期相符 + if got != tt.want { + t.Errorf("AccessTokenExpires() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestRefreshTokenExpires 測試 RefreshTokenExpires 方法是否能正確將 RefreshExpiresIn(秒)轉換成 time.Duration +func TestRefreshTokenExpires(t *testing.T) { + tests := []struct { + name string + refreshExpiresIn int64 + want time.Duration + }{ + {"zero", 0, 0}, + {"1 second", 1, 1 * time.Second}, + {"60 seconds", 60, 60 * time.Second}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + token := Token{ + RefreshExpiresIn: tt.refreshExpiresIn, + } + got := token.RefreshTokenExpires() + if got != tt.want { + t.Errorf("RefreshTokenExpires() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestRefreshTokenExpiresUnix 測試 RefreshTokenExpiresUnix 方法返回的 UnixNano 時間戳是否大致符合預期 +func TestRefreshTokenExpiresUnix(t *testing.T) { + // 設定 refreshExpiresIn 為 60 秒 + token := Token{ + RefreshExpiresIn: 60, + } + // 取得測試開始時的時間 + now := time.Now() + // 預期值:now + 60 秒 + expected := now.Add(60 * time.Second).UnixNano() + got := token.RefreshTokenExpiresUnix() + + // 設定允許誤差 50 毫秒 + tolerance := int64(50 * time.Millisecond) + if got < expected-tolerance || got > expected+tolerance { + t.Errorf("RefreshTokenExpiresUnix() = %v, want approx %v", got, expected) + } +} + +// TestIsExpires 測試 IsExpires 方法判斷 token 是否過期 +func TestIsExpires(t *testing.T) { + now := time.Now() + + // 測試未過期:token 創建時間為現在,過期時長為 10 秒 + tokenValid := Token{ + AccessCreateAt: now.UnixNano(), + ExpiresIn: 10, // 此處 ExpiresIn 為有效時長(秒) + } + if tokenValid.IsExpires() { + t.Errorf("IsExpires() 返回 true,但 token 尚未過期") + } + + // 測試已過期:token 創建時間為 20 秒前,過期時長為 10 秒 + tokenExpired := Token{ + AccessCreateAt: now.Add(-20 * time.Second).UnixNano(), + ExpiresIn: 10, + } + if !tokenExpired.IsExpires() { + t.Errorf("IsExpires() 返回 false,但 token 已過期") + } +} + +// TestRedisExpiredSec 測試 RedisExpiredSec 方法 +// 此方法根據 t.ExpiresIn (UnixNano 表示的到期時間) 與當前 UTC 時間計算剩餘秒數 +func TestRedisExpiredSec(t *testing.T) { + // 定義測試案例: + // - expireOffset 為從當前時間的偏移量(正數表示未過期,負數表示已過期) + // - minExpected 與 maxExpected 為允許的返回值範圍(因為執行期間會有少量延遲) + tests := []struct { + name string + expireOffset time.Duration // token 過期時間相對於當前的偏移量 + minExpected int64 // 預期返回值下限(秒) + maxExpected int64 // 預期返回值上限(秒) + }{ + {"Not expired +10s", 10 * time.Second, 9, 10}, + {"Expired -5s", -5 * time.Second, -6, -5}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + now := time.Now().UTC() + // token 的到期時間設定為當前時間加上偏移量 + expTime := now.Add(tt.expireOffset) + token := Token{ + ExpiresIn: expTime.UnixNano(), + } + + got := token.RedisExpiredSec() + if got < tt.minExpected || got > tt.maxExpected { + t.Errorf("RedisExpiredSec() = %d, want between %d and %d", got, tt.minExpected, tt.maxExpected) + } + }) + } +} + +// TestRedisRefreshExpiredSec 測試 RedisRefreshExpiredSec 方法 +// 此方法根據 t.RefreshExpiresIn (UnixNano 表示的刷新到期時間) 與當前 UTC 時間計算剩餘秒數 +func TestRedisRefreshExpiredSec(t *testing.T) { + tests := []struct { + name string + expireOffset time.Duration // 刷新 token 到期時間相對於當前的偏移量 + minExpected int64 // 預期返回值下限(秒) + maxExpected int64 // 預期返回值上限(秒) + }{ + {"Not expired +20s", 20 * time.Second, 19, 20}, + {"Expired -5s", -5 * time.Second, -6, -5}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + now := time.Now().UTC() + expTime := now.Add(tt.expireOffset) + token := Token{ + RefreshExpiresIn: expTime.UnixNano(), + } + + got := token.RedisRefreshExpiredSec() + if got < tt.minExpected || got > tt.maxExpected { + t.Errorf("RedisRefreshExpiredSec() = %d, want between %d and %d", got, tt.minExpected, tt.maxExpected) + } + }) + } +} diff --git a/pkg/domain/error.go b/pkg/domain/error.go new file mode 100644 index 0000000..4188b5a --- /dev/null +++ b/pkg/domain/error.go @@ -0,0 +1 @@ +package domain diff --git a/internal/domain/redis.go b/pkg/domain/redis.go similarity index 79% rename from internal/domain/redis.go rename to pkg/domain/redis.go index 2e31a91..d69da77 100644 --- a/internal/domain/redis.go +++ b/pkg/domain/redis.go @@ -2,23 +2,14 @@ package domain import "strings" -const ( - TicketKeyPrefix = "tic/" -) - -const ( - ClientDataKey = "permission:clients" -) - type RedisKey string const ( AccessTokenRedisKey RedisKey = "access_token" RefreshTokenRedisKey RedisKey = "refresh_token" - DeviceTokenRedisKey RedisKey = "device_token" UIDTokenRedisKey RedisKey = "uid_token" TicketRedisKey RedisKey = "ticket" - DeviceUIDRedisKey RedisKey = "device_uid" + DeviceTokenRedisKey RedisKey = "device_token" ) func (key RedisKey) ToString() string { @@ -35,10 +26,18 @@ func GetAccessTokenRedisKey(id string) string { return AccessTokenRedisKey.With(id).ToString() } +func GetRefreshTokenRedisKey(id string) string { + return RefreshTokenRedisKey.With(id).ToString() +} + func GetUIDTokenRedisKey(uid string) string { return UIDTokenRedisKey.With(uid).ToString() } +func GetDeviceTokenRedisKey(device string) string { + return DeviceTokenRedisKey.With(device).ToString() +} + func GetTicketRedisKey(ticket string) string { return TicketRedisKey.With(ticket).ToString() } diff --git a/pkg/domain/repository/token.go b/pkg/domain/repository/token.go new file mode 100644 index 0000000..f19e7da --- /dev/null +++ b/pkg/domain/repository/token.go @@ -0,0 +1,49 @@ +package repository + +import ( + "code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/entity" + "context" + "time" +) + +// TokenRepo 管理Token +type TokenRepo interface { + Create + Get + Delete +} + +type Create interface { + // Create 建立新的 Token + Create(ctx context.Context, token entity.Token) error + // CreateOneTimeToken 建立臨時(一次性)Token,並指定有效期限 + CreateOneTimeToken(ctx context.Context, key string, ticket entity.Ticket, et time.Duration) error +} + +type Get interface { + // GetAccessTokenByOneTimeToken 根據一次性 Token 獲取對應的存取 Token + GetAccessTokenByOneTimeToken(ctx context.Context, oneTimeToken string) (entity.Token, error) + // GetAccessTokenByID 根據 Token ID 獲取對應的存取 Token + GetAccessTokenByID(ctx context.Context, id string) (entity.Token, error) + // GetAccessTokensByUID 根據用戶 ID 獲取該用戶的所有存取 Token + GetAccessTokensByUID(ctx context.Context, uid string) ([]entity.Token, error) + // GetAccessTokenCountByUID 根據用戶 ID 獲取該用戶的存取 Token 數量 + GetAccessTokenCountByUID(ctx context.Context, uid string) (int, error) + // GetAccessTokensByDeviceID 根據裝置 ID 獲取該裝置的所有存取 Token + GetAccessTokensByDeviceID(ctx context.Context, deviceID string) ([]entity.Token, error) + // GetAccessTokenCountByDeviceID 根據裝置 ID 獲取該裝置的存取 Token 數量 + GetAccessTokenCountByDeviceID(ctx context.Context, deviceID string) (int, error) +} + +type Delete interface { + // Delete 刪除指定的 Token + Delete(ctx context.Context, token entity.Token) error + // DeleteAccessTokenByID 根據 Token ID 批量刪除存取 Token + DeleteAccessTokenByID(ctx context.Context, ids []string) error + // DeleteAccessTokensByUID 根據用戶 ID 刪除該用戶的所有存取 Token + DeleteAccessTokensByUID(ctx context.Context, uid string) error + // DeleteAccessTokensByDeviceID 根據裝置 ID 刪除該裝置的所有存取 Token + DeleteAccessTokensByDeviceID(ctx context.Context, deviceID string) error + // DeleteOneTimeToken 批量刪除一次性 Token + DeleteOneTimeToken(ctx context.Context, ids []string, tokens []entity.Token) error +} diff --git a/pkg/domain/usecase/token.go b/pkg/domain/usecase/token.go new file mode 100644 index 0000000..1e15c7a --- /dev/null +++ b/pkg/domain/usecase/token.go @@ -0,0 +1,40 @@ +package usecase + +import "context" + +type TokenUseCase interface { + // NewToken 創建新 Token,通常為 Access Token + NewToken(ctx context.Context, req AuthorizationReq) (TokenResp, error) + // RefreshToken 刷新目前的 Token,包括一次性 Token + RefreshToken(ctx context.Context, req RefreshTokenReq) (RefreshTokenResp, error) + // CancelToken 取消 Token,包括取消其關聯的 One-Time Token + CancelToken(ctx context.Context, req CancelTokenReq) error + // ValidationToken 驗證 Token 是否有效 + ValidationToken(ctx context.Context, req ValidationTokenReq) (ValidationTokenResp, error) + // CancelTokens 根據 UID 或 Token ID 取消所有相關 Token,通常在用戶登出時使用 + CancelTokens(ctx context.Context, req DoTokenByUIDReq) error + // CancelTokenByDeviceID 根據 Device ID 取消所有相關的 Token + CancelTokenByDeviceID(ctx context.Context, req DoTokenByDeviceIDReq) error + // GetUserTokensByDeviceID 根據 Device ID 獲取所有 Token + GetUserTokensByDeviceID(ctx context.Context, req DoTokenByDeviceIDReq) ([]*TokenResp, error) + // GetUserTokensByUID 根據 UID 獲取所有 Token + GetUserTokensByUID(ctx context.Context, req QueryTokenByUIDReq) ([]*TokenResp, error) + // NewOneTimeToken 創建一次性 Token,例如 Refresh Token + NewOneTimeToken(ctx context.Context, req CreateOneTimeTokenReq) (CreateOneTimeTokenResp, error) + // CancelOneTimeToken 取消一次性 Token + CancelOneTimeToken(ctx context.Context, req CancelOneTimeTokenReq) error + // ReadTokenBasicData 檢查Token 帶的資料 + ReadTokenBasicData(ctx context.Context, token string) (map[string]string, error) +} + +// AuthorizationReq 定義授權請求的結構 +type AuthorizationReq struct { + GrantType string `json:"grant_type"` // 授權類型 + DeviceID string `json:"device_id"` // 設備 ID + Scope string `json:"scope"` // 授權範圍 + Data map[string]string `json:"data"` // 附加數據 + Expires int64 `json:"expires"` // 過期時間(秒) + IsRefreshToken bool `json:"is_refresh_token"` // 是否為刷新令牌 + Role string `json:"role"` // 是否為刷新令牌 + Account string `json:"account"` // 登入時的帳號 +} diff --git a/pkg/repository/token.go b/pkg/repository/token.go new file mode 100644 index 0000000..75c809b --- /dev/null +++ b/pkg/repository/token.go @@ -0,0 +1,331 @@ +package repository + +import ( + "code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain" + "code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/entity" + "code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/repository" + "context" + "encoding/json" + "errors" + "fmt" + "github.com/zeromicro/go-zero/core/stores/redis" + "time" +) + +// TokenRepositoryParam token 需要的參數 +type TokenRepositoryParam struct { + Redis *redis.Redis +} + +// TokenRepository 用於操作 token 的存儲庫 +type TokenRepository struct { + TokenRepositoryParam +} + +// NewTokenRepository 初始化並返回一個 TokenRepository 實例 +func NewTokenRepository(param TokenRepositoryParam) repository.TokenRepo { + return &TokenRepository{ + TokenRepositoryParam: param, + } +} + +// ====================== 私有工具函數 ======================= + +// runPipeline 執行 Redis pipeline 操作 +func (repo *TokenRepository) runPipeline(ctx context.Context, pipelineFunc func(tx redis.Pipeliner) error) error { + return repo.Redis.PipelinedCtx(ctx, pipelineFunc) +} + +// setToken 使用 Redis pipeline 存儲 token 並設定 TTL +func (repo *TokenRepository) setToken(ctx context.Context, tx redis.Pipeliner, id string, body []byte, ttl time.Duration) error { + return tx.Set(ctx, domain.GetAccessTokenRedisKey(id), body, ttl).Err() +} + +// setRefreshToken 若 token 中有 refresh token,則存儲之(使用 pipeline) +func (repo *TokenRepository) setRefreshToken(ctx context.Context, tx redis.Pipeliner, token entity.Token, ttl time.Duration) error { + if token.RefreshToken == "" { + return nil + } + return tx.Set(ctx, domain.GetRefreshTokenRedisKey(token.RefreshToken), token.ID, ttl).Err() +} + +// setTokenRelation 在 Redis 中設定 token 與 UID/Device 之間的關聯,並設定過期時間 +func (repo *TokenRepository) setTokenRelation(ctx context.Context, tx redis.Pipeliner, uid, deviceID, tokenID string, ttl time.Duration) error { + // 定義需要執行的操作列表 + operations := []struct { + key string + op func() error + }{ + { + key: domain.GetUIDTokenRedisKey(uid), + op: func() error { + return tx.SAdd(ctx, domain.GetUIDTokenRedisKey(uid), tokenID).Err() + }, + }, + { + key: domain.GetDeviceTokenRedisKey(deviceID), + op: func() error { + return tx.SAdd(ctx, domain.GetDeviceTokenRedisKey(deviceID), tokenID).Err() + }, + }, + } + + // 執行每個操作,並為對應 key 設置過期時間 + for _, operation := range operations { + if err := operation.op(); err != nil { + return fmt.Errorf("failed to create token relaction: %w", err) + } + if err := tx.Expire(ctx, operation.key, ttl).Err(); err != nil { + return fmt.Errorf("failed to set expire: %w", err) + } + } + + return nil +} + +// retrieveToken 根據指定 key 從 Redis 中獲取 token +func (repo *TokenRepository) retrieveToken(ctx context.Context, key string) (entity.Token, error) { + body, err := repo.Redis.GetCtx(ctx, key) + if err != nil { + return entity.Token{}, err + } + + if body == "" { + return entity.Token{}, fmt.Errorf("failed to found token") + } + + var token entity.Token + if err := json.Unmarshal([]byte(body), &token); err != nil { + return entity.Token{}, fmt.Errorf("failed to unmarshal token JSON: %w", err) + } + + return token, nil +} + +// getTokensBySet 根據集合 key 獲取所有 token +func (repo *TokenRepository) getTokensBySet(ctx context.Context, setKey string) ([]entity.Token, error) { + ids, err := repo.Redis.Smembers(setKey) + if err != nil { + if errors.Is(err, redis.Nil) { + return nil, nil + } + return nil, err + } + + tokens := make([]entity.Token, 0, len(ids)) + var tokensToDelete []string + now := time.Now().UnixNano() + for _, id := range ids { + token, err := repo.retrieveToken(ctx, domain.GetAccessTokenRedisKey(id)) + if err != nil { + tokensToDelete = append(tokensToDelete, id) + continue + } + + if token.ExpiresIn < now { + tokensToDelete = append(tokensToDelete, id) + continue + } + tokens = append(tokens, token) + } + + // 清除過期或錯誤的 token + if len(tokensToDelete) > 0 { + _ = repo.DeleteAccessTokenByID(ctx, tokensToDelete) + } + + return tokens, nil +} + +// getCountBySet 獲取集合中元素的數量 +func (repo *TokenRepository) getCountBySet(ctx context.Context, setKey string) (int, error) { + count, err := repo.Redis.ScardCtx(ctx, setKey) + if err != nil { + return 0, err + } + + return int(count), nil +} + +// deleteKeysAndRelations 刪除指定的 Redis key 並移除相關關聯(UID 與 DeviceID) +func (repo *TokenRepository) deleteKeysAndRelations(ctx context.Context, keys []string, uid, deviceID, tokenID string) error { + err := repo.Redis.Pipelined(func(tx redis.Pipeliner) error { + // 移除 UID 與 DeviceID 關聯中的 tokenID + _ = tx.SRem(ctx, domain.GetUIDTokenRedisKey(uid), tokenID) + _ = tx.SRem(ctx, domain.GetDeviceTokenRedisKey(deviceID), tokenID) + + // 刪除所有指定的 keys + for _, key := range keys { + _ = tx.Del(ctx, key) + } + + return nil + }) + + return err +} + +// batchDeleteKeys 批量刪除 Redis keys +func (repo *TokenRepository) batchDeleteKeys(ctx context.Context, keys ...string) error { + return repo.Redis.Pipelined(func(tx redis.Pipeliner) error { + for _, key := range keys { + if err := tx.Del(ctx, key).Err(); err != nil { + return err + } + } + return nil + }) +} + +// ====================== 公開方法 ======================= + +// Create 創建新的 token +func (repo *TokenRepository) Create(ctx context.Context, token entity.Token) error { + binToken, err := json.Marshal(token) + if err != nil { + return err + } + // 根據 token 設定 refresh 過期秒數計算 TTL + refreshTTL := time.Duration(token.RedisRefreshExpiredSec()) * time.Second + + return repo.runPipeline(ctx, func(tx redis.Pipeliner) error { + if err := repo.setToken(ctx, tx, token.ID, binToken, refreshTTL); err != nil { + return err + } + if err := repo.setRefreshToken(ctx, tx, token, refreshTTL); err != nil { + return err + } + + if err := repo.setTokenRelation(ctx, tx, token.UID, token.DeviceID, token.ID, refreshTTL); err != nil { + return err + } + return nil + }) +} + +// CreateOneTimeToken 創建一次性 token +func (repo *TokenRepository) CreateOneTimeToken(ctx context.Context, key string, ticket entity.Ticket, dt time.Duration) error { + body, err := json.Marshal(ticket) + if err != nil { + return err + } + + _, err = repo.Redis.SetnxExCtx(ctx, domain.GetRefreshTokenRedisKey(key), string(body), int(dt.Seconds())) + return err +} + +// GetAccessTokenByOneTimeToken 根據一次性 token 獲取 access token +func (repo *TokenRepository) GetAccessTokenByOneTimeToken(ctx context.Context, oneTimeToken string) (entity.Token, error) { + return repo.retrieveToken(ctx, domain.GetRefreshTokenRedisKey(oneTimeToken)) +} + +// GetAccessTokenByID 根據 token ID 獲取 access token +func (repo *TokenRepository) GetAccessTokenByID(ctx context.Context, id string) (entity.Token, error) { + return repo.retrieveToken(ctx, domain.GetAccessTokenRedisKey(id)) +} + +// GetAccessTokensByUID 根據 UID 獲取所有 access tokens +func (repo *TokenRepository) GetAccessTokensByUID(ctx context.Context, uid string) ([]entity.Token, error) { + return repo.getTokensBySet(ctx, domain.GetUIDTokenRedisKey(uid)) +} + +// GetAccessTokenCountByUID 根據 UID 獲取 access token 的數量 +func (repo *TokenRepository) GetAccessTokenCountByUID(ctx context.Context, uid string) (int, error) { + return repo.getCountBySet(ctx, domain.GetUIDTokenRedisKey(uid)) +} + +// GetAccessTokensByDeviceID 根據 DeviceID 獲取所有 access tokens +func (repo *TokenRepository) GetAccessTokensByDeviceID(ctx context.Context, deviceID string) ([]entity.Token, error) { + return repo.getTokensBySet(ctx, domain.GetDeviceTokenRedisKey(deviceID)) +} + +// GetAccessTokenCountByDeviceID 根據 DeviceID 獲取 access token 的數量 +func (repo *TokenRepository) GetAccessTokenCountByDeviceID(ctx context.Context, deviceID string) (int, error) { + return repo.getCountBySet(ctx, domain.GetDeviceTokenRedisKey(deviceID)) +} + +// Delete 刪除指定的 token +func (repo *TokenRepository) Delete(ctx context.Context, token entity.Token) error { + keys := []string{ + domain.GetAccessTokenRedisKey(token.ID), + domain.GetRefreshTokenRedisKey(token.RefreshToken), + } + return repo.deleteKeysAndRelations(ctx, keys, token.UID, token.DeviceID, token.ID) +} + +// DeleteAccessTokenByID 根據 token ID 刪除 access token +func (repo *TokenRepository) DeleteAccessTokenByID(ctx context.Context, ids []string) error { + for _, tokenID := range ids { + token, err := repo.GetAccessTokenByID(ctx, tokenID) + if err != nil { + continue + } + keys := []string{ + domain.GetAccessTokenRedisKey(token.ID), + domain.GetRefreshTokenRedisKey(token.RefreshToken), + } + _ = repo.deleteKeysAndRelations(ctx, keys, token.UID, token.DeviceID, token.ID) + } + return nil +} + +// DeleteAccessTokensByUID 根據 UID 刪除所有 access tokens +func (repo *TokenRepository) DeleteAccessTokensByUID(ctx context.Context, uid string) error { + tokens, err := repo.GetAccessTokensByUID(ctx, uid) + if err != nil { + return err + } + for _, token := range tokens { + if err := repo.Delete(ctx, token); err != nil { + return err + } + } + return nil +} + +// DeleteAccessTokensByDeviceID 根據 DeviceID 刪除所有 access tokens +func (repo *TokenRepository) DeleteAccessTokensByDeviceID(ctx context.Context, deviceID string) error { + tokens, err := repo.GetAccessTokensByDeviceID(ctx, deviceID) + if err != nil { + return err + } + + // 預分配 keys:每個 token 包含兩個 key + keys := make([]string, 0, len(tokens)*2) + for _, token := range tokens { + keys = append(keys, domain.GetAccessTokenRedisKey(token.ID)) + keys = append(keys, domain.GetRefreshTokenRedisKey(token.RefreshToken)) + } + + // 移除 UID 關聯中的 tokenID + if err := repo.runPipeline(ctx, func(tx redis.Pipeliner) error { + for _, token := range tokens { + _ = tx.SRem(ctx, domain.GetUIDTokenRedisKey(token.UID), token.ID) + } + return nil + }); err != nil { + return err + } + + if err := repo.batchDeleteKeys(ctx, keys...); err != nil { + return err + } + + _, err = repo.Redis.Del(domain.GetDeviceTokenRedisKey(deviceID)) + return err +} + +// DeleteOneTimeToken 刪除一次性 token(支持多個 key 一併刪除) +func (repo *TokenRepository) DeleteOneTimeToken(ctx context.Context, ids []string, tokens []entity.Token) error { + totalKeys := len(ids) + len(tokens) + keys := make([]string, 0, totalKeys) + + for _, id := range ids { + keys = append(keys, domain.GetRefreshTokenRedisKey(id)) + } + for _, token := range tokens { + keys = append(keys, domain.GetRefreshTokenRedisKey(token.RefreshToken)) + } + + return repo.batchDeleteKeys(ctx, keys...) +} diff --git a/pkg/repository/token_test.go b/pkg/repository/token_test.go new file mode 100644 index 0000000..2984740 --- /dev/null +++ b/pkg/repository/token_test.go @@ -0,0 +1,1749 @@ +package repository + +import ( + "code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain" + "code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/entity" + "context" + "encoding/json" + "errors" + "github.com/alicebob/miniredis/v2" + "github.com/stretchr/testify/assert" + "github.com/zeromicro/go-zero/core/stores/redis" + "testing" + "time" +) + +func setupMiniRedis() (*miniredis.Miniredis, *redis.Redis) { + // 啟動 setupMiniRedis 作為模擬的 Redis 服務 + mr, err := miniredis.Run() + if err != nil { + panic("failed to start miniRedis: " + err.Error()) + } + + // 使用 setupMiniRedis 的地址配置 go-zero Redis 客戶端 + redisConf := redis.RedisConf{ + Host: mr.Addr(), + Type: "node", + } + r := redis.MustNewRedis(redisConf) + + return mr, r +} + +func TestTokenRepository_Create(t *testing.T) { + mr, r := setupMiniRedis() + defer mr.Close() + + // 初始化 TokenRepository + repo := &TokenRepository{TokenRepositoryParam: TokenRepositoryParam{Redis: r}} + + // 定義測試參數 + token := entity.Token{ + ID: "token123", + UID: "user123", + DeviceID: "device123", + AccessToken: "access123", + ExpiresIn: time.Now().UTC().Add(10 * time.Second).UnixNano(), // 過期時間,現在加 10 秒 = 10 秒後 + RefreshToken: "refresh123", + RefreshExpiresIn: time.Now().UTC().Add(10 * time.Second).UnixNano(), // 過期時間,現在加 10 秒 = 10 秒後 + } + expiredTTL := 10 * time.Second // 過期時間 + + // 定義測試場景 + tests := []struct { + name string + token entity.Token + prepareFunc func() error // 用於模擬 Redis 或序列化錯誤 + wantErr bool + errMsg string + }{ + { + name: "Successful token creation", + token: token, + wantErr: false, + }, + { + name: "Redis Pipeline error", + token: token, + prepareFunc: func() error { + mr.SetError("forced Redis error") // 模擬 Redis 操作錯誤 + return nil + }, + wantErr: true, + errMsg: "forced Redis error", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 清除上一次的錯誤模擬 + mr.SetError("") + + // 執行準備函數(模擬 Redis 或序列化錯誤) + if tt.prepareFunc != nil { + tt.prepareFunc() + } + + // 執行 Create 方法 + err := repo.Create(context.Background(), tt.token) + + // 檢查是否出現預期錯誤 + if tt.wantErr { + assert.Error(t, err) + if err != nil { + assert.Contains(t, err.Error(), tt.errMsg) + } + } else { + assert.NoError(t, err) + + // 檢查是否成功設置了 AccessToken、RefreshToken 和 UID 及 DeviceID 關聯 + tokenKey := domain.GetAccessTokenRedisKey(tt.token.ID) + refreshTokenKey := domain.GetRefreshTokenRedisKey(tt.token.RefreshToken) + uidKey := domain.GetUIDTokenRedisKey(tt.token.UID) + deviceIDKey := domain.GetDeviceTokenRedisKey(tt.token.DeviceID) + + // 驗證 AccessToken 是否已設置 + val, err := mr.Get(tokenKey) + assert.NoError(t, err) + expectedBody, _ := json.Marshal(tt.token) + assert.Equal(t, string(expectedBody), val) + + // 驗證 RefreshToken 是否已設置 + val, err = mr.Get(refreshTokenKey) + assert.NoError(t, err) + assert.Equal(t, tt.token.ID, val) + + // 檢查 UID 和 DeviceID 關聯是否已設置 + uidSetMembers, err := mr.SMembers(uidKey) + assert.NoError(t, err) + assert.Contains(t, uidSetMembers, tt.token.ID) + + deviceIDSetMembers, err := mr.SMembers(deviceIDKey) + assert.NoError(t, err) + assert.Contains(t, deviceIDSetMembers, tt.token.ID) + + // 檢查 AccessToken 和 RefreshToken 的過期時間 + accessTTL := mr.TTL(tokenKey) + assert.InDelta(t, expiredTTL.Seconds(), accessTTL.Seconds(), 2, "AccessToken TTL 與設置的過期 TTl 應該相近") + + refreshTTLVal := mr.TTL(refreshTokenKey) + assert.InDelta(t, expiredTTL.Seconds(), refreshTTLVal.Seconds(), 2, "Refresh TTL 與 與設置的過期 TTl 應該相近") + } + + // 清除模擬錯誤 + mr.SetError("") + }) + } +} + +func TestTokenRepository_retrieveToken(t *testing.T) { + mr, r := setupMiniRedis() + defer mr.Close() + + // 初始化 TokenRepository + repo := &TokenRepository{TokenRepositoryParam: TokenRepositoryParam{Redis: r}} + + // 模擬一個 Token 實例並將其存入 Redis + now := time.Now().UTC().UnixNano() + token := entity.Token{ + ID: "token123", + UID: "user123", + DeviceID: "device123", + AccessToken: "access123", + ExpiresIn: time.Now().UTC().Add(3600 * time.Second).UnixNano(), + AccessCreateAt: now, + RefreshToken: "refresh123", + RefreshExpiresIn: time.Now().UTC().Add(7200 * time.Second).UnixNano(), + RefreshCreateAt: now, + } + + // 將 Token 序列化為 JSON 並存入 Redis + tokenKey := domain.GetAccessTokenRedisKey(token.ID) + tokenData, _ := json.Marshal(token) + err := mr.Set(tokenKey, string(tokenData)) + assert.NoError(t, err) + + // 定義測試場景 + tests := []struct { + name string + key string + want entity.Token + wantErr bool + errMsg string + }{ + { + name: "ok", + key: tokenKey, + want: token, + wantErr: false, + }, + { + name: "Token not found", + key: domain.GetAccessTokenRedisKey("nonexistent"), + want: entity.Token{}, + wantErr: true, + errMsg: "failed to found token", + }, + { + name: "Invalid JSON format", + key: domain.GetAccessTokenRedisKey("invalid_json"), + want: entity.Token{}, + wantErr: true, + errMsg: "failed to unmarshal token JSON: invalid character 'i' looking for beginning of object key string", + }, + } + + // 將錯誤的 JSON 格式設置到 Redis + err = mr.Set(domain.GetAccessTokenRedisKey("invalid_json"), "{invalid_json}") + assert.NoError(t, err) + + // 執行測試 + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := repo.retrieveToken(context.Background(), tt.key) + + if tt.wantErr { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.errMsg) + } else { + assert.NoError(t, err) + // 比較 Token 的每個字段 + assert.Equal(t, tt.want.ID, got.ID) + assert.Equal(t, tt.want.UID, got.UID) + assert.Equal(t, tt.want.DeviceID, got.DeviceID) + assert.Equal(t, tt.want.AccessToken, got.AccessToken) + assert.Equal(t, tt.want.ExpiresIn, got.ExpiresIn) + assert.Equal(t, tt.want.RefreshToken, got.RefreshToken) + assert.Equal(t, tt.want.RefreshExpiresIn, got.RefreshExpiresIn) + + // 將時間字段轉換為 Unix() 格式進行比較 + assert.Equal(t, tt.want.AccessCreateAt, got.AccessCreateAt) + assert.Equal(t, tt.want.RefreshCreateAt, got.RefreshCreateAt) + } + }) + } +} + +func TestTokenRepository_GetTokensBySet(t *testing.T) { + mr, r := setupMiniRedis() + defer mr.Close() + + // 初始化 TokenRepository + repo := &TokenRepository{TokenRepositoryParam: TokenRepositoryParam{Redis: r}} + + // 模擬兩個 Token 實例,一個過期,一個未過期,並將它們存入 Redis + now := time.Now().UTC() + unexpiredToken := entity.Token{ + ID: "token123", + UID: "user123", + DeviceID: "device123", + AccessToken: "access123", + ExpiresIn: now.Add(time.Hour).UnixNano(), // 1 小時後過期 + AccessCreateAt: now.UnixNano(), + RefreshToken: "refresh123", + RefreshExpiresIn: now.Add(2 * time.Hour).UnixNano(), + RefreshCreateAt: now.UnixNano(), + } + + expiredToken := entity.Token{ + ID: "token456", + UID: "user456", + DeviceID: "device456", + AccessToken: "access456", + ExpiresIn: now.Add(-time.Hour).UnixNano(), // 1 小時前過期 + AccessCreateAt: now.Add(-2 * time.Hour).UnixNano(), + RefreshToken: "refresh456", + RefreshExpiresIn: now.Add(-30 * time.Minute).UnixNano(), + RefreshCreateAt: now.Add(-90 * time.Minute).UnixNano(), + } + + // 將 Token 存入 Redis + unexpiredTokenData, _ := json.Marshal(unexpiredToken) + expiredTokenData, _ := json.Marshal(expiredToken) + err := mr.Set(domain.GetAccessTokenRedisKey(unexpiredToken.ID), string(unexpiredTokenData)) + assert.NoError(t, err) + err = mr.Set(domain.GetAccessTokenRedisKey(expiredToken.ID), string(expiredTokenData)) + assert.NoError(t, err) + + // 將兩個 Token ID 添加到 Set 集合中 + setKey := "permission:token_set" + _, err = mr.SAdd(setKey, unexpiredToken.ID) + if err != nil { + return + } + _, err = mr.SAdd(setKey, expiredToken.ID) + assert.NoError(t, err) + + // 定義測試場景 + tests := []struct { + name string + setKey string + wantTokens []entity.Token + wantErr bool + }{ + { + name: "Set contains unexpired and expired tokens", + setKey: setKey, + wantTokens: []entity.Token{unexpiredToken}, // 預期僅返回未過期的 Token + wantErr: false, + }, + { + name: "Set key not found", + setKey: "permission:nonexistent_set", + wantTokens: nil, // 預期返回 nil + wantErr: false, + }, + } + + // 執行測試 + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := repo.getTokensBySet(context.Background(), tt.setKey) + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, len(tt.wantTokens), len(got)) + + // 比較每個返回的 Token,並檢查時間戳 + for i, token := range got { + assert.Equal(t, tt.wantTokens[i].ID, token.ID) + assert.Equal(t, tt.wantTokens[i].UID, token.UID) + assert.Equal(t, tt.wantTokens[i].DeviceID, token.DeviceID) + assert.Equal(t, tt.wantTokens[i].AccessToken, token.AccessToken) + assert.Equal(t, tt.wantTokens[i].ExpiresIn, token.ExpiresIn) + assert.Equal(t, tt.wantTokens[i].RefreshToken, token.RefreshToken) + assert.Equal(t, tt.wantTokens[i].RefreshExpiresIn, token.RefreshExpiresIn) + assert.Equal(t, tt.wantTokens[i].AccessCreateAt, token.AccessCreateAt) + assert.Equal(t, tt.wantTokens[i].RefreshCreateAt, token.RefreshCreateAt) + } + } + }) + } +} + +func TestTokenRepository_GetCountBySet(t *testing.T) { + mr, r := setupMiniRedis() + defer mr.Close() + + // 初始化 TokenRepository + repo := &TokenRepository{TokenRepositoryParam: TokenRepositoryParam{Redis: r}} + + // 定義測試集合鍵和測試數據 + setKey := "permission:token_set" + + // 將測試數據存入 Redis + mr.SAdd(setKey, "token123") + mr.SAdd(setKey, "token456") + mr.SAdd(setKey, "token789") + + // 定義測試場景 + tests := []struct { + name string + setKey string + want int + wantErr bool + }{ + { + name: "Count of existing set", + setKey: setKey, + want: 3, // 預期集合中有 3 個元素 + wantErr: false, + }, + { + name: "Non-existent set", + setKey: "permission:nonexistent_set", + want: 0, // 預期集合不存在,返回 0 + wantErr: false, + }, + } + + // 執行測試 + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := repo.getCountBySet(context.Background(), tt.setKey) + + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.want, got) + } + }) + } +} + +func TestTokenRepository_SetRelation(t *testing.T) { + mr, r := setupMiniRedis() + defer mr.Close() + + // 初始化 TokenRepository + repo := &TokenRepository{TokenRepositoryParam: TokenRepositoryParam{Redis: r}} + + // 定義測試參數 + uid := "user123" + deviceID := "device123" + tokenID := "token123" + ttl := 10 * time.Second // 設置過期時間為 10 秒 + + // 定義測試場景 + tests := []struct { + name string + uid string + deviceID string + tokenID string + ttl time.Duration + prepareFunc func() error // 用於模擬 Redis 錯誤 + wantErr bool + errMsg string + }{ + { + name: "Valid relation setting", + uid: uid, + deviceID: deviceID, + tokenID: tokenID, + ttl: ttl, + wantErr: false, + }, + { + name: "Redis SAdd error", + uid: uid, + deviceID: deviceID, + tokenID: tokenID, + ttl: ttl, + prepareFunc: func() error { + mr.SetError("forced SAdd error") // 模擬 SAdd 錯誤 + return nil + }, + wantErr: true, + errMsg: "forced SAdd error", + }, + { + name: "Redis Expire error", + uid: uid, + deviceID: deviceID, + tokenID: tokenID, + ttl: ttl, + prepareFunc: func() error { + mr.SetError("forced Expire error") // 模擬 Expire 錯誤 + return nil + }, + wantErr: true, + errMsg: "forced Expire error", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 清除上一次的錯誤模擬 + mr.SetError("") + + // 執行準備函數(模擬 Redis 錯誤) + if tt.prepareFunc != nil { + tt.prepareFunc() + } + + // 構建 Redis 鍵 + uidKey := domain.GetUIDTokenRedisKey(tt.uid) + deviceIDKey := domain.GetDeviceTokenRedisKey(tt.deviceID) + + // 執行 Redis Pipeline + err := r.Pipelined(func(tx redis.Pipeliner) error { + return repo.setTokenRelation(context.Background(), tx, tt.uid, tt.deviceID, tt.tokenID, tt.ttl) + }) + + // 檢查是否出現預期錯誤 + if tt.wantErr { + assert.Error(t, err) + if err != nil { + assert.Contains(t, err.Error(), tt.errMsg) + } + } else { + assert.NoError(t, err) + + // 檢查 UID 和 DeviceID 關聯是否已設置 + uidSetMembers, err := mr.SMembers(uidKey) + assert.NoError(t, err) + assert.Contains(t, uidSetMembers, tt.tokenID) + + deviceIDSetMembers, err := mr.SMembers(deviceIDKey) + assert.NoError(t, err) + assert.Contains(t, deviceIDSetMembers, tt.tokenID) + + // 檢查 UID 和 DeviceID 鍵的過期時間 + uidTTL := mr.TTL(uidKey) + assert.Equal(t, tt.ttl.Seconds(), uidTTL.Seconds()) + + deviceIDTTL := mr.TTL(deviceIDKey) + assert.Equal(t, tt.ttl.Seconds(), deviceIDTTL.Seconds()) + } + + // 清除模擬錯誤 + mr.SetError("") + }) + } +} + +func TestTokenRepository_SetRefreshToken(t *testing.T) { + mr, r := setupMiniRedis() + defer mr.Close() + + // 初始化 TokenRepository + repo := &TokenRepository{TokenRepositoryParam: TokenRepositoryParam{Redis: r}} + + // 定義測試參數 + ttl := 10 * time.Second // 設置過期時間為 10 秒 + + // 定義測試場景 + tests := []struct { + name string + token entity.Token + ttl time.Duration + prepareFunc func() error // 用於模擬 Redis 錯誤 + wantErr bool + errMsg string + }{ + { + name: "Valid RefreshToken setting", + token: entity.Token{ + ID: "token123", + RefreshToken: "refresh123", + }, + ttl: ttl, + wantErr: false, + }, + { + name: "Empty RefreshToken", + token: entity.Token{ + ID: "token456", + RefreshToken: "", + }, + ttl: ttl, + wantErr: false, + }, + { + name: "Redis Set error", + token: entity.Token{ + ID: "token789", + RefreshToken: "refresh789", + }, + ttl: ttl, + prepareFunc: func() error { + mr.SetError("forced Set error") // 模擬 Set 操作錯誤 + return nil + }, + wantErr: true, + errMsg: "forced Set error", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 清除上一次的錯誤模擬 + mr.SetError("") + + // 執行準備函數(模擬 Redis 錯誤) + if tt.prepareFunc != nil { + tt.prepareFunc() + } + + // 執行 Redis Pipeline + err := r.Pipelined(func(tx redis.Pipeliner) error { + return repo.setRefreshToken(context.Background(), tx, tt.token, tt.ttl) + }) + + // 檢查是否出現預期錯誤 + if tt.wantErr { + assert.Error(t, err) + if err != nil { + assert.Contains(t, err.Error(), tt.errMsg) + } + } else { + assert.NoError(t, err) + + // 如果 RefreshToken 不為空,檢查是否成功設置了鍵 + if tt.token.RefreshToken != "" { + refreshTokenKey := domain.GetRefreshTokenRedisKey(tt.token.RefreshToken) + val, err := mr.Get(refreshTokenKey) + assert.NoError(t, err) + assert.Equal(t, tt.token.ID, val) + + // 檢查 RefreshToken 鍵的過期時間 + ttlVal := mr.TTL(refreshTokenKey) + assert.Equal(t, tt.ttl.Seconds(), ttlVal.Seconds()) + } + } + + // 清除模擬錯誤 + mr.SetError("") + }) + } +} + +func TestTokenRepository_SetToken(t *testing.T) { + mr, r := setupMiniRedis() + defer mr.Close() + + // 初始化 TokenRepository + repo := &TokenRepository{TokenRepositoryParam: TokenRepositoryParam{Redis: r}} + + // 定義測試參數 + ttl := 10 * time.Second // 設置過期時間為 10 秒 + token := entity.Token{ + ID: "token123", + UID: "user123", + DeviceID: "device123", + AccessToken: "access123", + ExpiresIn: time.Now().UTC().Add(7200 * time.Second).UnixNano(), + RefreshToken: "refresh123", + } + body, _ := json.Marshal(token) // 將 Token 轉為 JSON 格式 + + // 定義測試場景 + tests := []struct { + name string + token entity.Token + body []byte + ttl time.Duration + prepareFunc func() error // 用於模擬 Redis 錯誤 + wantErr bool + errMsg string + }{ + { + name: "Valid Token setting", + token: token, + body: body, + ttl: ttl, + wantErr: false, + }, + { + name: "Redis Set error", + token: token, + body: body, + ttl: ttl, + prepareFunc: func() error { + mr.SetError("forced Set error") // 模擬 Set 操作錯誤 + return nil + }, + wantErr: true, + errMsg: "forced Set error", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 清除上一次的錯誤模擬 + mr.SetError("") + + // 執行準備函數(模擬 Redis 錯誤) + if tt.prepareFunc != nil { + tt.prepareFunc() + } + + // 構建 Redis 鍵 + tokenKey := domain.GetAccessTokenRedisKey(tt.token.ID) + + // 執行 Redis Pipeline + err := r.Pipelined(func(tx redis.Pipeliner) error { + return repo.setToken(context.Background(), tx, tt.token.ID, tt.body, tt.ttl) + }) + + // 檢查是否出現預期錯誤 + if tt.wantErr { + assert.Error(t, err) + if err != nil { + assert.Contains(t, err.Error(), tt.errMsg) + } + } else { + assert.NoError(t, err) + + // 驗證 Token 是否已設置 + val, err := mr.Get(tokenKey) + assert.NoError(t, err) + assert.Equal(t, string(tt.body), val) + + // 檢查 Token 鍵的過期時間 + ttlVal := mr.TTL(tokenKey) + assert.Equal(t, tt.ttl.Seconds(), ttlVal.Seconds()) + } + + // 清除模擬錯誤 + mr.SetError("") + }) + } +} + +func TestTokenRepository_RunPipeline(t *testing.T) { + mr, r := setupMiniRedis() + defer mr.Close() + + // 初始化 TokenRepository + repo := &TokenRepository{TokenRepositoryParam: TokenRepositoryParam{Redis: r}} + + // 定義測試場景 + tests := []struct { + name string + prepareFunc func() error // 準備函數,用於模擬 Redis 錯誤 + fn func(tx redis.Pipeliner) error // 要在 Pipeline 中執行的函數 + wantErr bool // 是否期望錯誤 + errMsg string // 預期的錯誤信息 + }{ + { + name: "Successful Pipeline Execution", + fn: func(tx redis.Pipeliner) error { + // 模擬一個簡單的操作 + return tx.Set(context.Background(), "testkey", "testvalue", 0).Err() + }, + wantErr: false, + }, + { + name: "Pipeline Function Error", + fn: func(tx redis.Pipeliner) error { + return errors.New("forced function error") // 模擬 Pipeline 操作中的錯誤 + }, + wantErr: true, + errMsg: "forced function error", + }, + { + name: "Redis Pipeline Error", + fn: func(tx redis.Pipeliner) error { + return tx.Set(context.Background(), "testkey", "testvalue", 0).Err() + }, + prepareFunc: func() error { + mr.SetError("forced Redis error") // 模擬 Redis 操作錯誤 + return nil + }, + wantErr: true, + errMsg: "forced Redis error", + }, + } + + // 執行測試 + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 清除上一次的錯誤模擬 + mr.SetError("") + + // 執行準備函數以模擬 Redis 錯誤 + if tt.prepareFunc != nil { + tt.prepareFunc() + } + + // 執行 runPipeline 並捕獲錯誤 + err := repo.runPipeline(context.Background(), tt.fn) + + // 檢查是否出現預期錯誤 + if tt.wantErr { + assert.Error(t, err) + if err != nil { + assert.Contains(t, err.Error(), tt.errMsg) + } + } else { + assert.NoError(t, err) + + // 如果操作成功,驗證 Redis 中的鍵 + if tt.name == "Successful Pipeline Execution" { + val, err := mr.Get("testkey") + assert.NoError(t, err) + assert.Equal(t, "testvalue", val) + } + } + + // 清除模擬錯誤 + mr.SetError("") + }) + } +} + +// 定義一個無法序列化的結構以模擬序列化錯誤 +type Unserializable struct{} + +func (u Unserializable) MarshalJSON() ([]byte, error) { + return nil, errors.New("forced JSON marshal error") +} + +func TestTokenRepository_CreateOneTimeToken(t *testing.T) { + mr, r := setupMiniRedis() + defer mr.Close() + + // 初始化 TokenRepository + repo := &TokenRepository{TokenRepositoryParam: TokenRepositoryParam{Redis: r}} + + // 定義測試參數 + key := "one_time_key" + duration := 10 * time.Second // 設置過期時間為 10 秒 + ticket := entity.Ticket{ + Data: "sample_data", + Token: entity.Token{ + ID: "token123", + AccessToken: "access123", + }, + } + + // 定義測試場景 + tests := []struct { + name string + key string + ticket entity.Ticket + duration time.Duration + prepareFunc func() error // 用於模擬 Redis 或序列化錯誤 + wantErr bool + errMsg string + }{ + { + name: "Successful one-time token creation", + key: key, + ticket: ticket, + duration: duration, + wantErr: false, + }, + { + name: "JSON marshal error", + key: key, + ticket: entity.Ticket{ + Data: Unserializable{}, + Token: entity.Token{ + ID: "invalid_token", + }, + }, + duration: duration, + wantErr: true, + errMsg: "json: error calling MarshalJSON for type repository.Unserializable: forced JSON marshal error", + }, + { + name: "Redis SetnxEx error", + key: key, + ticket: ticket, + duration: duration, + prepareFunc: func() error { + mr.SetError("forced Redis error") // 模擬 Redis 操作錯誤 + return nil + }, + wantErr: true, + errMsg: "forced Redis error", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 清除上一次的錯誤模擬 + mr.SetError("") + + // 執行準備函數(模擬 Redis 或序列化錯誤) + if tt.prepareFunc != nil { + tt.prepareFunc() + } + + // 執行 CreateOneTimeToken 方法 + err := repo.CreateOneTimeToken(context.Background(), tt.key, tt.ticket, tt.duration) + + // 檢查是否出現預期錯誤 + if tt.wantErr { + assert.Error(t, err) + if err != nil { + assert.Contains(t, err.Error(), tt.errMsg) + } + } else { + assert.NoError(t, err) + + // 構建預期的 Redis 鍵 + oneTimeTokenKey := domain.GetRefreshTokenRedisKey(tt.key) + + // 檢查 Redis 中是否設置了臨時 Token + val, err := mr.Get(oneTimeTokenKey) + assert.NoError(t, err) + + expectedBody, _ := json.Marshal(tt.ticket) + assert.Equal(t, string(expectedBody), val) + + // 檢查過期時間 + ttl := mr.TTL(oneTimeTokenKey) + assert.Equal(t, tt.duration.Seconds(), ttl.Seconds()) + } + + // 清除模擬錯誤 + mr.SetError("") + }) + } +} + +func TestTokenRepository_GetAccessTokenByOneTimeToken(t *testing.T) { + mr, r := setupMiniRedis() + defer mr.Close() + + // 初始化 TokenRepository + repo := &TokenRepository{TokenRepositoryParam: TokenRepositoryParam{Redis: r}} + + // 定義測試參數 + oneTimeToken := "one_time_token_123" + accessTokenID := "token123" + expectedToken := entity.Token{ + ID: accessTokenID, + UID: "user123", + DeviceID: "device123", + AccessToken: "access123", + ExpiresIn: 3600, + RefreshToken: "refresh123", + } + + // 在 Redis 中設置模擬的數據 + _ = mr.Set(domain.GetRefreshTokenRedisKey(oneTimeToken), accessTokenID) + tokenData, _ := json.Marshal(expectedToken) + _ = mr.Set(domain.GetRefreshTokenRedisKey(oneTimeToken), string(tokenData)) + + // 定義測試場景 + tests := []struct { + name string + oneTimeToken string + prepareFunc func() error // 用於模擬 Redis 錯誤 + expected entity.Token + wantErr bool + errMsg string + }{ + { + name: "Successful retrieval of access token by one-time token", + oneTimeToken: oneTimeToken, + expected: expectedToken, + wantErr: false, + }, + { + name: "Token not found in Redis", + oneTimeToken: "nonexistent_token", + wantErr: true, + errMsg: "failed to found token", + }, + { + name: "Redis Get error", + oneTimeToken: oneTimeToken, + prepareFunc: func() error { + mr.SetError("forced Redis error") // 模擬 Redis 錯誤 + return nil + }, + wantErr: true, + errMsg: "forced Redis error", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 清除上一次的錯誤模擬 + mr.SetError("") + + // 執行準備函數(模擬 Redis 錯誤) + if tt.prepareFunc != nil { + tt.prepareFunc() + } + + // 執行 GetAccessTokenByOneTimeToken 方法 + result, err := repo.GetAccessTokenByOneTimeToken(context.Background(), tt.oneTimeToken) + + // 檢查是否出現預期錯誤 + if tt.wantErr { + assert.Error(t, err) + if err != nil { + assert.Contains(t, err.Error(), tt.errMsg) + } + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expected, result) + } + + // 清除模擬錯誤 + mr.SetError("") + }) + } +} + +func TestTokenRepository_GetAccessTokensByUID(t *testing.T) { + mr, r := setupMiniRedis() + defer mr.Close() + + // 初始化 TokenRepository + repo := &TokenRepository{TokenRepositoryParam: TokenRepositoryParam{Redis: r}} + + // 定義測試參數 + uid := "user123" + tokens := []entity.Token{ + { + ID: "token1", + UID: uid, + DeviceID: "device1", + AccessToken: "access1", + ExpiresIn: time.Now().UTC().Add(60 * time.Minute).UnixNano(), + RefreshExpiresIn: time.Now().UTC().Add(60 * time.Minute).UnixNano(), + RefreshToken: "refresh1", + }, + { + ID: "token2", + UID: uid, + DeviceID: "device2", + AccessToken: "access2", + ExpiresIn: time.Now().UTC().Add(60 * time.Minute).UnixNano(), + RefreshExpiresIn: time.Now().UTC().Add(60 * time.Minute).UnixNano(), + RefreshToken: "refresh2", + }, + } + + for _, token := range tokens { + err := repo.Create(context.Background(), token) + assert.NoError(t, err) + } + + // 定義測試場景 + tests := []struct { + name string + uid string + prepareFunc func() error // 用於模擬 Redis 錯誤 + expected []entity.Token + wantErr bool + errMsg string + }{ + { + name: "Successful retrieval of tokens by UID", + uid: uid, + expected: tokens, + wantErr: false, + }, + { + name: "UID not found in Redis", + uid: "nonexistent_user", + expected: []entity.Token{}, + wantErr: false, + }, + { + name: "Redis SMember error", + uid: uid, + prepareFunc: func() error { + mr.SetError("forced Redis error") // 模擬 Redis 錯誤 + return nil + }, + wantErr: true, + errMsg: "forced Redis error", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 清除上一次的錯誤模擬 + mr.SetError("") + + // 執行準備函數(模擬 Redis 錯誤) + if tt.prepareFunc != nil { + tt.prepareFunc() + } + + // 執行 GetAccessTokensByUID 方法 + result, err := repo.GetAccessTokensByUID(context.Background(), tt.uid) + + // 檢查是否出現預期錯誤 + if tt.wantErr { + assert.Error(t, err) + if err != nil { + assert.Contains(t, err.Error(), tt.errMsg) + } + } else { + + assert.NoError(t, err) + assert.Equal(t, tt.expected, result) + } + + // 清除模擬錯誤 + mr.SetError("") + }) + } +} + +func TestTokenRepository_GetAccessTokenCountByUID(t *testing.T) { + mr, r := setupMiniRedis() + defer mr.Close() + + // 初始化 TokenRepository + repo := &TokenRepository{TokenRepositoryParam: TokenRepositoryParam{Redis: r}} + + // 定義測試參數 + uid := "user123" + uidKey := domain.GetUIDTokenRedisKey(uid) + + // 在 Redis 中設置模擬的數據 + _, _ = mr.SAdd(uidKey, "token1") + _, _ = mr.SAdd(uidKey, "token2") + _, _ = mr.SAdd(uidKey, "token3") + + // 定義測試場景 + tests := []struct { + name string + uid string + prepareFunc func() error // 用於模擬 Redis 錯誤 + expected int + wantErr bool + errMsg string + }{ + { + name: "Successful retrieval of token count by UID", + uid: uid, + expected: 3, + wantErr: false, + }, + { + name: "UID not found in Redis", + uid: "nonexistent_user", + expected: 0, + wantErr: false, + }, + { + name: "Redis Scard error", + uid: uid, + prepareFunc: func() error { + mr.SetError("forced Redis error") // 模擬 Redis 錯誤 + return nil + }, + wantErr: true, + errMsg: "forced Redis error", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 清除上一次的錯誤模擬 + mr.SetError("") + + // 執行準備函數(模擬 Redis 錯誤) + if tt.prepareFunc != nil { + tt.prepareFunc() + } + + // 執行 GetAccessTokenCountByUID 方法 + result, err := repo.GetAccessTokenCountByUID(context.Background(), tt.uid) + + // 檢查是否出現預期錯誤 + if tt.wantErr { + assert.Error(t, err) + if err != nil { + assert.Contains(t, err.Error(), tt.errMsg) + } + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expected, result) + } + + // 清除模擬錯誤 + mr.SetError("") + }) + } +} + +func TestTokenRepository_GetAccessTokensByDeviceID(t *testing.T) { + mr, r := setupMiniRedis() + defer mr.Close() + + // 初始化 TokenRepository + repo := &TokenRepository{TokenRepositoryParam: TokenRepositoryParam{Redis: r}} + + // 定義測試參數 + deviceID := "device123" + deviceKey := domain.GetDeviceTokenRedisKey(deviceID) + + // 模擬在 Redis 中存儲多個 Token + tokens := []entity.Token{ + { + ID: "token1", + UID: "user123", + DeviceID: deviceID, + AccessToken: "access1", + ExpiresIn: time.Now().UTC().Add(60 * time.Minute).UnixNano(), + RefreshToken: "refresh1", + }, + { + ID: "token2", + UID: "user123", + DeviceID: deviceID, + AccessToken: "access2", + ExpiresIn: time.Now().UTC().Add(60 * time.Minute).UnixNano(), + RefreshToken: "refresh2", + }, + } + + // 在 Redis 中設置初始數據 + for _, token := range tokens { + tokenData, _ := json.Marshal(token) + _ = mr.Set(domain.GetAccessTokenRedisKey(token.ID), string(tokenData)) + _, _ = mr.SAdd(deviceKey, token.ID) + } + + // 定義測試場景 + tests := []struct { + name string + deviceID string + prepareFunc func() error // 用於模擬 Redis 錯誤 + expected []entity.Token + wantErr bool + errMsg string + }{ + { + name: "Successful retrieval of tokens by Device ID", + deviceID: deviceID, + expected: tokens, + wantErr: false, + }, + { + name: "Device ID not found in Redis", + deviceID: "nonexistent_device", + expected: []entity.Token{}, + wantErr: false, + }, + { + name: "Redis SMember error", + deviceID: deviceID, + prepareFunc: func() error { + mr.SetError("forced Redis error") // 模擬 Redis 錯誤 + return nil + }, + wantErr: true, + errMsg: "forced Redis error", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 清除上一次的錯誤模擬 + mr.SetError("") + + // 執行準備函數(模擬 Redis 錯誤) + if tt.prepareFunc != nil { + tt.prepareFunc() + } + + // 執行 GetAccessTokensByDeviceID 方法 + result, err := repo.GetAccessTokensByDeviceID(context.Background(), tt.deviceID) + + // 檢查是否出現預期錯誤 + if tt.wantErr { + assert.Error(t, err) + if err != nil { + assert.Contains(t, err.Error(), tt.errMsg) + } + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expected, result) + } + + // 清除模擬錯誤 + mr.SetError("") + }) + } +} + +func TestTokenRepository_GetAccessTokenCountByDeviceID(t *testing.T) { + mr, r := setupMiniRedis() + defer mr.Close() + + // 初始化 TokenRepository + repo := &TokenRepository{TokenRepositoryParam: TokenRepositoryParam{Redis: r}} + + // 定義測試參數 + deviceID := "device123" + deviceKey := domain.GetDeviceTokenRedisKey(deviceID) + + // 在 Redis 中設置模擬的數據 + _, _ = mr.SAdd(deviceKey, "token1") + _, _ = mr.SAdd(deviceKey, "token2") + _, _ = mr.SAdd(deviceKey, "token3") + + // 定義測試場景 + tests := []struct { + name string + deviceID string + prepareFunc func() error // 用於模擬 Redis 錯誤 + expected int + wantErr bool + errMsg string + }{ + { + name: "Successful retrieval of token count by Device ID", + deviceID: deviceID, + expected: 3, + wantErr: false, + }, + { + name: "Device ID not found in Redis", + deviceID: "nonexistent_device", + expected: 0, + wantErr: false, + }, + { + name: "Redis Scard error", + deviceID: deviceID, + prepareFunc: func() error { + mr.SetError("forced Redis error") // 模擬 Redis 錯誤 + return nil + }, + wantErr: true, + errMsg: "forced Redis error", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 清除上一次的錯誤模擬 + mr.SetError("") + + // 執行準備函數(模擬 Redis 錯誤) + if tt.prepareFunc != nil { + tt.prepareFunc() + } + + // 執行 GetAccessTokenCountByDeviceID 方法 + result, err := repo.GetAccessTokenCountByDeviceID(context.Background(), tt.deviceID) + + // 檢查是否出現預期錯誤 + if tt.wantErr { + assert.Error(t, err) + if err != nil { + assert.Contains(t, err.Error(), tt.errMsg) + } + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expected, result) + } + + // 清除模擬錯誤 + mr.SetError("") + }) + } +} + +func TestTokenRepository_Delete(t *testing.T) { + mr, r := setupMiniRedis() + defer mr.Close() + + // 初始化 TokenRepository + repo := &TokenRepository{TokenRepositoryParam: TokenRepositoryParam{Redis: r}} + + // 定義測試參數 + token := entity.Token{ + ID: "token123", + UID: "user123", + DeviceID: "device123", + AccessToken: "access123", + RefreshToken: "refresh123", + ExpiresIn: time.Now().UTC().Add(60 * time.Minute).UnixNano(), + RefreshExpiresIn: time.Now().UTC().Add(60 * time.Minute).UnixNano(), + } + + // 模擬在 Redis 中存儲 Token 的數據 + accessTokenKey := domain.GetAccessTokenRedisKey(token.ID) + refreshTokenKey := domain.GetRefreshTokenRedisKey(token.RefreshToken) + uidKey := domain.GetUIDTokenRedisKey(token.UID) + deviceIDKey := domain.GetDeviceTokenRedisKey(token.DeviceID) + + // 模擬在 Redis 中存儲 Token 的數據 + repo.Create(context.TODO(), token) + + // 定義測試場景 + tests := []struct { + name string + token entity.Token + prepareFunc func() error // 用於模擬 Redis 錯誤 + wantErr bool + errMsg string + jump bool + }{ + { + name: "Successful deletion of token", + token: token, + wantErr: false, + }, + { + name: "Redis delete error", + token: token, + prepareFunc: func() error { + mr.SetError("forced Redis delete error") // 模擬 Redis 錯誤 + return nil + }, + wantErr: true, + errMsg: "forced Redis delete error", + }, + { + name: "Deletion of non-existent token", + token: entity.Token{ID: "nonexistent_token", UID: "user123", DeviceID: "device123"}, + wantErr: false, + jump: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 清除上一次的錯誤模擬 + mr.SetError("") + + // 執行準備函數(模擬 Redis 錯誤) + if tt.prepareFunc != nil { + tt.prepareFunc() + } + + // 執行 Delete 方法 + err := repo.Delete(context.Background(), tt.token) + // 檢查是否出現預期錯誤 + if tt.wantErr { + assert.Error(t, err) + if err != nil { + assert.Contains(t, err.Error(), tt.errMsg) + } + } else { + if !tt.jump { + assert.NoError(t, err) + // 驗證 Token 的鍵已刪除 + _, err = mr.Get(accessTokenKey) + assert.Error(t, miniredis.ErrKeyNotFound, err) + _, err = mr.Get(refreshTokenKey) + assert.Error(t, miniredis.ErrKeyNotFound, err) + + // 驗證 UID 和 DeviceID 關聯已刪除 + uidSetMembers, err := mr.SMembers(uidKey) + assert.Error(t, miniredis.ErrKeyNotFound, err) + assert.NotContains(t, uidSetMembers, token.ID) + + deviceIDSetMembers, err := mr.SMembers(deviceIDKey) + assert.Error(t, miniredis.ErrKeyNotFound, err) + assert.NotContains(t, deviceIDSetMembers, token.ID) + } + } + // 清除模擬錯誤 + mr.SetError("") + }) + } +} + +func TestTokenRepository_DeleteAccessTokensByDeviceID(t *testing.T) { + mr, r := setupMiniRedis() + defer mr.Close() + + // 初始化 TokenRepository + repo := &TokenRepository{TokenRepositoryParam: TokenRepositoryParam{Redis: r}} + + // 定義測試參數 + deviceID := "device123" + tokens := []entity.Token{ + { + ID: "token1", + UID: "user123", + DeviceID: deviceID, + AccessToken: "access1", + RefreshToken: "refresh1", + ExpiresIn: time.Now().UTC().Add(60 * time.Minute).UnixNano(), + RefreshExpiresIn: time.Now().UTC().Add(60 * time.Minute).UnixNano(), + }, + { + ID: "token2", + UID: "user123", + DeviceID: deviceID, + AccessToken: "access2", + RefreshToken: "refresh2", + ExpiresIn: time.Now().UTC().Add(60 * time.Minute).UnixNano(), + RefreshExpiresIn: time.Now().UTC().Add(60 * time.Minute).UnixNano(), + }, + } + + // 在 Redis 中設置初始數據 + deviceKey := domain.GetDeviceTokenRedisKey(deviceID) + for _, token := range tokens { + accessTokenKey := domain.GetAccessTokenRedisKey(token.ID) + refreshTokenKey := domain.GetRefreshTokenRedisKey(token.RefreshToken) + uidKey := domain.GetUIDTokenRedisKey(token.UID) + + _ = mr.Set(accessTokenKey, token.AccessToken) + _ = mr.Set(refreshTokenKey, token.ID) + _, _ = mr.SAdd(uidKey, token.ID) + _, _ = mr.SAdd(deviceKey, token.ID) + } + + // 定義測試場景 + tests := []struct { + name string + deviceID string + prepareFunc func() error // 用於模擬 Redis 錯誤 + wantErr bool + errMsg string + }{ + { + name: "Successful deletion of tokens by Device ID", + deviceID: deviceID, + wantErr: false, + }, + { + name: "GetAccessTokensByDeviceID error", + deviceID: deviceID, + prepareFunc: func() error { + mr.SetError("forced error in GetAccessTokensByDeviceID") // 模擬錯誤 + return nil + }, + wantErr: true, + errMsg: "forced error in GetAccessTokensByDeviceID", + }, + { + name: "Delete non-existent device ID", + deviceID: "nonexistent_device", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 清除上一次的錯誤模擬 + mr.SetError("") + + // 執行準備函數(模擬 Redis 錯誤) + if tt.prepareFunc != nil { + tt.prepareFunc() + } + + // 執行 DeleteAccessTokensByDeviceID 方法 + err := repo.DeleteAccessTokensByDeviceID(context.Background(), tt.deviceID) + + // 檢查是否出現預期錯誤 + if tt.wantErr { + assert.Error(t, err) + if err != nil { + assert.Contains(t, err.Error(), tt.errMsg) + } + } else { + assert.NoError(t, err) + + // 檢查是否刪除了 AccessToken、RefreshToken 和 UID 關聯的鍵 + for _, token := range tokens { + accessTokenKey := domain.GetAccessTokenRedisKey(token.ID) + refreshTokenKey := domain.GetRefreshTokenRedisKey(token.RefreshToken) + + _, err = mr.Get(accessTokenKey) + assert.Error(t, miniredis.ErrKeyNotFound, err) + + _, err = mr.Get(refreshTokenKey) + assert.Error(t, miniredis.ErrKeyNotFound, err) + + } + + // 檢查是否刪除了 deviceID 關聯的鍵 + _, err = mr.Get(deviceKey) + assert.Equal(t, miniredis.ErrKeyNotFound, err) + } + + // 清除模擬錯誤 + mr.SetError("") + }) + } +} + +func TestTokenRepository_DeleteOneTimeToken(t *testing.T) { + mr, r := setupMiniRedis() + defer mr.Close() + + // 初始化 TokenRepository + repo := &TokenRepository{TokenRepositoryParam: TokenRepositoryParam{Redis: r}} + + // 定義測試參數 + ids := []string{"one_time_token1", "one_time_token2"} + tokens := []entity.Token{ + {RefreshToken: "refresh_token1"}, + {RefreshToken: "refresh_token2"}, + } + + // 在 Redis 中設置模擬的數據 + for _, id := range ids { + _ = mr.Set(domain.GetRefreshTokenRedisKey(id), "dummy_value") + } + for _, token := range tokens { + _ = mr.Set(domain.GetRefreshTokenRedisKey(token.RefreshToken), "dummy_value") + } + + // 定義測試場景 + tests := []struct { + name string + ids []string + tokens []entity.Token + prepareFunc func() error // 用於模擬 Redis 錯誤 + wantErr bool + errMsg string + }{ + { + name: "Successful deletion of one-time tokens", + ids: ids, + tokens: tokens, + wantErr: false, + }, + { + name: "Deletion of non-existent one-time tokens", + ids: []string{"nonexistent_id1", "nonexistent_id2"}, + tokens: []entity.Token{{RefreshToken: "nonexistent_refresh1"}, {RefreshToken: "nonexistent_refresh2"}}, + wantErr: false, + }, + { + name: "Redis delete error", + ids: ids, + tokens: tokens, + prepareFunc: func() error { + mr.SetError("forced Redis delete error") // 模擬 Redis 刪除錯誤 + return nil + }, + wantErr: true, + errMsg: "forced Redis delete error", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 清除上一次的錯誤模擬 + mr.SetError("") + + // 執行準備函數(模擬 Redis 錯誤) + if tt.prepareFunc != nil { + tt.prepareFunc() + } + + // 執行 DeleteOneTimeToken 方法 + err := repo.DeleteOneTimeToken(context.Background(), tt.ids, tt.tokens) + + // 檢查是否出現預期錯誤 + if tt.wantErr { + assert.Error(t, err) + if err != nil { + assert.Contains(t, err.Error(), tt.errMsg) + } + } else { + assert.NoError(t, err) + + // 驗證 Redis 中的鍵已刪除 + for _, id := range tt.ids { + key := domain.GetRefreshTokenRedisKey(id) + _, err := mr.Get(key) + assert.Equal(t, miniredis.ErrKeyNotFound, err) + } + for _, token := range tt.tokens { + key := domain.GetRefreshTokenRedisKey(token.RefreshToken) + _, err := mr.Get(key) + assert.Equal(t, miniredis.ErrKeyNotFound, err) + } + } + + // 清除模擬錯誤 + mr.SetError("") + }) + } +} + +func TestTokenRepository_DeleteAccessTokensByUID(t *testing.T) { + mr, r := setupMiniRedis() + defer mr.Close() + + // 初始化 TokenRepository + repo := &TokenRepository{TokenRepositoryParam: TokenRepositoryParam{Redis: r}} + + // 定義測試參數 + uid := "user123" + tokens := []entity.Token{ + { + ID: "token1", + UID: uid, + DeviceID: "device1", + AccessToken: "access1", + RefreshToken: "refresh1", + }, + { + ID: "token2", + UID: uid, + DeviceID: "device2", + AccessToken: "access2", + RefreshToken: "refresh2", + }, + } + + // 在 Redis 中設置模擬的數據 + for _, token := range tokens { + accessTokenKey := domain.GetAccessTokenRedisKey(token.ID) + refreshTokenKey := domain.GetRefreshTokenRedisKey(token.RefreshToken) + uidKey := domain.GetUIDTokenRedisKey(uid) + + _ = mr.Set(accessTokenKey, token.AccessToken) + _ = mr.Set(refreshTokenKey, token.ID) + _, _ = mr.SAdd(uidKey, token.ID) + } + + // 定義測試場景 + tests := []struct { + name string + uid string + prepareFunc func() error // 用於模擬 Redis 錯誤 + wantErr bool + errMsg string + jump bool + }{ + { + name: "Successful deletion of tokens by UID", + uid: uid, + wantErr: false, + }, + { + name: "GetAccessTokensByUID error", + uid: uid, + prepareFunc: func() error { + mr.SetError("forced error in GetAccessTokensByUID") // 模擬查詢錯誤 + return nil + }, + wantErr: true, + errMsg: "forced error in GetAccessTokensByUID", + }, + { + name: "Delete non-existent UID", + uid: "nonexistent_uid", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 清除上一次的錯誤模擬 + mr.SetError("") + + // 執行準備函數(模擬 Redis 錯誤) + if tt.prepareFunc != nil { + tt.prepareFunc() + } + + // 執行 DeleteAccessTokensByUID 方法 + err := repo.DeleteAccessTokensByUID(context.Background(), tt.uid) + + // 檢查是否出現預期錯誤 + if tt.wantErr { + assert.Error(t, err) + if err != nil { + assert.Contains(t, err.Error(), tt.errMsg) + } + } else { + assert.NoError(t, err) + + if tt.jump { + // 驗證 Redis 中的鍵已刪除 + for _, token := range tokens { + accessTokenKey := domain.GetAccessTokenRedisKey(token.ID) + refreshTokenKey := domain.GetRefreshTokenRedisKey(token.RefreshToken) + uidKey := domain.GetUIDTokenRedisKey(uid) + + // 驗證 AccessToken 和 RefreshToken 鍵是否已刪除 + _, err := mr.Get(accessTokenKey) + assert.Error(t, miniredis.ErrKeyNotFound, err) + + _, err = mr.Get(refreshTokenKey) + assert.Error(t, miniredis.ErrKeyNotFound, err) + + // 驗證 UID 關聯是否已刪除 + uidSetMembers, err := mr.SMembers(uidKey) + assert.NoError(t, err) + assert.NotContains(t, uidSetMembers, token.ID) + } + } + } + + // 清除模擬錯誤 + mr.SetError("") + }) + } +}