diff --git a/coverage.out b/coverage.out deleted file mode 100644 index 177d503..0000000 --- a/coverage.out +++ /dev/null @@ -1,189 +0,0 @@ -mode: set -backend/pkg/library/mongo/custom_mongo_decimal.go:18.104,21.9 2 1 -backend/pkg/library/mongo/custom_mongo_decimal.go:21.9,23.3 1 1 -backend/pkg/library/mongo/custom_mongo_decimal.go:26.2,27.16 2 0 -backend/pkg/library/mongo/custom_mongo_decimal.go:27.16,29.3 1 0 -backend/pkg/library/mongo/custom_mongo_decimal.go:31.2,31.35 1 0 -backend/pkg/library/mongo/custom_mongo_decimal.go:34.104,36.16 2 0 -backend/pkg/library/mongo/custom_mongo_decimal.go:36.16,38.3 1 0 -backend/pkg/library/mongo/custom_mongo_decimal.go:41.2,42.16 2 0 -backend/pkg/library/mongo/custom_mongo_decimal.go:42.16,44.3 1 0 -backend/pkg/library/mongo/custom_mongo_decimal.go:47.2,49.12 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:18.171,20.16 2 1 -backend/pkg/library/mongo/doc-db-with-cache.go:20.16,22.3 1 1 -backend/pkg/library/mongo/doc-db-with-cache.go:24.2,29.8 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:32.84,34.2 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:36.66,38.2 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:40.66,42.2 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:45.137,48.27 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:48.27,49.17 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:49.17,51.28 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:51.28,53.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:54.4,54.26 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:54.26,56.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:57.4,57.23 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:57.23,59.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:60.4,60.44 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:64.2,65.16 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:65.16,67.3 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:69.2,69.46 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:69.46,71.3 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:73.2,73.17 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:77.127,78.57 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:78.57,81.28 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:81.28,82.18 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:82.18,84.29 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:84.29,86.6 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:87.5,87.27 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:87.27,89.6 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:90.5,90.24 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:90.24,92.6 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:93.5,93.30 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:93.30,95.6 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:96.5,96.24 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:96.24,98.6 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:99.5,99.24 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:99.24,101.6 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:102.5,102.45 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:106.3,106.63 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:111.145,114.27 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:114.27,115.17 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:115.17,117.28 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:117.28,119.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:120.4,120.26 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:120.26,122.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:123.4,123.23 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:123.23,125.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:126.4,126.29 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:126.29,128.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:129.4,129.23 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:129.23,131.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:132.4,132.44 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:136.2,136.87 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:136.87,138.3 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:140.2,140.30 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:144.160,147.27 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:147.27,148.17 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:148.17,150.28 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:150.28,152.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:153.4,153.26 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:153.26,155.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:156.4,156.23 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:156.23,158.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:159.4,159.29 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:159.29,161.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:162.4,162.33 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:162.33,164.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:165.4,165.23 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:165.23,167.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:168.4,168.25 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:168.25,170.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:171.4,171.44 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:175.2,175.101 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:175.101,177.3 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:179.2,179.30 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:183.156,186.27 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:186.27,187.17 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:187.17,189.43 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:189.43,191.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:192.4,192.26 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:192.26,194.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:195.4,195.44 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:199.2,200.16 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:200.16,202.3 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:204.2,204.45 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:204.45,206.3 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:208.2,208.17 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:212.156,215.27 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:215.27,216.17 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:216.17,218.31 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:218.31,220.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:221.4,221.43 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:221.43,223.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:224.4,224.28 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:224.28,226.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:227.4,227.26 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:227.26,229.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:230.4,230.23 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:230.23,232.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:233.4,233.25 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:233.25,235.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:236.4,236.44 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:240.2,241.16 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:241.16,243.3 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:245.2,245.45 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:245.45,247.3 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:249.2,249.17 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:253.164,256.27 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:256.27,257.17 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:257.17,259.31 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:259.31,261.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:262.4,262.43 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:262.43,264.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:265.4,265.28 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:265.28,267.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:268.4,268.26 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:268.26,270.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:271.4,271.23 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:271.23,273.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:274.4,274.25 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:274.25,276.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:277.4,277.44 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:281.2,282.16 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:282.16,284.3 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:286.2,286.49 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:286.49,288.3 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:290.2,290.17 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:294.159,297.27 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:297.27,298.17 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:298.17,300.31 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:300.31,302.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:303.4,303.43 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:303.43,305.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:306.4,306.28 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:306.28,308.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:309.4,309.26 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:309.26,311.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:312.4,312.23 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:312.23,314.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:315.4,315.25 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:315.25,317.5 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:318.4,318.44 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:322.2,323.16 2 0 -backend/pkg/library/mongo/doc-db-with-cache.go:323.16,325.3 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:327.2,327.45 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:327.45,329.3 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:331.2,331.17 1 0 -backend/pkg/library/mongo/doc-db-with-cache.go:337.77,339.2 1 0 -backend/pkg/library/mongo/doc-db.go:23.100,25.23 2 1 -backend/pkg/library/mongo/doc-db.go:25.23,31.3 1 1 -backend/pkg/library/mongo/doc-db.go:33.2,41.16 3 1 -backend/pkg/library/mongo/doc-db.go:41.16,43.3 1 1 -backend/pkg/library/mongo/doc-db.go:44.2,46.43 3 0 -backend/pkg/library/mongo/doc-db.go:46.43,50.3 3 0 -backend/pkg/library/mongo/doc-db.go:52.2,57.16 5 0 -backend/pkg/library/mongo/doc-db.go:57.16,59.3 1 0 -backend/pkg/library/mongo/doc-db.go:61.2,68.16 4 0 -backend/pkg/library/mongo/doc-db.go:68.16,70.3 1 0 -backend/pkg/library/mongo/doc-db.go:71.2,75.8 2 0 -backend/pkg/library/mongo/doc-db.go:78.101,85.16 5 0 -backend/pkg/library/mongo/doc-db.go:85.16,87.3 1 0 -backend/pkg/library/mongo/doc-db.go:90.115,98.16 5 0 -backend/pkg/library/mongo/doc-db.go:98.16,100.3 1 0 -backend/pkg/library/mongo/doc-db.go:103.112,104.29 1 0 -backend/pkg/library/mongo/doc-db.go:104.29,108.3 2 0 -backend/pkg/library/mongo/doc-db.go:110.2,115.16 5 0 -backend/pkg/library/mongo/doc-db.go:115.16,117.3 1 0 -backend/pkg/library/mongo/doc-db.go:120.52,122.2 1 0 -backend/pkg/library/mongo/doc-db.go:124.144,126.29 2 0 -backend/pkg/library/mongo/doc-db.go:126.29,130.3 3 0 -backend/pkg/library/mongo/doc-db.go:131.2,131.21 1 0 -backend/pkg/library/mongo/doc-db.go:131.21,133.3 1 0 -backend/pkg/library/mongo/doc-db.go:134.2,139.14 3 0 -backend/pkg/library/mongo/option.go:19.56,20.40 1 1 -backend/pkg/library/mongo/option.go:20.40,22.32 2 0 -backend/pkg/library/mongo/option.go:22.32,25.4 2 0 -backend/pkg/library/mongo/option.go:26.3,26.26 1 0 -backend/pkg/library/mongo/option.go:31.40,37.2 1 1 -backend/pkg/library/mongo/option.go:39.44,40.43 1 1 -backend/pkg/library/mongo/option.go:40.43,45.3 4 0 diff --git a/gateway.json b/gateway.json index 12b283b..363bef2 100644 --- a/gateway.json +++ b/gateway.json @@ -9,7 +9,8 @@ }, "CredentialsPayload": { "properties": { - "accountType": { + "account_type": { + "description": "帳號型別 email phone any", "type": "string" }, "password": { @@ -24,7 +25,7 @@ "required": [ "password", "password_confirm", - "accountType" + "account_type" ], "type": "object" }, @@ -52,6 +53,7 @@ "LoginReq": { "properties": { "auth_method": { + "description": "驗證類型 credentials platform", "type": "string" }, "credentials": { @@ -117,7 +119,7 @@ "PlatformPayload": { "properties": { "provider": { - "description": "平台名稱", + "description": "平台名稱,目前支援 google line apple", "type": "string" }, "token": { @@ -1148,7 +1150,7 @@ "url": "https://localhost:8888" } ], - "x-date": "2025-10-02 22:08:26", + "x-date": "2025-10-02 23:03:53", "x-description": "This is a go-doc generated swagger file.", "x-generator": "go-doc", "x-github": "https://github.com/danielchan-25/go-doc", diff --git a/generate/api/member.api b/generate/api/member.api index e313d3b..1801e3a 100644 --- a/generate/api/member.api +++ b/generate/api/member.api @@ -10,18 +10,18 @@ type ( CredentialsPayload { Password string `json:"password" validate:"required,min=8,max=128"` // 密碼 (後端應使用 bcrypt 進行雜湊) PasswordConfirm string `json:"password_confirm" validate:"eqfield=Password"` // 確認密碼 - AccountType string `json:"accountType" validate:"required,oneof=email phone any"` + AccountType string `json:"account_type" validate:"required,oneof=email phone any"` // 帳號型別 email phone any } // PlatformPayload 第三方平台註冊的資料 PlatformPayload { - Provider string `json:"provider" validate:"required,oneof=google line apple"` // 平台名稱 + Provider string `json:"provider" validate:"required,oneof=google line apple"` // 平台名稱,目前支援 google line apple Token string `json:"token" validate:"required"` // 平台提供的 Access Token 或 ID Token } // RegisterReq 註冊請求 (整合了兩種方式) LoginReq { - AuthMethod string `json:"auth_method" validate:"required,oneof=credentials platform"` + AuthMethod string `json:"auth_method" validate:"required,oneof=credentials platform"`// 驗證類型 credentials platform LoginID string `json:"login_id" validate:"required,min=3,max=50"` // 信箱或手機號碼 Credentials *CredentialsPayload `json:"credentials,optional"` // AuthMethod 為 'credentials' 時使用 Platform *PlatformPayload `json:"platform,optional"` // AuthMethod 為 'platform' 時使用 diff --git a/go.mod b/go.mod index 5b26b59..4976c66 100644 --- a/go.mod +++ b/go.mod @@ -7,22 +7,38 @@ replace backend/pkg/library/errs => ./pkg/library/errs require ( code.30cm.net/digimon/library-go/utils/invited_code v1.2.5 github.com/alicebob/miniredis/v2 v2.35.0 + github.com/aws/aws-sdk-go-v2 v1.39.2 + github.com/aws/aws-sdk-go-v2/credentials v1.18.16 + github.com/aws/aws-sdk-go-v2/service/ses v1.34.5 + github.com/go-playground/validator/v10 v10.27.0 + github.com/matcornic/hermes/v2 v2.1.0 + github.com/minchao/go-mitake v1.0.0 + github.com/panjf2000/ants/v2 v2.11.3 github.com/shopspring/decimal v1.4.0 github.com/stretchr/testify v1.11.1 github.com/testcontainers/testcontainers-go v0.39.0 - github.com/zeromicro/go-zero v1.9.0 - go.mongodb.org/mongo-driver v1.17.1 + github.com/zeromicro/go-zero v1.9.1 + go.mongodb.org/mongo-driver v1.17.4 go.mongodb.org/mongo-driver/v2 v2.3.0 - go.uber.org/mock v0.4.0 - golang.org/x/crypto v0.37.0 - google.golang.org/grpc v1.67.0 - google.golang.org/protobuf v1.36.5 + go.uber.org/mock v0.6.0 + golang.org/x/crypto v0.42.0 + google.golang.org/grpc v1.75.1 + google.golang.org/protobuf v1.36.10 + gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df ) require ( dario.cat/mergo v1.0.2 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Masterminds/semver v1.4.2 // indirect + github.com/Masterminds/sprig v2.16.0+incompatible // indirect github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/PuerkitoBio/goquery v1.5.0 // indirect + github.com/andybalholm/cascadia v1.0.0 // indirect + github.com/aokoli/goutils v1.0.1 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 // indirect + github.com/aws/smithy-go v1.23.0 // 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 @@ -40,21 +56,30 @@ require ( github.com/ebitengine/purego v0.8.4 // indirect github.com/fatih/color v1.18.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang/snappy v1.0.0 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grafana/pyroscope-go v1.2.4 // indirect - github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect + github.com/gorilla/css v1.0.0 // indirect + github.com/grafana/pyroscope-go v1.2.7 // indirect + github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect + github.com/huandu/xstrings v1.2.0 // indirect + github.com/imdario/mergo v0.3.6 // indirect + github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0 // indirect github.com/klauspost/compress v1.18.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.10 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/go-archive v0.1.0 // indirect github.com/moby/patternmatcher v0.6.0 // indirect @@ -64,6 +89,7 @@ require ( github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/olekukonko/tablewriter v0.0.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/openzipkin/zipkin-go v0.4.3 // indirect @@ -75,38 +101,45 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/redis/go-redis/v9 v9.12.1 // indirect + github.com/redis/go-redis/v9 v9.15.0 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/russross/blackfriday/v2 v2.0.1 // indirect github.com/shirou/gopsutil/v4 v4.25.6 // indirect + github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect + github.com/vanng822/css v0.0.0-20190504095207-a21e860bcd04 // indirect + github.com/vanng822/go-premailer v0.0.0-20191214114701-be27abe028fe // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect github.com/yuin/gopher-lua v1.1.1 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect - go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect - go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel v1.37.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.38.0 // indirect - go.opentelemetry.io/otel/sdk v1.38.0 // indirect - go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.opentelemetry.io/otel/metric v1.37.0 // indirect + go.opentelemetry.io/otel/sdk v1.37.0 // indirect + go.opentelemetry.io/otel/trace v1.37.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/automaxprocs v1.6.0 // indirect - golang.org/x/net v0.38.0 // indirect - golang.org/x/sync v0.13.0 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect - golang.org/x/text v0.24.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect + golang.org/x/text v0.29.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect + gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // 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 index 379409a..a43d5a2 100644 --- a/go.sum +++ b/go.sum @@ -6,10 +6,32 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8af github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= +github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/sprig v2.16.0+incompatible h1:QZbMUPxRQ50EKAq3LFMnxddMu88/EUUG3qmxwtDmPsY= +github.com/Masterminds/sprig v2.16.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk= +github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg= github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI= github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM= +github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o= +github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= +github.com/aokoli/goutils v1.0.1 h1:7fpzNGoJ3VA8qcrm++XEE1QUe0mIwNeLa02Nwq7RDkg= +github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= +github.com/aws/aws-sdk-go-v2 v1.39.2 h1:EJLg8IdbzgeD7xgvZ+I8M1e0fL0ptn/M47lianzth0I= +github.com/aws/aws-sdk-go-v2 v1.39.2/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY= +github.com/aws/aws-sdk-go-v2/credentials v1.18.16 h1:4JHirI4zp958zC026Sm+V4pSDwW4pwLefKrc0bF2lwI= +github.com/aws/aws-sdk-go-v2/credentials v1.18.16/go.mod h1:qQMtGx9OSw7ty1yLclzLxXCRbrkjWAM7JnObZjmCB7I= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 h1:se2vOWGD3dWQUtfn4wEjRQJb1HK1XsNIt825gskZ970= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9/go.mod h1:hijCGH2VfbZQxqCDN7bwz/4dzxV+hkyhjawAtdPWKZA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 h1:6RBnKZLkJM4hQ+kN6E7yWFveOTg8NLPHAkqrs4ZPlTU= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9/go.mod h1:V9rQKRmK7AWuEsOMnHzKj8WyrIir1yUJbZxDuZLFvXI= +github.com/aws/aws-sdk-go-v2/service/ses v1.34.5 h1:NwOeuOFrWoh4xWKINrmaAK4Vh75jmmY0RAuNjQ6W5Es= +github.com/aws/aws-sdk-go-v2/service/ses v1.34.5/go.mod h1:m3BsMJZD0eqjGIniBzwrNUqG9ZUPquC4hY9FyE2qNFo= +github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= +github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= 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= @@ -51,6 +73,9 @@ 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/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= +github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df/go.mod h1:GJr+FCSXshIwgHBtLglIg9M2l2kQSi6QjVAngtzI08Y= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -58,44 +83,75 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= +github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/grafana/pyroscope-go v1.2.4 h1:B22GMXz+O0nWLatxLuaP7o7L9dvP0clLvIpmeEQQM0Q= -github.com/grafana/pyroscope-go v1.2.4/go.mod h1:zzT9QXQAp2Iz2ZdS216UiV8y9uXJYQiGE1q8v1FyhqU= -github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg= -github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= +github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= +github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= +github.com/grafana/pyroscope-go v1.2.7 h1:VWBBlqxjyR0Cwk2W6UrE8CdcdD80GOFNutj0Kb1T8ac= +github.com/grafana/pyroscope-go v1.2.7/go.mod h1:o/bpSLiJYYP6HQtvcoVKiE9s5RiNgjYTj1DhiddP2Pc= +github.com/grafana/pyroscope-go/godeltaprof v0.1.9 h1:c1Us8i6eSmkW+Ez05d3co8kasnuOY813tbMN8i/a3Og= +github.com/grafana/pyroscope-go/godeltaprof v0.1.9/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= 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/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0= +github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0 h1:xqgexXAGQgY3HAjNPSaCqn5Aahbo5TKsmhp8VRfr1iQ= +github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= 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.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 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/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/matcornic/hermes/v2 v2.1.0 h1:9TDYFBPFv6mcXanaDmRDEp/RTWj0dTTi+LpFnnnfNWc= +github.com/matcornic/hermes/v2 v2.1.0/go.mod h1:2+ziJeoyRfaLiATIL8VZ7f9hpzH4oDHqTmn0bhrsgVI= 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/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/minchao/go-mitake v1.0.0 h1:OgfCUkSRftd6sWibpJyeKU3/gPQhq1t0ttHsnoaeVgQ= +github.com/minchao/go-mitake v1.0.0/go.mod h1:RAo0TijPUqhM2ZLMqP9x76wsomL11Ud4sDSwRYwbeGU= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= @@ -116,12 +172,16 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 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/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= 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/panjf2000/ants/v2 v2.11.3 h1:AfI0ngBoXJmYOpDh9m516vjqoUu2sLrIVgppI9TZVpg= +github.com/panjf2000/ants/v2 v2.11.3/go.mod h1:8u92CYMUc6gyvTIw8Ru7Mt7+/ESnJahz5EVtqfrilek= 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= @@ -140,23 +200,32 @@ github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= 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.12.1 h1:k5iquqv27aBtnTm2tIkROUDp8JBXhXZIVu1InSgvovg= -github.com/redis/go-redis/v9 v9.12.1/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/redis/go-redis/v9 v9.15.0 h1:2jdes0xJxer4h3NUZrZ4OGSntGlXp4WbXju2nOTRXto= +github.com/redis/go-redis/v9 v9.15.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs= github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= +github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= 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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -170,6 +239,10 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/vanng822/css v0.0.0-20190504095207-a21e860bcd04 h1:L0rPdfzq43+NV8rfIx2kA4iSSLRj2jN5ijYHoeXRwvQ= +github.com/vanng822/css v0.0.0-20190504095207-a21e860bcd04/go.mod h1:tcnB1voG49QhCrwq1W0w5hhGasvOg+VQp9i9H1rCM1w= +github.com/vanng822/go-premailer v0.0.0-20191214114701-be27abe028fe h1:9YnI5plmy+ad6BM+JCLJb2ZV7/TNiE5l7SNKfumYKgc= +github.com/vanng822/go-premailer v0.0.0-20191214114701-be27abe028fe/go.mod h1:JTFJA/t820uFDoyPpErFQ3rb3amdZoPtxcKervG0OE4= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= @@ -185,18 +258,18 @@ 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/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -github.com/zeromicro/go-zero v1.9.0 h1:hlVtQCSHPszQdcwZTawzGwTej1G2mhHybYzMRLuwCt4= -github.com/zeromicro/go-zero v1.9.0/go.mod h1:TMyCxiaOjLQ3YxyYlJrejaQZF40RlzQ3FVvFu5EbcV4= -go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM= -go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4= +github.com/zeromicro/go-zero v1.9.1 h1:GZCl4jun/ZgZHnSvX3SSNDHf+tEGmEQ8x2Z23xjHa9g= +github.com/zeromicro/go-zero v1.9.1/go.mod h1:bHOl7Xr7EV/iHZWEqsUNJwFc/9WgAMrPpPagYvOaMtY= +go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw= +go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= go.mongodb.org/mongo-driver/v2 v2.3.0 h1:sh55yOXA2vUjW1QYw/2tRlHSQViwDyPnW61AwpZ4rtU= go.mongodb.org/mongo-driver/v2 v2.3.0/go.mod h1:jHeEDJHJq7tm6ZF45Issun9dbogjfnPySb1vXA7EeAI= -go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= -go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= 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= @@ -209,46 +282,50 @@ go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 h1:s0PHtIkN+3xrbDO 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.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= 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/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/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +golang.org/x/crypto v0.0.0-20181029175232-7e6ffbd03851/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= -golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= 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-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190225065934-cc5685c2db12/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -267,14 +344,15 @@ golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= -golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= +golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -286,19 +364,27 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T 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-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= -google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw= -google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= -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= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU= +google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= +google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= 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/yaml.v2 v2.2.1/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= diff --git a/internal/handler/auth/register_handler.go b/internal/handler/auth/register_handler.go index b67c267..d2b93ae 100644 --- a/internal/handler/auth/register_handler.go +++ b/internal/handler/auth/register_handler.go @@ -25,15 +25,17 @@ func RegisterHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { return } - //if err := svcCtx.Validate.ValidateAll(req); err != nil { - // e := errs.InvalidFormat(err.Error()) - // httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.RespOK{ - // Code: int(e.FullCode()), - // Msg: err.Error(), - // }) - // - // return - //} + + if err := svcCtx.Validate.ValidateAll(req); err != nil { + e := errs.InvalidFormat(err.Error()) + httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.RespOK{ + Code: int(e.FullCode()), + Msg: err.Error(), + }) + + return + } + l := auth.NewRegisterLogic(r.Context(), svcCtx) resp, err := l.Register(&req) if err != nil { diff --git a/internal/handler/routes.go b/internal/handler/routes.go index 0a58aa2..23cfd99 100644 --- a/internal/handler/routes.go +++ b/internal/handler/routes.go @@ -1,5 +1,5 @@ // Code generated by goctl. DO NOT EDIT. -// goctl 1.8.5 +// goctl 1.8.1 package handler diff --git a/internal/logic/auth/register_logic.go b/internal/logic/auth/register_logic.go index 15a7094..6d73723 100644 --- a/internal/logic/auth/register_logic.go +++ b/internal/logic/auth/register_logic.go @@ -3,14 +3,15 @@ package auth import ( "backend/internal/svc" "backend/internal/types" - mb "backend/pkg/member/domain/member" - member "backend/pkg/member/domain/usecase" "backend/pkg/library/errs" "backend/pkg/library/errs/code" + mb "backend/pkg/member/domain/member" + member "backend/pkg/member/domain/usecase" "context" - "google.golang.org/protobuf/proto" "time" + "google.golang.org/protobuf/proto" + "github.com/zeromicro/go-zero/core/logx" ) @@ -36,7 +37,7 @@ func NewRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Register } func (l *RegisterLogic) Register(req *types.LoginReq) (resp *types.LoginResp, err error) { - // Step 1: 根據平台構建相關數據 + // Step 1: 根據平台構建相關資料 var bd buildData switch req.AuthMethod { case "credentials": diff --git a/internal/svc/account_model.go b/internal/svc/account_model.go index c89e887..6200bf1 100644 --- a/internal/svc/account_model.go +++ b/internal/svc/account_model.go @@ -8,6 +8,7 @@ import ( "backend/pkg/member/repository" uc "backend/pkg/member/usecase" "context" + "github.com/zeromicro/go-zero/core/stores/cache" "github.com/zeromicro/go-zero/core/stores/mon" "github.com/zeromicro/go-zero/core/stores/redis" diff --git a/internal/svc/service_context.go b/internal/svc/service_context.go index 769bc76..43160f2 100644 --- a/internal/svc/service_context.go +++ b/internal/svc/service_context.go @@ -3,6 +3,9 @@ package svc import ( "backend/internal/config" "backend/internal/middleware" + "backend/pkg/library/errs" + "backend/pkg/library/errs/code" + vi "backend/pkg/library/validator" "backend/pkg/member/domain/usecase" "github.com/zeromicro/go-zero/core/stores/redis" @@ -13,6 +16,7 @@ type ServiceContext struct { Config config.Config AuthMiddleware rest.Middleware AccountUC usecase.AccountUseCase + Validate vi.Validate } func NewServiceContext(c config.Config) *ServiceContext { @@ -20,10 +24,12 @@ func NewServiceContext(c config.Config) *ServiceContext { if err != nil { panic(err) } + errs.Scope = code.CloudEPPortalGW return &ServiceContext{ Config: c, AuthMiddleware: middleware.NewAuthMiddleware().Handle, AccountUC: NewAccountUC(&c, rds), + Validate: vi.MustValidator(), } } diff --git a/internal/types/types.go b/internal/types/types.go index 46f4e3c..0d9ad4c 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -1,5 +1,5 @@ // Code generated by goctl. DO NOT EDIT. -// goctl 1.8.5 +// goctl 1.8.1 package types @@ -11,9 +11,9 @@ type BaseReq struct { } type CredentialsPayload struct { - Password string `json:"password" validate:"required,min=8,max=128"` // 密碼 (後端應使用 bcrypt 進行雜湊) - PasswordConfirm string `json:"password_confirm" validate:"eqfield=Password"` // 確認密碼 - AccountType string `json:"accountType" validate:"required,oneof=email phone any"` + Password string `json:"password" validate:"required,min=8,max=128"` // 密碼 (後端應使用 bcrypt 進行雜湊) + PasswordConfirm string `json:"password_confirm" validate:"eqfield=Password"` // 確認密碼 + AccountType string `json:"account_type" validate:"required,oneof=email phone any"` // 帳號型別 email phone any } type ErrorResp struct { @@ -24,10 +24,10 @@ type ErrorResp struct { } type LoginReq struct { - AuthMethod string `json:"auth_method" validate:"required,oneof=credentials platform"` - LoginID string `json:"login_id" validate:"required,min=3,max=50"` // 信箱或手機號碼 - Credentials *CredentialsPayload `json:"credentials,optional"` // AuthMethod 為 'credentials' 時使用 - Platform *PlatformPayload `json:"platform,optional"` // AuthMethod 為 'platform' 時使用 + AuthMethod string `json:"auth_method" validate:"required,oneof=credentials platform"` // 驗證類型 credentials platform + LoginID string `json:"login_id" validate:"required,min=3,max=50"` // 信箱或手機號碼 + Credentials *CredentialsPayload `json:"credentials,optional"` // AuthMethod 為 'credentials' 時使用 + Platform *PlatformPayload `json:"platform,optional"` // AuthMethod 為 'platform' 時使用 } type LoginResp struct { @@ -44,7 +44,7 @@ type PagerResp struct { } type PlatformPayload struct { - Provider string `json:"provider" validate:"required,oneof=google line apple"` // 平台名稱 + Provider string `json:"provider" validate:"required,oneof=google line apple"` // 平台名稱,目前支援 google line apple Token string `json:"token" validate:"required"` // 平台提供的 Access Token 或 ID Token } diff --git a/pkg/library/errs/easy_func.go b/pkg/library/errs/easy_func.go index 05b4d27..69d911d 100644 --- a/pkg/library/errs/easy_func.go +++ b/pkg/library/errs/easy_func.go @@ -6,6 +6,7 @@ import ( "strings" "backend/pkg/library/errs/code" + "github.com/zeromicro/go-zero/core/logx" "google.golang.org/grpc/status" ) @@ -305,6 +306,12 @@ func ResourceAlreadyExist(s ...string) *LibError { fmt.Sprintf("%s", strings.Join(s, " "))) } +// ResourceAlreadyExistWithScope returns Err +func ResourceAlreadyExistWithScope(scope uint32, s ...string) *LibError { + return NewError(scope, code.ResourceAlreadyExist, defaultDetailCode, + fmt.Sprintf("%s", strings.Join(s, " "))) +} + // ResourceAlreadyExistL logs error message and returns Err func ResourceAlreadyExistL(l logx.Logger, filed []logx.LogField, s ...string) *LibError { e := ResourceAlreadyExist(s...) diff --git a/pkg/library/errs/easy_func_test.go b/pkg/library/errs/easy_func_test.go index 5ddcadd..d18359a 100644 --- a/pkg/library/errs/easy_func_test.go +++ b/pkg/library/errs/easy_func_test.go @@ -5,6 +5,7 @@ import ( "testing" "backend/pkg/library/errs/code" + "github.com/zeromicro/go-zero/core/logx" ) diff --git a/pkg/library/errs/error_code.go b/pkg/library/errs/error_code.go index d5125a4..6a0a1df 100644 --- a/pkg/library/errs/error_code.go +++ b/pkg/library/errs/error_code.go @@ -5,6 +5,7 @@ import ( "strings" "backend/pkg/library/errs/code" + "github.com/zeromicro/go-zero/core/logx" ) diff --git a/pkg/library/errs/errors.go b/pkg/library/errs/errors.go index 03a89f2..e4a0c0b 100644 --- a/pkg/library/errs/errors.go +++ b/pkg/library/errs/errors.go @@ -6,6 +6,7 @@ import ( "net/http" "backend/pkg/library/errs/code" + "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -154,8 +155,8 @@ func (e *LibError) HTTPStatus() int { return http.StatusOK } // 根據錯誤碼判斷對應的 HTTP 狀態碼 - switch e.Code() % 100 { - case code.ResourceInsufficient: + switch e.Code() / 100 { + case code.ResourceInsufficient, code.InvalidFormat: // 如果資源不足,返回 400 狀態碼 return http.StatusBadRequest case code.Unauthorized, code.InsufficientPermission: diff --git a/pkg/library/errs/errors_test.go b/pkg/library/errs/errors_test.go index 15e1f1e..f690ad6 100644 --- a/pkg/library/errs/errors_test.go +++ b/pkg/library/errs/errors_test.go @@ -6,6 +6,7 @@ import ( "testing" "backend/pkg/library/errs/code" + "google.golang.org/grpc/codes" ) diff --git a/pkg/library/validator/validate.go b/pkg/library/validator/validate.go new file mode 100644 index 0000000..d53fb48 --- /dev/null +++ b/pkg/library/validator/validate.go @@ -0,0 +1,50 @@ +package required + +import ( + "fmt" + + "github.com/go-playground/validator/v10" + + "github.com/zeromicro/go-zero/core/logx" +) + +type Validate interface { + ValidateAll(obj any) error + BindToValidator(opts ...Option) error +} + +type Validator struct { + V *validator.Validate +} + +func (v *Validator) ValidateAll(obj any) error { + err := v.V.Struct(obj) + if err != nil { + return err + } + + return nil +} + +func (v *Validator) BindToValidator(opts ...Option) error { + for _, item := range opts { + err := v.V.RegisterValidation(item.ValidatorName, item.ValidatorFunc) + if err != nil { + return fmt.Errorf("failed to register validator : %w", err) + } + } + + return nil +} + +func MustValidator(option ...Option) Validate { + v := &Validator{ + V: validator.New(), + } + + if err := v.BindToValidator(option...); err != nil { + logx.Error("failed to bind validator") + } + + return v +} diff --git a/pkg/library/validator/validate_option.go b/pkg/library/validator/validate_option.go new file mode 100644 index 0000000..8a896cb --- /dev/null +++ b/pkg/library/validator/validate_option.go @@ -0,0 +1,29 @@ +package required + +import ( + "regexp" + + "github.com/go-playground/validator/v10" +) + +type Option struct { + ValidatorName string + ValidatorFunc func(fl validator.FieldLevel) bool +} + +// WithAccount 創建一個新的 Option 結構,包含自定義的驗證函數,用於驗證 email 和台灣的手機號碼格式 +func WithAccount(tagName string) Option { + return Option{ + ValidatorName: tagName, + ValidatorFunc: func(fl validator.FieldLevel) bool { + value := fl.Field().String() + emailRegex := `^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$` + phoneRegex := `^(\+886|0)?9\d{8}$` + + emailMatch, _ := regexp.MatchString(emailRegex, value) + phoneMatch, _ := regexp.MatchString(phoneRegex, value) + + return emailMatch || phoneMatch + }, + } +} diff --git a/pkg/library/worker_pool/worker_pool.go b/pkg/library/worker_pool/worker_pool.go new file mode 100644 index 0000000..d6cf3b0 --- /dev/null +++ b/pkg/library/worker_pool/worker_pool.go @@ -0,0 +1,71 @@ +package worker_pool + +import ( + "sync" + + "github.com/panjf2000/ants/v2" +) + +const defaultWorkerPoolSize = 2000 + +type WorkerPool interface { + Submit(task func()) error + SubmitAndWaitAll(tasks ...func() error) (taskErr chan error, submitErr error) +} + +type workerPool struct { + p *ants.Pool +} + +func NewWorkerPool(size int) WorkerPool { + if size <= 0 { + size = defaultWorkerPoolSize + } + + p, err := ants.NewPool( + size, + ants.WithDisablePurge(true), + ) + if err != nil { + return &workerPool{p: nil} + } + + return &workerPool{p: p} +} + +func (p *workerPool) Submit(task func()) error { + if p.p == nil { + return ants.Submit(task) + } + + return p.p.Submit(task) +} + +func (p *workerPool) SubmitAndWaitAll(tasks ...func() error) (chan error, error) { + taskErrCh := make(chan error, len(tasks)) + submitErrCh := make(chan error, len(tasks)) + wg := sync.WaitGroup{} + wg.Add(len(tasks)) + + for i := range tasks { + task := tasks[i] + err := p.Submit(func() { + defer wg.Done() + if err := task(); err != nil { + taskErrCh <- err + } + }) + if err != nil { + submitErrCh <- err + wg.Done() + } + } + + wg.Wait() + + if len(submitErrCh) != 0 { + return nil, <-submitErrCh + } + + return taskErrCh, nil +} diff --git a/pkg/library/worker_pool/worker_pool_test.go b/pkg/library/worker_pool/worker_pool_test.go new file mode 100644 index 0000000..4c99cf2 --- /dev/null +++ b/pkg/library/worker_pool/worker_pool_test.go @@ -0,0 +1,89 @@ +package worker_pool + +import ( + "errors" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestNewWorkerPool(t *testing.T) { + t.Run("default size pool", func(t *testing.T) { + pool := NewWorkerPool(0) + assert.NotNil(t, pool) + }) + + t.Run("custom size pool", func(t *testing.T) { + size := 100 + pool := NewWorkerPool(size) + assert.NotNil(t, pool) + }) +} + +func TestSubmit(t *testing.T) { + t.Run("submit task to worker pool", func(t *testing.T) { + pool := NewWorkerPool(10) + var wg sync.WaitGroup + wg.Add(1) + + err := pool.Submit(func() { + defer wg.Done() + time.Sleep(100 * time.Millisecond) + }) + assert.NoError(t, err) + + wg.Wait() + }) +} + +func TestSubmitAndWaitAll(t *testing.T) { + t.Run("submit and wait all tasks succeed", func(t *testing.T) { + pool := NewWorkerPool(10) + tasks := []func() error{ + func() error { + time.Sleep(100 * time.Millisecond) + return nil + }, + func() error { + time.Sleep(50 * time.Millisecond) + return nil + }, + } + + taskErrCh, submitErr := pool.SubmitAndWaitAll(tasks...) + assert.NoError(t, submitErr) + close(taskErrCh) + for err := range taskErrCh { + assert.NoError(t, err) + } + }) + + t.Run("submit and wait all tasks with errors", func(t *testing.T) { + pool := NewWorkerPool(10) + expectedError := errors.New("task error") + tasks := []func() error{ + func() error { + time.Sleep(100 * time.Millisecond) + return nil + }, + func() error { + time.Sleep(50 * time.Millisecond) + return expectedError + }, + } + + taskErrCh, submitErr := pool.SubmitAndWaitAll(tasks...) + assert.NoError(t, submitErr) + close(taskErrCh) + foundError := false + for err := range taskErrCh { + if err != nil { + foundError = true + assert.Equal(t, expectedError, err) + } + } + assert.True(t, foundError) + }) +} diff --git a/pkg/member/usecase/binding.go b/pkg/member/usecase/binding.go index 5966624..de75822 100644 --- a/pkg/member/usecase/binding.go +++ b/pkg/member/usecase/binding.go @@ -11,6 +11,7 @@ import ( "backend/pkg/library/errs" "backend/pkg/library/errs/code" + "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/stores/mon" ) diff --git a/pkg/member/usecase/generate.go b/pkg/member/usecase/generate.go index 93c68f6..e239457 100644 --- a/pkg/member/usecase/generate.go +++ b/pkg/member/usecase/generate.go @@ -9,6 +9,7 @@ import ( "backend/pkg/library/errs" "backend/pkg/library/errs/code" + GIDLib "code.30cm.net/digimon/library-go/utils/invited_code" "github.com/zeromicro/go-zero/core/logx" ) diff --git a/pkg/member/usecase/member.go b/pkg/member/usecase/member.go index f8390e1..f1628f1 100644 --- a/pkg/member/usecase/member.go +++ b/pkg/member/usecase/member.go @@ -4,9 +4,10 @@ import ( "context" "errors" "fmt" - "go.mongodb.org/mongo-driver/v2/mongo" "math" + "go.mongodb.org/mongo-driver/v2/mongo" + "backend/pkg/member/domain" "backend/pkg/member/domain/entity" "backend/pkg/member/domain/member" @@ -15,6 +16,7 @@ import ( "backend/pkg/library/errs" "backend/pkg/library/errs/code" + "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/stores/mon" ) @@ -86,7 +88,7 @@ func (use *MemberUseCase) insertAccount(ctx context.Context, account *entity.Acc if err != nil { if mongo.IsDuplicateKeyError(err) { - return errs.DBDuplicate("account duplicate").Wrap(err) + return errs.ResourceAlreadyExistWithScope(code.CloudEPMember, "account duplicate").Wrap(err) } return errs.DatabaseErrorWithScopeL( code.CloudEPMember, diff --git a/pkg/member/usecase/member_test.go b/pkg/member/usecase/member_test.go index b355b05..f74a874 100644 --- a/pkg/member/usecase/member_test.go +++ b/pkg/member/usecase/member_test.go @@ -15,6 +15,7 @@ import ( "backend/pkg/library/errs" "backend/pkg/library/errs/code" + "github.com/stretchr/testify/assert" "github.com/zeromicro/go-zero/core/stores/mon" "go.uber.org/mock/gomock" diff --git a/pkg/member/usecase/verify_google_test.go b/pkg/member/usecase/verify_google_test.go index bfe6dab..71af03a 100644 --- a/pkg/member/usecase/verify_google_test.go +++ b/pkg/member/usecase/verify_google_test.go @@ -10,6 +10,7 @@ import ( "backend/pkg/library/errs" "backend/pkg/library/errs/code" + "github.com/stretchr/testify/assert" ) diff --git a/pkg/member/usecase/verify_test.go b/pkg/member/usecase/verify_test.go index db24859..c2ac49b 100644 --- a/pkg/member/usecase/verify_test.go +++ b/pkg/member/usecase/verify_test.go @@ -12,6 +12,7 @@ import ( mockRepo "backend/pkg/member/mock/repository" "backend/pkg/library/errs" + "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" ) diff --git a/pkg/notification/config/config.go b/pkg/notification/config/config.go new file mode 100644 index 0000000..054b66a --- /dev/null +++ b/pkg/notification/config/config.go @@ -0,0 +1,34 @@ +package config + +type SMTPConfig struct { + Enable bool + Sort int + GoroutinePoolNum int + + Host string + Port int + Username string + Password string +} + +type AmazonSesSettings struct { + Enable bool + Sort int + PoolSize int + + Region string + Sender string + Charset string + AccessKey string + SecretKey string + Token string +} + +type MitakeSMSSender struct { + Enable bool + Sort int + PoolSize int + + User string + Password string +} diff --git a/pkg/notification/domain/error.go b/pkg/notification/domain/error.go new file mode 100644 index 0000000..37b5200 --- /dev/null +++ b/pkg/notification/domain/error.go @@ -0,0 +1,29 @@ +package domain + +import ( + "fmt" + "strings" + + ers "backend/pkg/library/errs" + "backend/pkg/library/errs/code" + + "github.com/zeromicro/go-zero/core/logx" +) + +func ThirdPartyError(scope uint32, ec ers.ErrorCode, s ...string) *ers.LibError { + return ers.NewError(scope, code.ThirdParty, ec.ToUint32(), fmt.Sprintf("thirty error: %s", strings.Join(s, " "))) +} + +func ThirdPartyErrorL(scope uint32, ec ers.ErrorCode, + l logx.Logger, filed []logx.LogField, s ...string) *ers.LibError { + e := ThirdPartyError(scope, ec, s...) + l.WithCallerSkip(1).WithFields(filed...).Error(e.Error()) + + return e +} + +const ( + NotificationErrorCode = 1 + iota + FailedToSendEmailErrorCode + FailedToSendSMSErrorCode +) diff --git a/pkg/notification/domain/notification/language.go b/pkg/notification/domain/notification/language.go new file mode 100644 index 0000000..693fb68 --- /dev/null +++ b/pkg/notification/domain/notification/language.go @@ -0,0 +1,9 @@ +package notification + +// Language 定義模板請求 +type Language string + +const ( + LanguageZhTW Language = "zh-tw" + LanguageEnUS Language = "en-us" +) diff --git a/pkg/notification/domain/notification/type.go b/pkg/notification/domain/notification/type.go new file mode 100644 index 0000000..fc0810a --- /dev/null +++ b/pkg/notification/domain/notification/type.go @@ -0,0 +1,16 @@ +package notification + +import "fmt" + +type TypeID int64 + +func (id TypeID) String() string { + return fmt.Sprintf("%4d", id) +} + +// 驗證碼通知類 0 ~ 100 +const ( + BindingEmail TypeID = 1 // 驗證碼:綁定 Email + BindingPhone TypeID = 2 // 驗證碼:綁定 手機 + ForgetPasswordVerify TypeID = 3 // 驗證碼: 忘記密碼 +) diff --git a/pkg/notification/domain/repository/mail.go b/pkg/notification/domain/repository/mail.go new file mode 100644 index 0000000..f95f764 --- /dev/null +++ b/pkg/notification/domain/repository/mail.go @@ -0,0 +1,15 @@ +package repository + +import "context" + +type MailRepository interface { + // SendMail 送出 Email + SendMail(ctx context.Context, req MailReq) error +} + +type MailReq struct { + To []string + From string + Subject string + Body string +} diff --git a/pkg/notification/domain/repository/sms.go b/pkg/notification/domain/repository/sms.go new file mode 100644 index 0000000..b9b230e --- /dev/null +++ b/pkg/notification/domain/repository/sms.go @@ -0,0 +1,16 @@ +package repository + +import ( + "context" +) + +type SMSClientRepository interface { + SendSMS(ctx context.Context, req SMSMessageRequest) error +} + +// SMSMessageRequest SMS 訊息請求結構 +type SMSMessageRequest struct { + PhoneNumber string `json:"phone_number" validate:"required,e164"` // 接收者號碼 (e164 格式用於驗證國際號碼) + RecipientName string `json:"recipient_name" validate:"required"` // 接收者姓名 + MessageContent string `json:"message_content" validate:"required"` // 要傳送的訊息 +} diff --git a/pkg/notification/domain/template/const.go b/pkg/notification/domain/template/const.go new file mode 100644 index 0000000..3e2d6a3 --- /dev/null +++ b/pkg/notification/domain/template/const.go @@ -0,0 +1,12 @@ +package template + +// ============================== +// 產品資訊常數 +// ============================== + +const ( + ProductName = "TrueHeart 團隊" + ProductLink = "https://code.30cm.net" + ProductLogo = "https://true-heart-dev.s3.ap-northeast-3.amazonaws.com/f70904eb-1a29-40f7-8940-9a124f23793a.png" + ProductCopyright = "© 2025 TrueHeart Inc. 版權所有" +) diff --git a/pkg/notification/domain/template/email.go b/pkg/notification/domain/template/email.go new file mode 100644 index 0000000..652ff36 --- /dev/null +++ b/pkg/notification/domain/template/email.go @@ -0,0 +1,128 @@ +package template + +import ( + "github.com/matcornic/hermes/v2" +) + +// ============================== +// Email 結構定義 +// ============================== + +// EmailTemplate 代表 Email 樣板 +type EmailTemplate struct { + Title string + Body string +} + +// ProductInfo 包含產品相關的資訊 +type ProductInfo struct { + Name string + Link string + Logo string + Copyright string +} + +// EmailBodyContent 包含郵件正文的資訊 +type EmailBodyContent struct { + RecipientName string + Intros []string + Actions []hermes.Action + Outros []string + Signature string +} + +// EmailContentParams 組成 Email 內容的參數 +type EmailContentParams struct { + Product ProductInfo + Content EmailBodyContent +} + +// ============================== +// Email 內容產生器 +// ============================== + +// GenerateEmailBody 根據參數產生 Email HTML 內容 +func GenerateEmailBody(params EmailContentParams) (string, error) { + product := hermes.Product{ + Name: params.Product.Name, + Link: params.Product.Link, + Logo: params.Product.Logo, + Copyright: params.Product.Copyright, + } + + body := hermes.Body{ + Name: params.Content.RecipientName, + Intros: params.Content.Intros, + Actions: params.Content.Actions, + Outros: params.Content.Outros, + Signature: params.Content.Signature, + } + + h := hermes.Hermes{Product: product} + email := hermes.Email{Body: body} + + return h.GenerateHTML(email) +} + +// GenerateEmailContent 生成 Email 內容(可用於不同驗證類型) +func GenerateEmailContent(title string, intros []string) (EmailTemplate, error) { + req := EmailContentParams{ + Product: ProductInfo{ + Name: ProductName, + Link: ProductLink, + Logo: ProductLogo, + Copyright: ProductCopyright, + }, + Content: EmailBodyContent{ + RecipientName: "{{.Username}}", + Intros: intros, + Actions: []hermes.Action{ + { + Instructions: "請複製您的驗證碼,到網頁重置", + InviteCode: "{{.VerifyCode}}", + }, + }, + Outros: []string{ + "如果您沒有請求此操作,請忽略此郵件。", + }, + Signature: "", + }, + } + + emailBody, err := GenerateEmailBody(req) + if err != nil { + return EmailTemplate{}, err + } + + return EmailTemplate{ + Title: title, + Body: emailBody, + }, nil +} + +// ============================== +// Email 產生函數 +// ============================== + +// GenerateForgetPasswordEmailZHTW 生成繁體中文的忘記密碼驗證信 +func GenerateForgetPasswordEmailZHTW() (EmailTemplate, error) { + return GenerateEmailContent("TrueHeart 重設密碼驗證信", + []string{"您收到此電子郵件是因為我們收到了針對帳戶的密碼重置請求。"}) +} + +// GenerateBindingEmailZHTW 生成綁定帳號驗證信 +func GenerateBindingEmailZHTW() (EmailTemplate, error) { + return GenerateEmailContent("TrueHeart 綁定信箱驗證信", + []string{"您收到此電子郵件是因為我們收到了針對帳戶的 Email 認證請求。"}) +} + +// ============================== +// Email 模板對應表 +// ============================== + +var EmailTemplateMap = map[Language]map[Type]func() (EmailTemplate, error){ + LanguageZhTW: { + ForgetPasswordVerify: GenerateForgetPasswordEmailZHTW, + BindingEmail: GenerateBindingEmailZHTW, + }, +} diff --git a/pkg/notification/domain/template/language.go b/pkg/notification/domain/template/language.go new file mode 100644 index 0000000..2be8fcb --- /dev/null +++ b/pkg/notification/domain/template/language.go @@ -0,0 +1,11 @@ +package template + +// ============================== +// 語言與驗證碼類型 +// ============================== + +type Language string + +const ( + LanguageZhTW Language = "zh-tw" +) diff --git a/pkg/notification/domain/template/type.go b/pkg/notification/domain/template/type.go new file mode 100644 index 0000000..ea74771 --- /dev/null +++ b/pkg/notification/domain/template/type.go @@ -0,0 +1,9 @@ +package template + +type Type string + +const ( + BindingEmail Type = "binding_email" // 驗證碼:綁定 Email + BindingPhone Type = "binding_phone" // 驗證碼:綁定 手機 + ForgetPasswordVerify Type = "forget_password" // 驗證碼: 忘記密碼 +) diff --git a/pkg/notification/domain/usecase/delivary.go b/pkg/notification/domain/usecase/delivary.go new file mode 100644 index 0000000..c4d07a7 --- /dev/null +++ b/pkg/notification/domain/usecase/delivary.go @@ -0,0 +1,43 @@ +package usecase + +import ( + "backend/pkg/notification/domain/repository" + "context" +) + +type DeliveryUseCase interface { + SendMessage(ctx context.Context, req SMSMessageRequest) error + SendEmail(ctx context.Context, req MailReq) error +} + +type MailReq struct { + To []string + From string + Subject string + Body string +} + +type SMSMessageRequest struct { + PhoneNumber string `json:"phone_number" validate:"required,e164"` // 接收者號碼 (e164 格式用於驗證國際號碼) + RecipientName string `json:"recipient_name" validate:"required"` // 接收者姓名 + MessageContent string `json:"message_content" validate:"required"` // 要傳送的訊息 +} + +type EmailTemplateResp struct { + Subject string `json:"subject"` // 郵件主題 + Body string `json:"body"` // 郵件內容 +} + +type SMSTemplateResp struct { + Body string `json:"body"` +} + +type SMSProvider struct { + Sort int64 + Repo repository.SMSClientRepository +} + +type EmailProvider struct { + Sort int64 + Repo repository.MailRepository +} diff --git a/pkg/notification/domain/usecase/template.go b/pkg/notification/domain/usecase/template.go new file mode 100644 index 0000000..a896ed7 --- /dev/null +++ b/pkg/notification/domain/usecase/template.go @@ -0,0 +1,10 @@ +package usecase + +import ( + "backend/pkg/notification/domain/template" + "context" +) + +type TemplateUseCase interface { + GetEmailTemplateByStatic(ctx context.Context, language template.Language, templateID template.Type) (template.EmailTemplate, error) +} diff --git a/pkg/notification/repository/aws_ses_mailer.go b/pkg/notification/repository/aws_ses_mailer.go new file mode 100644 index 0000000..d6782bc --- /dev/null +++ b/pkg/notification/repository/aws_ses_mailer.go @@ -0,0 +1,110 @@ +package repository + +import ( + "backend/pkg/notification/config" + "backend/pkg/notification/domain" + "backend/pkg/notification/domain/repository" + "context" + "time" + + "backend/pkg/library/errs/code" + pool "backend/pkg/library/worker_pool" + + "github.com/aws/aws-sdk-go-v2/credentials" + "github.com/aws/aws-sdk-go-v2/service/ses/types" + "github.com/zeromicro/go-zero/core/logx" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ses" +) + +// AwsEmailDeliveryParam 傳送參數配置 +type AwsEmailDeliveryParam struct { + Conf *config.AmazonSesSettings +} + +type AwsEmailDeliveryRepository struct { + Client *ses.Client + Pool pool.WorkerPool +} + +func MustAwsSesMailRepository(param AwsEmailDeliveryParam) repository.MailRepository { + // 手動指定 AWS 配置,不使用默認配置 + cfg := aws.Config{ + Region: param.Conf.Region, // 自定義的 AWS 區域 + Credentials: credentials.NewStaticCredentialsProvider( + param.Conf.AccessKey, // AWS Access Key + param.Conf.SecretKey, // AWS Secret Key + "", + ), + } + // 創建 SES 客戶端 + sesClient := ses.NewFromConfig(cfg) + + return &AwsEmailDeliveryRepository{ + Client: sesClient, + Pool: pool.NewWorkerPool(param.Conf.PoolSize), + } +} + +func (use *AwsEmailDeliveryRepository) SendMail(ctx context.Context, req repository.MailReq) error { + err := use.Pool.Submit(func() { + // 設置郵件參數 + to := make([]string, 0, len(req.To)) + to = append(to, req.To...) + + input := &ses.SendEmailInput{ + Destination: &types.Destination{ + ToAddresses: to, + }, + Message: &types.Message{ + Body: &types.Body{ + Html: &types.Content{ + Charset: aws.String("UTF-8"), + Data: aws.String(req.Body), + }, + }, + Subject: &types.Content{ + Charset: aws.String("UTF-8"), + Data: aws.String(req.Subject), + }, + }, + Source: aws.String(req.From), + } + + // 發送郵件 + // TODO 不明原因送不出去,會被 context cancel 這裡先把它手動加到100sec + newCtx, cancel := context.WithTimeout(context.Background(), 100*time.Second) + defer cancel() + + //nolint:contextcheck + if _, err := use.Client.SendEmail(newCtx, input); err != nil { + _ = domain.ThirdPartyErrorL( + code.CloudEPNotification, + domain.FailedToSendEmailErrorCode, + logx.WithContext(ctx), + []logx.LogField{ + {Key: "req", Value: req}, + {Key: "func", Value: "AwsEmailDeliveryU.SendEmail"}, + {Key: "err", Value: err.Error()}, + }, + "failed to send mail by aws ses") + } + }) + if err != nil { + e := domain.ThirdPartyErrorL( + code.CloudEPNotification, + domain.FailedToSendEmailErrorCode, + logx.WithContext(ctx), + []logx.LogField{ + {Key: "req", Value: req}, + {Key: "func", Value: "AwsEmailDeliveryU.SendEmail"}, + {Key: "err", Value: err.Error()}, + }, + "failed to send mail by aws ses") + + return e + } + + return nil +} diff --git a/pkg/notification/repository/mitake_sms_sender.go b/pkg/notification/repository/mitake_sms_sender.go new file mode 100644 index 0000000..1a36127 --- /dev/null +++ b/pkg/notification/repository/mitake_sms_sender.go @@ -0,0 +1,64 @@ +package repository + +import ( + "backend/pkg/notification/config" + "backend/pkg/notification/domain" + "backend/pkg/notification/domain/repository" + "context" + + "backend/pkg/library/errs/code" + pool "backend/pkg/library/worker_pool" + + "github.com/minchao/go-mitake" + "github.com/zeromicro/go-zero/core/logx" +) + +// MitakeSMSDeliveryParam 三竹傳送參數配置 +type MitakeSMSDeliveryParam struct { + Conf *config.MitakeSMSSender +} + +type MitakeSMSDeliveryRepository struct { + Client *mitake.Client + Pool pool.WorkerPool +} + +func (use *MitakeSMSDeliveryRepository) SendSMS(ctx context.Context, req repository.SMSMessageRequest) error { + // 用 goroutine pool 送,否則會超時 + err := use.Pool.Submit(func() { + message := mitake.Message{ + Dstaddr: req.PhoneNumber, + Destname: req.RecipientName, + Smbody: req.MessageContent, + } + _, err := use.Client.Send(message) + if err != nil { + logx.Error("failed to send sms via mitake") + } + }) + + if err != nil { + // 錯誤代碼 20-201-04 + e := domain.ThirdPartyErrorL( + code.CloudEPNotification, + domain.FailedToSendSMSErrorCode, + logx.WithContext(ctx), + []logx.LogField{ + {Key: "req", Value: req}, + {Key: "func", Value: "MitakeSMSDeliveryRepository.Client.Send"}, + {Key: "err", Value: err.Error()}, + }, + "failed to send sns by mitake").Wrap(err) + + return e + } + + return nil +} + +func MustMitakeRepository(param MitakeSMSDeliveryParam) repository.SMSClientRepository { + return &MitakeSMSDeliveryRepository{ + Client: mitake.NewClient(param.Conf.User, param.Conf.Password, nil), + Pool: pool.NewWorkerPool(param.Conf.PoolSize), + } +} diff --git a/pkg/notification/repository/smtp_mailer.go b/pkg/notification/repository/smtp_mailer.go new file mode 100644 index 0000000..2e778d6 --- /dev/null +++ b/pkg/notification/repository/smtp_mailer.go @@ -0,0 +1,53 @@ +package repository + +import ( + "backend/pkg/notification/config" + "backend/pkg/notification/domain/repository" + "context" + + pool "backend/pkg/library/worker_pool" + + "github.com/zeromicro/go-zero/core/logx" + "gopkg.in/gomail.v2" +) + +type SMTPMailUseCaseParam struct { + Conf config.SMTPConfig +} + +type SMTPMailRepository struct { + Client *gomail.Dialer + Pool pool.WorkerPool +} + +func MustSMTPUseCase(param SMTPMailUseCaseParam) repository.MailRepository { + return &SMTPMailRepository{ + Client: gomail.NewDialer( + param.Conf.Host, + param.Conf.Port, + param.Conf.Username, + param.Conf.Password, + ), + Pool: pool.NewWorkerPool(param.Conf.GoroutinePoolNum), + } +} + +func (repo *SMTPMailRepository) SendMail(_ context.Context, req repository.MailReq) error { + // 用 goroutine pool 送,否則會超時 + err := repo.Pool.Submit(func() { + m := gomail.NewMessage() + m.SetHeader("From", req.From) + m.SetHeader("To", req.To...) + m.SetHeader("Subject", req.Subject) + m.SetBody("text/html", req.Body) + if err := repo.Client.DialAndSend(m); err != nil { + logx.WithCallerSkip(1).WithFields( + logx.Field("func", "MailUseCase.SendMail"), + logx.Field("req", req), + logx.Field("err", err), + ).Error("failed to send mail by mailgun") + } + }) + + return err +} diff --git a/pkg/notification/usecase/delivery.go b/pkg/notification/usecase/delivery.go new file mode 100644 index 0000000..f48115a --- /dev/null +++ b/pkg/notification/usecase/delivery.go @@ -0,0 +1,75 @@ +package usecase + +import ( + "backend/pkg/notification/domain/repository" + "backend/pkg/notification/domain/usecase" + "context" + "sort" + "time" +) + +// DeliveryUseCaseParam 傳送參數配置 +type DeliveryUseCaseParam struct { + SMSProviders []usecase.SMSProvider + EmailProviders []usecase.EmailProvider +} + +// DeliveryUseCase 通知 +type DeliveryUseCase struct { + param DeliveryUseCaseParam +} + +func MustDeliveryUseCase(param DeliveryUseCaseParam) usecase.DeliveryUseCase { + return &DeliveryUseCase{ + param: param, + } +} + +func (use *DeliveryUseCase) SendMessage(ctx context.Context, req usecase.SMSMessageRequest) error { + var err error + // 根據 Sort 欄位對 SMSProviders 進行排序 + sort.Slice(use.param.SMSProviders, func(i, j int) bool { + return use.param.SMSProviders[i].Sort < use.param.SMSProviders[j].Sort + }) + + newCtx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + + // 依序嘗試發送 + for _, provider := range use.param.SMSProviders { + if err = provider.Repo.SendSMS(newCtx, repository.SMSMessageRequest{ + PhoneNumber: req.PhoneNumber, + RecipientName: req.RecipientName, + MessageContent: req.MessageContent, + }); err == nil { + return nil // 發送成功 + } + } + + return err +} + +func (use *DeliveryUseCase) SendEmail(ctx context.Context, req usecase.MailReq) error { + var err error + // 根據 Sort 欄位對 SMSProviders 進行排序 + sort.Slice(use.param.EmailProviders, func(i, j int) bool { + return use.param.EmailProviders[i].Sort < use.param.EmailProviders[j].Sort + }) + + newCtx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + + // 依序嘗試發送 dreq + for _, provider := range use.param.EmailProviders { + if err = provider.Repo.SendMail(newCtx, repository.MailReq{ + From: req.From, + To: req.To, + Subject: req.Subject, + Body: req.Body, + }); err == nil { + return nil // 發送成功 + } + } + + return err +} diff --git a/pkg/notification/usecase/template.go b/pkg/notification/usecase/template.go new file mode 100644 index 0000000..e980748 --- /dev/null +++ b/pkg/notification/usecase/template.go @@ -0,0 +1,43 @@ +package usecase + +import ( + "backend/pkg/notification/domain/template" + "backend/pkg/notification/domain/usecase" + "context" + "fmt" +) + +type TemplateUseCaseParam struct{} + +type TemplateUseCase struct { + TemplateUseCaseParam +} + +func MustTemplateUseCase(param TemplateUseCaseParam) usecase.TemplateUseCase { + return &TemplateUseCase{ + param, + } +} + +func (use *TemplateUseCase) GetEmailTemplateByStatic(_ context.Context, language template.Language, templateID template.Type) (template.EmailTemplate, error) { + // 查找指定語言的模板映射 + templateByLang, exists := template.EmailTemplateMap[language] + if !exists { + return template.EmailTemplate{}, fmt.Errorf("email template not found for language: %s", language) + } + + // 查找指定類型的模板生成函數 + templateFunc, exists := templateByLang[templateID] + if !exists { + return template.EmailTemplate{}, fmt.Errorf("email template not found for type ID: %s", templateID) + } + + // 執行模板生成函數 + tmp, err := templateFunc() + if err != nil { + return template.EmailTemplate{}, fmt.Errorf("error generating email template: %w", err) + } + + // 返回構建好的響應 + return tmp, nil +}