From 781a71a27ca0fd2c61505371b7b696d2b7f2b120 Mon Sep 17 00:00:00 2001 From: landaiqing Date: Sat, 22 Mar 2025 13:05:50 +0800 Subject: [PATCH] :construction: improve image encryption and decryption --- app/auth/api/auth.api | 68 ++ app/auth/api/etc/auth.yaml | 2 + app/auth/api/etc/common_rsa_private_key.pem | 27 + app/auth/api/etc/common_rsa_public_key.pem | 9 + app/auth/api/internal/config/config.go | 4 +- .../captcha/generate_text_captcha_handler.go | 21 + app/auth/api/internal/handler/routes.go | 34 + .../storage/get_private_image_url_handler.go | 29 + .../handler/system/get_user_list_handler.go | 21 + .../handler/user/admin_login_handler.go | 29 + .../captcha/generate_text_captcha_logic.go | 39 + .../logic/share/delete_share_record_logic.go | 4 +- .../logic/share/query_share_image_logic.go | 12 +- .../logic/share/upload_share_image_logic.go | 5 +- .../storage/get_coordinate_list_logic.go | 7 +- .../storage/get_private_image_list_logic.go | 129 +--- .../storage/get_private_image_url_logic.go | 218 ++++++ .../logic/storage/upload_file_logic.go | 50 +- .../logic/system/get_user_list_logic.go | 30 + .../internal/logic/user/admin_login_logic.go | 59 ++ .../internal/logic/user/phone_login_logic.go | 6 +- .../user/wechat_offiaccount_login_logic.go | 7 +- app/auth/api/internal/svc/service_context.go | 9 +- app/auth/api/internal/types/types.go | 45 ++ app/auth/resources/language/active.en.toml | 2 +- app/auth/resources/language/active.zh.toml | 1 + .../generate/generate_basic_text_captcha.go | 2 +- common/captcha/initialize/text_captcha.go | 47 +- common/constant/redis_prefix.go | 1 - common/constant/user_role.go | 1 - common/hybrid_encrypt/README.md | 82 +++ common/hybrid_encrypt/hybrid_encrypt.go | 180 +++++ common/hybrid_encrypt/hybrid_encrypt_js.js | 148 ++++ common/hybrid_encrypt/image_encrypt.go | 46 ++ common/hybrid_encrypt/image_encrypt_test.go | 28 + common/utils/encrypt_test.go | 11 + common/utils/generate_avatar.go | 683 ------------------ go.mod | 20 +- go.sum | 135 +--- 39 files changed, 1274 insertions(+), 977 deletions(-) create mode 100644 app/auth/api/etc/common_rsa_private_key.pem create mode 100644 app/auth/api/etc/common_rsa_public_key.pem create mode 100644 app/auth/api/internal/handler/captcha/generate_text_captcha_handler.go create mode 100644 app/auth/api/internal/handler/storage/get_private_image_url_handler.go create mode 100644 app/auth/api/internal/handler/system/get_user_list_handler.go create mode 100644 app/auth/api/internal/handler/user/admin_login_handler.go create mode 100644 app/auth/api/internal/logic/captcha/generate_text_captcha_logic.go create mode 100644 app/auth/api/internal/logic/storage/get_private_image_url_logic.go create mode 100644 app/auth/api/internal/logic/system/get_user_list_logic.go create mode 100644 app/auth/api/internal/logic/user/admin_login_logic.go create mode 100644 common/hybrid_encrypt/README.md create mode 100644 common/hybrid_encrypt/hybrid_encrypt.go create mode 100644 common/hybrid_encrypt/hybrid_encrypt_js.js create mode 100644 common/hybrid_encrypt/image_encrypt.go create mode 100644 common/hybrid_encrypt/image_encrypt_test.go create mode 100644 common/utils/encrypt_test.go delete mode 100644 common/utils/generate_avatar.go diff --git a/app/auth/api/auth.api b/app/auth/api/auth.api index 47003cc..10b993e 100644 --- a/app/auth/api/auth.api +++ b/app/auth/api/auth.api @@ -45,6 +45,12 @@ type ( Avatar string `json:"avatar"` Status int64 `json:"status"` } + AdminLoginRequest { + Account string `json:"account"` + Password string `json:"password"` + Dots string `json:"dots"` + Key string `json:"key"` + } ) // OAuth请求参数 @@ -95,6 +101,11 @@ type ( ThumbX int64 `json:"thumb_x"` ThumbY int64 `json:"thumb_y"` } + TextCaptchaResponse { + Key string `json:"key"` + Image string `json:"image"` + Thumb string `json:"thumb"` + } ) // 用户服务 @@ -128,6 +139,10 @@ service auth { // 获取微信公众号二维码 @handler getWechatOffiaccountQrcode post /wechat/offiaccount/qrcode (OAuthWechatRequest) returns (string) + + // 管理员登录 + @handler adminLogin + post /admin/login (AdminLoginRequest) returns (LoginResponse) } @server ( @@ -250,6 +265,10 @@ service auth { @handler generateSlideBasicCaptcha get /slide/generate returns (SlideCaptchaResponse) + + // 文字点选验证码 + @handler generateTextCaptcha + get /text/generate returns (TextCaptchaResponse) } type ( @@ -731,12 +750,23 @@ type ( Provider string `json:"provider"` Bucket string `json:"bucket"` Password string `json:"password"` + Dots string `json:"dots"` + Key string `json:"key"` + } + SinglePrivateImageRequest { + ID int64 `json:"id"` + Password string `json:"password"` + Provider string `json:"provider"` + Bucket string `json:"bucket"` } CoordinateMeta { ID int64 `json:"id"` Latitude float64 `json:"latitude"` Longitude float64 `json:"longitude"` ImageCount int64 `json:"image_count"` + Country string `json:"country"` + Province string `json:"province"` + City string `json:"city"` } CoordinateListResponse { Records []CoordinateMeta `json:"records"` @@ -884,6 +914,10 @@ service auth { @handler getPrivateImageList post /image/private/list (PrivateImageListRequest) returns (AllImageListResponse) + // 获取解密单个隐私加密图片 + @handler getPrivateImageUrl + post /image/private/url/single (SinglePrivateImageRequest) returns (string) + // 获取图像经纬度列表 @handler getCoordinateList post /coordinate/list returns (CoordinateListResponse) @@ -1037,3 +1071,37 @@ service auth { post /logout returns (string) } +type ( + UserMeta { + ID int64 `json:"id"` + Username string `json:"username"` + Nickname string `json:"nickname"` + Avatar string `json:"avatar"` + Email string `json:"email"` + Phone string `json:"phone"` + Status int64 `json:"status"` + CreatedAt string `json:"created_at"` + } + UserInfoListResponse { + Records []UserMeta `json:"records"` + } +) + +// 系统服务 +@server ( + group: system // 微服务分组 + prefix: /api/auth/system // 微服务前缀 + timeout: 10s // 超时时间 + maxBytes: 10485760 // 最大请求大小 + signature: true // 是否开启签名验证 + middleware: SecurityHeadersMiddleware,CasbinVerifyMiddleware,NonceMiddleware,AuthMiddleware // 注册中间件 + MaxConns: true // 是否开启最大连接数限制 + Recover: true // 是否开启自动恢复 + jwt: Auth // 是否开启jwt验证 +) +service auth { + // 获取用户列表 + @handler getUserList + post /user/list returns (UserInfoListResponse) +} + diff --git a/app/auth/api/etc/auth.yaml b/app/auth/api/etc/auth.yaml index 054fa05..b24b176 100644 --- a/app/auth/api/etc/auth.yaml +++ b/app/auth/api/etc/auth.yaml @@ -90,6 +90,8 @@ Signature: Encrypt: # 密钥(32) Key: p3380puliiep184buh8d5dvujeerqtem + PublicKey: api/etc/common_rsa_public_key.pem + PrivateKey: api/etc/common_rsa_private_key.pem # Redis 配置 Redis: # Redis 地址 diff --git a/app/auth/api/etc/common_rsa_private_key.pem b/app/auth/api/etc/common_rsa_private_key.pem new file mode 100644 index 0000000..2dad27a --- /dev/null +++ b/app/auth/api/etc/common_rsa_private_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAseOyY9ETmhkaSfUj9LqvU9/+Jo81a+pKGgKGBPB0If+YkCH0 +Frw1mCtZoCsidZeEMXtSaS1U/lbmKs2L6vExcZr8uSAGb2NXuQn+y8wPnEjAvsU7 +KaP/cUK11R/6JRHQes5izliNQfdUdnlSuN0X9PFoozuaELb0+SUTTLKkd1mnDk7x +3bMN6CNNsTg/zT5hYeuGkunyr5QA4YpofnBC8YID6YlD/mw7x8S4JRPFCmq58Hgq ++C2eW716PibyMbCftWMVASvy/OgxnRwvJVho/Br14YiDl3qQWiUPxUYcE1mUrW5t +qjARKc0OX5Owid+/ydwtxg8hi/yTskUfjUnXtQIDAQABAoIBACD3MkrfJwPKnR2R +iT1ED1O60c1xgpPiEiNpzk5CBTN7u1kSgbpo3IG7nttYwwUJtBy7XtVQ6kxL7FGI +T+KVGfWUpDrmXWrs/Qe0e3xm74mlzdpMkJ8x3heuJiY9y8xs1ba8YoEc1eigng1q +hFLv3g2tYxfE5tMsJI+7OC1heasIKd88ZO170/mLpDzaKFI0+tUlRCyOIfP8ltCE +Bm+1jDeVeWI3HTWESbEX8+eCA/av/h0/JBmtvqZpK2ZYLQMBv9t0ekVqSmm6T9Mj +cy/S9Lb7qXoW/2m4QFkD9ph9RY5HJWNZcEKwJNs3ecJJiMD52aGA9obqH9GH5SBj +yDq42eECgYEAxTlYngXh3cQ81/KcZxJE9ZoFnmrKeqMX7g4OUyTzLDhUk7K+oGZI +wn+K31GpDgC5fpjLsvpKVsnjOjluTLcnRsJ0OUyITHNQo4Z/FzgxwNLnsM99+h3E +/75/3xqwnoCWuRSvZHKD6Zk/uBu1UV8rsCJRwUYvzIAVwZxd0Aabjm0CgYEA5udG +ILV20HTZq3auHj1x16idhB8Nemg1GyD+hNxzVLZ8Zkrj3HjjukJv1FOOOPkKhmtl +3nrYX/8gwxh7HEC+coi7WQqbbvlLIiKaAlYJB7DKip02n3dlzUmp9kpB5EcjJuvX +hDrN/s/i6q9M3XWP2zl0oLwkv/GJzs/3cZ7mAWkCgYALcpSuN3Ewyh8t+asSYIEY +MGR7GX+/NpBBBRfXw6FJw8tE928RKF64y2ZoJ/lEEs6xhnTsYpLGDtndm0/HrCnf +dZIBcWvH5DmeBESEOILKynMgVCrfxbKVlZ0eehIeYSBehdDYZ704ZejI6vLPUlLa +2mMccNJ9cEHTBxx64qdM0QKBgBELpauoebrtxVvZCQWGd6757ZbhS/drVfBIwUFB +nOn2Brzubl/KNNV9LhA4ktk12UcPCpgf7XU4ukxstDnjtaty2JG8LLlGgftlHoVp +oIUG0gzlijC/ea5r77YUyUR20+t9oY1LYgWbhx7YDg6TLSl71lY/TV82D3xK8fNb +TZNxAoGAZQlW9+UMOtptfzOh4HLeEs9y2/KvlfUYClgcKyz8FuGeuOMyf8bgFK8L +OzGNkEFOgU1CQ9j6de3Id996qK1sGiZwyBVCEXqnnLLUk3yUGbO32wVXniJSrrlT +0bekyTJEjnol2E4Rv3WrX7ddGunU2R7p35HkXoWBXHGfwd7F3eI= +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/app/auth/api/etc/common_rsa_public_key.pem b/app/auth/api/etc/common_rsa_public_key.pem new file mode 100644 index 0000000..84e8a7c --- /dev/null +++ b/app/auth/api/etc/common_rsa_public_key.pem @@ -0,0 +1,9 @@ +-----BEGIN RSA PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAseOyY9ETmhkaSfUj9Lqv +U9/+Jo81a+pKGgKGBPB0If+YkCH0Frw1mCtZoCsidZeEMXtSaS1U/lbmKs2L6vEx +cZr8uSAGb2NXuQn+y8wPnEjAvsU7KaP/cUK11R/6JRHQes5izliNQfdUdnlSuN0X +9PFoozuaELb0+SUTTLKkd1mnDk7x3bMN6CNNsTg/zT5hYeuGkunyr5QA4YpofnBC +8YID6YlD/mw7x8S4JRPFCmq58Hgq+C2eW716PibyMbCftWMVASvy/OgxnRwvJVho +/Br14YiDl3qQWiUPxUYcE1mUrW5tqjARKc0OX5Owid+/ydwtxg8hi/yTskUfjUnX +tQIDAQAB +-----END RSA PUBLIC KEY----- \ No newline at end of file diff --git a/app/auth/api/internal/config/config.go b/app/auth/api/internal/config/config.go index 6b65e11..3afd5f6 100644 --- a/app/auth/api/internal/config/config.go +++ b/app/auth/api/internal/config/config.go @@ -15,7 +15,9 @@ type Config struct { AccessSecret string } Encrypt struct { - Key string + Key string + PublicKey string + PrivateKey string } Mysql struct { DataSource string diff --git a/app/auth/api/internal/handler/captcha/generate_text_captcha_handler.go b/app/auth/api/internal/handler/captcha/generate_text_captcha_handler.go new file mode 100644 index 0000000..46a62de --- /dev/null +++ b/app/auth/api/internal/handler/captcha/generate_text_captcha_handler.go @@ -0,0 +1,21 @@ +package captcha + +import ( + "net/http" + + "schisandra-album-cloud-microservices/app/auth/api/internal/logic/captcha" + "schisandra-album-cloud-microservices/app/auth/api/internal/svc" + "schisandra-album-cloud-microservices/common/xhttp" +) + +func GenerateTextCaptchaHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + l := captcha.NewGenerateTextCaptchaLogic(r.Context(), svcCtx) + resp, err := l.GenerateTextCaptcha() + if err != nil { + xhttp.JsonBaseResponseCtx(r.Context(), w, err) + } else { + xhttp.JsonBaseResponseCtx(r.Context(), w, resp) + } + } +} diff --git a/app/auth/api/internal/handler/routes.go b/app/auth/api/internal/handler/routes.go index 9cb1514..81ce484 100644 --- a/app/auth/api/internal/handler/routes.go +++ b/app/auth/api/internal/handler/routes.go @@ -16,6 +16,7 @@ import ( share "schisandra-album-cloud-microservices/app/auth/api/internal/handler/share" sms "schisandra-album-cloud-microservices/app/auth/api/internal/handler/sms" storage "schisandra-album-cloud-microservices/app/auth/api/internal/handler/storage" + system "schisandra-album-cloud-microservices/app/auth/api/internal/handler/system" token "schisandra-album-cloud-microservices/app/auth/api/internal/handler/token" user "schisandra-album-cloud-microservices/app/auth/api/internal/handler/user" websocket "schisandra-album-cloud-microservices/app/auth/api/internal/handler/websocket" @@ -61,6 +62,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/slide/generate", Handler: captcha.GenerateSlideBasicCaptchaHandler(serverCtx), }, + { + Method: http.MethodGet, + Path: "/text/generate", + Handler: captcha.GenerateTextCaptchaHandler(serverCtx), + }, }..., ), rest.WithPrefix("/api/captcha"), @@ -395,6 +401,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/image/private/list", Handler: storage.GetPrivateImageListHandler(serverCtx), }, + { + Method: http.MethodPost, + Path: "/image/private/url/single", + Handler: storage.GetPrivateImageUrlHandler(serverCtx), + }, { Method: http.MethodPost, Path: "/image/recent/list", @@ -453,6 +464,24 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { rest.WithMaxBytes(104857600), ) + server.AddRoutes( + rest.WithMiddlewares( + []rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.CasbinVerifyMiddleware, serverCtx.NonceMiddleware, serverCtx.AuthMiddleware}, + []rest.Route{ + { + Method: http.MethodPost, + Path: "/user/list", + Handler: system.GetUserListHandler(serverCtx), + }, + }..., + ), + rest.WithJwt(serverCtx.Config.Auth.AccessSecret), + rest.WithSignature(serverCtx.Config.Signature), + rest.WithPrefix("/api/auth/system"), + rest.WithTimeout(10000*time.Millisecond), + rest.WithMaxBytes(10485760), + ) + server.AddRoutes( rest.WithMiddlewares( []rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.CasbinVerifyMiddleware, serverCtx.NonceMiddleware}, @@ -474,6 +503,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { rest.WithMiddlewares( []rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.NonceMiddleware}, []rest.Route{ + { + Method: http.MethodPost, + Path: "/admin/login", + Handler: user.AdminLoginHandler(serverCtx), + }, { Method: http.MethodPost, Path: "/login", diff --git a/app/auth/api/internal/handler/storage/get_private_image_url_handler.go b/app/auth/api/internal/handler/storage/get_private_image_url_handler.go new file mode 100644 index 0000000..3e84b4d --- /dev/null +++ b/app/auth/api/internal/handler/storage/get_private_image_url_handler.go @@ -0,0 +1,29 @@ +package storage + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "schisandra-album-cloud-microservices/app/auth/api/internal/logic/storage" + "schisandra-album-cloud-microservices/app/auth/api/internal/svc" + "schisandra-album-cloud-microservices/app/auth/api/internal/types" + "schisandra-album-cloud-microservices/common/xhttp" +) + +func GetPrivateImageUrlHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.SinglePrivateImageRequest + if err := httpx.Parse(r, &req); err != nil { + xhttp.JsonBaseResponseCtx(r.Context(), w, err) + return + } + + l := storage.NewGetPrivateImageUrlLogic(r.Context(), svcCtx) + resp, err := l.GetPrivateImageUrl(&req) + if err != nil { + xhttp.JsonBaseResponseCtx(r.Context(), w, err) + } else { + xhttp.JsonBaseResponseCtx(r.Context(), w, resp) + } + } +} diff --git a/app/auth/api/internal/handler/system/get_user_list_handler.go b/app/auth/api/internal/handler/system/get_user_list_handler.go new file mode 100644 index 0000000..556fc9a --- /dev/null +++ b/app/auth/api/internal/handler/system/get_user_list_handler.go @@ -0,0 +1,21 @@ +package system + +import ( + "net/http" + + "schisandra-album-cloud-microservices/app/auth/api/internal/logic/system" + "schisandra-album-cloud-microservices/app/auth/api/internal/svc" + "schisandra-album-cloud-microservices/common/xhttp" +) + +func GetUserListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + l := system.NewGetUserListLogic(r.Context(), svcCtx) + resp, err := l.GetUserList() + if err != nil { + xhttp.JsonBaseResponseCtx(r.Context(), w, err) + } else { + xhttp.JsonBaseResponseCtx(r.Context(), w, resp) + } + } +} diff --git a/app/auth/api/internal/handler/user/admin_login_handler.go b/app/auth/api/internal/handler/user/admin_login_handler.go new file mode 100644 index 0000000..ad9427c --- /dev/null +++ b/app/auth/api/internal/handler/user/admin_login_handler.go @@ -0,0 +1,29 @@ +package user + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "schisandra-album-cloud-microservices/app/auth/api/internal/logic/user" + "schisandra-album-cloud-microservices/app/auth/api/internal/svc" + "schisandra-album-cloud-microservices/app/auth/api/internal/types" + "schisandra-album-cloud-microservices/common/xhttp" +) + +func AdminLoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.AdminLoginRequest + if err := httpx.Parse(r, &req); err != nil { + xhttp.JsonBaseResponseCtx(r.Context(), w, err) + return + } + + l := user.NewAdminLoginLogic(r.Context(), svcCtx) + resp, err := l.AdminLogin(r, &req) + if err != nil { + xhttp.JsonBaseResponseCtx(r.Context(), w, err) + } else { + xhttp.JsonBaseResponseCtx(r.Context(), w, resp) + } + } +} diff --git a/app/auth/api/internal/logic/captcha/generate_text_captcha_logic.go b/app/auth/api/internal/logic/captcha/generate_text_captcha_logic.go new file mode 100644 index 0000000..bc6ba63 --- /dev/null +++ b/app/auth/api/internal/logic/captcha/generate_text_captcha_logic.go @@ -0,0 +1,39 @@ +package captcha + +import ( + "context" + "net/http" + "schisandra-album-cloud-microservices/common/captcha/generate" + "schisandra-album-cloud-microservices/common/errors" + + "schisandra-album-cloud-microservices/app/auth/api/internal/svc" + "schisandra-album-cloud-microservices/app/auth/api/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type GenerateTextCaptchaLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewGenerateTextCaptchaLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GenerateTextCaptchaLogic { + return &GenerateTextCaptchaLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *GenerateTextCaptchaLogic) GenerateTextCaptcha() (resp *types.TextCaptchaResponse, err error) { + captcha, err := generate.GenerateBasicTextCaptcha(l.svcCtx.TextCaptcha, l.svcCtx.RedisClient, l.ctx) + if err != nil { + return nil, errors.New(http.StatusInternalServerError, err.Error()) + } + return &types.TextCaptchaResponse{ + Key: captcha["key"].(string), + Image: captcha["image"].(string), + Thumb: captcha["thumb"].(string), + }, nil +} diff --git a/app/auth/api/internal/logic/share/delete_share_record_logic.go b/app/auth/api/internal/logic/share/delete_share_record_logic.go index 010a873..1ff1c52 100644 --- a/app/auth/api/internal/logic/share/delete_share_record_logic.go +++ b/app/auth/api/internal/logic/share/delete_share_record_logic.go @@ -49,8 +49,8 @@ func (l *DeleteShareRecordLogic) DeleteShareRecord(req *types.DeleteShareRecordR return "", errors.New("delete share record failed") } shareVisit := tx.ScaStorageShareVisit - shareVisitDeleted, err := shareVisit.Where(shareVisit.ShareID.Eq(req.ID), shareVisit.UserID.Eq(uid)).Delete() - if err != nil || shareVisitDeleted.RowsAffected == 0 { + _, err = shareVisit.Where(shareVisit.ShareID.Eq(req.ID), shareVisit.UserID.Eq(uid)).Delete() + if err != nil { tx.Rollback() return "", errors.New("delete share visit record failed") } diff --git a/app/auth/api/internal/logic/share/query_share_image_logic.go b/app/auth/api/internal/logic/share/query_share_image_logic.go index f94aac2..7d3d845 100644 --- a/app/auth/api/internal/logic/share/query_share_image_logic.go +++ b/app/auth/api/internal/logic/share/query_share_image_logic.go @@ -54,23 +54,25 @@ func (l *QueryShareImageLogic) QueryShareImage(req *types.QueryShareImageRequest shareData, err := l.svcCtx.RedisClient.Get(l.ctx, cacheKey).Result() if err != nil { if errors.Is(err, redis.Nil) { - return nil, errors.New("share code not found") + return nil, errors.New("没有找到分享记录") } return nil, err } var storageShare model.ScaStorageShare if err := json.Unmarshal([]byte(shareData), &storageShare); err != nil { - return nil, errors.New("unmarshal share data failed") + return nil, errors.New("解析分享记录失败") } // 验证密码 if storageShare.AccessPassword != "" && storageShare.AccessPassword != req.AccessPassword { - return nil, errors.New("incorrect password") + return nil, errors.New("密码错误") } // 检查分享是否过期 - if storageShare.ExpireTime.Before(time.Now()) { - return nil, errors.New("share link has expired") + if storageShare.ValidityPeriod > 0 { + if storageShare.ExpireTime.Before(time.Now()) { + return nil, errors.New("分享已过期") + } } // 检查访问限制 diff --git a/app/auth/api/internal/logic/share/upload_share_image_logic.go b/app/auth/api/internal/logic/share/upload_share_image_logic.go index b0d9213..60b4e50 100644 --- a/app/auth/api/internal/logic/share/upload_share_image_logic.go +++ b/app/auth/api/internal/logic/share/upload_share_image_logic.go @@ -87,7 +87,10 @@ func (l *UploadShareImageLogic) UploadShareImage(req *types.ShareImageRequest) ( if err != nil { return "", errors.New("invalid expire date") } - expiryTime := l.GenerateExpiryTime(time.Now(), duration) + var expiryTime time.Time + if duration > 0 { + expiryTime = l.GenerateExpiryTime(time.Now(), duration) + } storageShare := model.ScaStorageShare{ UserID: uid, AlbumID: album.ID, diff --git a/app/auth/api/internal/logic/storage/get_coordinate_list_logic.go b/app/auth/api/internal/logic/storage/get_coordinate_list_logic.go index ad285cf..a128002 100644 --- a/app/auth/api/internal/logic/storage/get_coordinate_list_logic.go +++ b/app/auth/api/internal/logic/storage/get_coordinate_list_logic.go @@ -35,13 +35,18 @@ func (l *GetCoordinateListLogic) GetCoordinateList() (resp *types.CoordinateList storageLocation.ID, storageLocation.Longitude, storageLocation.Latitude, + storageLocation.Country, + storageLocation.Province, + storageLocation.City, storageInfo.ID.Count().As("image_count"), ).Join( storageInfo, storageLocation.ID.EqCol(storageInfo.LocationID), ).Where(storageLocation.UserID.Eq(uid), storageInfo.UserID.Eq(uid), - ).Scan(&records) + ). + Group(storageLocation.ID). + Scan(&records) if err != nil { return nil, err } diff --git a/app/auth/api/internal/logic/storage/get_private_image_list_logic.go b/app/auth/api/internal/logic/storage/get_private_image_list_logic.go index 2a6c101..4de1bcc 100644 --- a/app/auth/api/internal/logic/storage/get_private_image_list_logic.go +++ b/app/auth/api/internal/logic/storage/get_private_image_list_logic.go @@ -1,24 +1,20 @@ package storage import ( - "bytes" "context" - "encoding/base64" "encoding/json" "errors" - "fmt" - "github.com/go-resty/resty/v2" "github.com/redis/go-redis/v9" "golang.org/x/sync/errgroup" "golang.org/x/sync/semaphore" "gorm.io/gen" - "io" - "math/rand" - "net/http" + "gorm.io/gorm" "schisandra-album-cloud-microservices/app/auth/model/mysql/model" + "schisandra-album-cloud-microservices/common/captcha/verify" "schisandra-album-cloud-microservices/common/constant" "schisandra-album-cloud-microservices/common/encrypt" storageConfig "schisandra-album-cloud-microservices/common/storage/config" + "schisandra-album-cloud-microservices/common/utils" "sort" "sync" "time" @@ -31,9 +27,8 @@ import ( type GetPrivateImageListLogic struct { logx.Logger - ctx context.Context - svcCtx *svc.ServiceContext - RestyClient *resty.Client + ctx context.Context + svcCtx *svc.ServiceContext } func NewGetPrivateImageListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPrivateImageListLogic { @@ -41,16 +36,6 @@ func NewGetPrivateImageListLogic(ctx context.Context, svcCtx *svc.ServiceContext Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, - RestyClient: resty.New(). - SetTimeout(30 * time.Second). // 总超时时间 - SetRetryCount(3). // 重试次数 - SetRetryWaitTime(5 * time.Second). // 重试等待时间 - SetRetryMaxWaitTime(30 * time.Second). // 最大重试等待 - AddRetryCondition(func(r *resty.Response, err error) bool { - return r.StatusCode() == http.StatusTooManyRequests || - err != nil || - r.StatusCode() >= 500 - }), } } @@ -59,8 +44,29 @@ func (l *GetPrivateImageListLogic) GetPrivateImageList(req *types.PrivateImageLi if !ok { return nil, errors.New("user_id not found") } + captcha := verify.VerifyBasicTextCaptcha(req.Dots, req.Key, l.svcCtx.RedisClient, l.ctx) + if !captcha { + return nil, errors.New("验证错误") + } + if req.Password == "" { + return nil, errors.New("密码不能为空") + } + authUser := l.svcCtx.DB.ScaAuthUser + userInfo, err := authUser. + Select(authUser.UID, authUser.Password). + Where(authUser.UID.Eq(uid)).First() + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + if userInfo == nil { + return nil, errors.New("密码错误") + } + if !utils.Verify(userInfo.Password, req.Password) { + return nil, errors.New("密码错误") + } storageInfo := l.svcCtx.DB.ScaStorageInfo + storageThumb := l.svcCtx.DB.ScaStorageThumb conditions := []gen.Condition{ storageInfo.UserID.Eq(uid), storageInfo.Provider.Eq(req.Provider), @@ -74,10 +80,16 @@ func (l *GetPrivateImageListLogic) GetPrivateImageList(req *types.PrivateImageLi storageInfo.ID, storageInfo.FileName, storageInfo.CreatedAt, - storageInfo.Path). + storageThumb.ThumbPath, + storageInfo.Path, + storageThumb.ThumbW, + storageThumb.ThumbH, + storageThumb.ThumbSize, + ). + LeftJoin(storageThumb, storageInfo.ID.EqCol(storageThumb.InfoID)). Where(conditions...). Order(storageInfo.CreatedAt.Desc()).Scan(&storageInfoList) - if err != nil { + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return nil, err } if len(storageInfoList) == 0 { @@ -110,48 +122,15 @@ func (l *GetPrivateImageListLogic) GetPrivateImageList(req *types.PrivateImageLi g.Go(func() error { defer sem.Release(1) - // 生成单条缓存键(包含文件唯一标识) - imageCacheKey := fmt.Sprintf("%s%s:%s:%s:%s:%v", - constant.ImageCachePrefix, - uid, - "list", - req.Provider, - req.Bucket, - dbFileInfo.ID) - // 尝试获取单条缓存 - if cached, err := l.svcCtx.RedisClient.Get(l.ctx, imageCacheKey).Result(); err == nil { - var meta types.ImageMeta - if err := json.Unmarshal([]byte(cached), &meta); err == nil { - parse, err := time.Parse("2006-01-02 15:04:05", meta.CreatedAt) - if err == nil { - logx.Error("Parse Time Error:", err) - return nil - } - date := parse.Format("2006年1月2日 星期") + WeekdayMap[parse.Weekday()] - value, _ := groupedImages.LoadOrStore(date, []types.ImageMeta{}) - images := value.([]types.ImageMeta) - images = append(images, meta) - groupedImages.Store(date, images) - return nil - } - } weekday := WeekdayMap[dbFileInfo.CreatedAt.Weekday()] date := dbFileInfo.CreatedAt.Format("2006年1月2日 星期" + weekday) - url, err := service.PresignedURL(l.ctx, ossConfig.BucketName, dbFileInfo.Path, time.Minute*30) - if err != nil { - logx.Error(err) - return err - } - imageBytes, err := l.DownloadAndDecrypt(l.ctx, url, uid) - if err != nil { - logx.Error(err) - return err - } - imageData, err := l.svcCtx.XCipher.Decrypt(imageBytes, []byte(uid)) + + thumbnailUrl, err := service.PresignedURL(l.ctx, ossConfig.BucketName, dbFileInfo.ThumbPath, time.Minute*30) if err != nil { logx.Error(err) return err } + // 使用 Load 或 Store 确保原子操作 value, _ := groupedImages.LoadOrStore(date, []types.ImageMeta{}) images := value.([]types.ImageMeta) @@ -159,7 +138,7 @@ func (l *GetPrivateImageListLogic) GetPrivateImageList(req *types.PrivateImageLi images = append(images, types.ImageMeta{ ID: dbFileInfo.ID, FileName: dbFileInfo.FileName, - URL: base64.StdEncoding.EncodeToString(imageData), + Thumbnail: thumbnailUrl, Width: dbFileInfo.ThumbW, Height: dbFileInfo.ThumbH, CreatedAt: dbFileInfo.CreatedAt.Format("2006-01-02 15:04:05"), @@ -167,14 +146,6 @@ func (l *GetPrivateImageListLogic) GetPrivateImageList(req *types.PrivateImageLi // 重新存储更新后的图像列表 groupedImages.Store(date, images) - - // 缓存单条数据(24小时基础缓存 + 随机防雪崩) - if data, err := json.Marshal(images); err == nil { - expire := 24*time.Hour + time.Duration(rand.Intn(3600))*time.Second - if err := l.svcCtx.RedisClient.Set(l.ctx, imageCacheKey, data, expire).Err(); err != nil { - logx.Error("Failed to cache image meta:", err) - } - } return nil }) } @@ -263,27 +234,3 @@ func (l *GetPrivateImageListLogic) getOssConfigFromCacheOrDb(cacheKey, uid, prov return ossConfig, nil } - -func (l *GetPrivateImageListLogic) DownloadAndDecrypt(ctx context.Context, url string, uid string) ([]byte, error) { - resp, err := l.RestyClient.R(). - SetContext(ctx). - SetDoNotParseResponse(true). // 保持原始响应流 - Get(url) - - if err != nil { - return nil, fmt.Errorf("request failed: %w", err) - } - defer resp.RawBody().Close() - - if resp.StatusCode() != http.StatusOK { - return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode(), resp.Status()) - } - - // 使用缓冲区分块读取 - buf := new(bytes.Buffer) - if _, err := io.Copy(buf, resp.RawBody()); err != nil { - return nil, fmt.Errorf("read response body failed: %w", err) - } - - return buf.Bytes(), nil -} diff --git a/app/auth/api/internal/logic/storage/get_private_image_url_logic.go b/app/auth/api/internal/logic/storage/get_private_image_url_logic.go new file mode 100644 index 0000000..0689b42 --- /dev/null +++ b/app/auth/api/internal/logic/storage/get_private_image_url_logic.go @@ -0,0 +1,218 @@ +package storage + +import ( + "context" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "github.com/go-resty/resty/v2" + "github.com/redis/go-redis/v9" + "gorm.io/gorm" + "io" + "net/http" + "os" + "path/filepath" + "schisandra-album-cloud-microservices/app/auth/model/mysql/model" + "schisandra-album-cloud-microservices/common/constant" + "schisandra-album-cloud-microservices/common/encrypt" + "schisandra-album-cloud-microservices/common/hybrid_encrypt" + storageConfig "schisandra-album-cloud-microservices/common/storage/config" + "schisandra-album-cloud-microservices/common/utils" + "time" + + "schisandra-album-cloud-microservices/app/auth/api/internal/svc" + "schisandra-album-cloud-microservices/app/auth/api/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type GetPrivateImageUrlLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext + RestyClient *resty.Client +} + +func NewGetPrivateImageUrlLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPrivateImageUrlLogic { + return &GetPrivateImageUrlLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + RestyClient: resty.New(). + SetTimeout(30 * time.Second). // 总超时时间 + SetRetryCount(3). // 重试次数 + SetRetryWaitTime(5 * time.Second). // 重试等待时间 + SetRetryMaxWaitTime(30 * time.Second). // 最大重试等待 + AddRetryCondition(func(r *resty.Response, err error) bool { + return r.StatusCode() == http.StatusTooManyRequests || + err != nil || + r.StatusCode() >= 500 + }), + } +} + +// 修改函数签名和实现 +func (l *GetPrivateImageUrlLogic) GetPrivateImageUrl(req *types.SinglePrivateImageRequest) (string, error) { + uid, ok := l.ctx.Value("user_id").(string) + if !ok { + return "", errors.New("user_id not found") + } + + // 构建缓存key + cacheKey := fmt.Sprintf("%s%s:%s:%v", constant.ImageCachePrefix, uid, "encrypted", req.ID) + + // 检查缓存 + if cachedData, err := l.svcCtx.RedisClient.Get(l.ctx, cacheKey).Result(); err == nil { + return cachedData, nil + } + + storageInfo := l.svcCtx.DB.ScaStorageInfo + authUser := l.svcCtx.DB.ScaAuthUser + var result struct { + ID int64 `json:"id"` + Path string `json:"path"` + Password string `json:"password"` + FileType string `json:"file_type"` + } + err := storageInfo. + Select( + storageInfo.ID, + storageInfo.Path, + storageInfo.FileType, + authUser.Password). + LeftJoin(authUser, authUser.UID.EqCol(storageInfo.UserID)). + Where(storageInfo.ID.Eq(req.ID), storageInfo.UserID.Eq(uid), + storageInfo.IsEncrypted.Eq(constant.Encrypt), storageInfo.Provider.Eq(req.Provider), + storageInfo.Bucket.Eq(req.Bucket), authUser.UID.Eq(uid)). + Group(storageInfo.ID).Scan(&result) + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return "", err + } + if result.ID == 0 { + return "", errors.New("image not found") + } + verify := utils.Verify(result.Password, req.Password) + if !verify { + return "", errors.New("invalid password") + } + + // 加载用户oss配置信息 + cacheOssConfigKey := constant.UserOssConfigPrefix + uid + ":" + req.Provider + ossConfig, err := l.getOssConfigFromCacheOrDb(cacheOssConfigKey, uid, req.Provider) + if err != nil { + return "", err + } + + service, err := l.svcCtx.StorageManager.GetStorage(uid, ossConfig) + if err != nil { + return "", errors.New("get storage failed") + } + url, err := service.PresignedURL(l.ctx, ossConfig.BucketName, result.Path, time.Minute*15) + if err != nil { + logx.Error(err) + return "", errors.New("get private image url failed") + } + + resp, err := l.RestyClient.R(). + SetContext(l.ctx). + SetDoNotParseResponse(true). // 保持原始响应流 + Get(url) + if err != nil { + return "", fmt.Errorf("download private image failed: %w", err) + } + defer resp.RawBody().Close() + + body, err := io.ReadAll(resp.RawBody()) + if err != nil { + return "", err + } + + dir, err := os.Getwd() + if err != nil { + return "", err + } + privateKeyPath := filepath.Join(dir, l.svcCtx.Config.Encrypt.PrivateKey) + privateKey, err := os.ReadFile(privateKeyPath) + if err != nil { + return "", err + } + + pem, err := hybrid_encrypt.ImportPrivateKeyPEM(privateKey) + if err != nil { + return "", err + } + image, err := hybrid_encrypt.DecryptImage(pem, body) + if err != nil { + return "", err + } + base64Str := base64.StdEncoding.EncodeToString(image) + + // 设置缓存,过期时间设为12小时 + err = l.svcCtx.RedisClient.Set(l.ctx, cacheKey, base64Str, 12*time.Hour).Err() + if err != nil { + logx.Errorf("cache private image failed: %v", err) + } + + return base64Str, nil +} + +// 提取解密操作为函数 +func (l *GetPrivateImageUrlLogic) decryptConfig(config *model.ScaStorageConfig) (*storageConfig.StorageConfig, error) { + accessKey, err := encrypt.Decrypt(config.AccessKey, l.svcCtx.Config.Encrypt.Key) + if err != nil { + return nil, errors.New("decrypt access key failed") + } + secretKey, err := encrypt.Decrypt(config.SecretKey, l.svcCtx.Config.Encrypt.Key) + if err != nil { + return nil, errors.New("decrypt secret key failed") + } + return &storageConfig.StorageConfig{ + Provider: config.Provider, + Endpoint: config.Endpoint, + AccessKey: accessKey, + SecretKey: secretKey, + BucketName: config.Bucket, + Region: config.Region, + }, nil +} + +// 从缓存或数据库中获取 OSS 配置 +func (l *GetPrivateImageUrlLogic) getOssConfigFromCacheOrDb(cacheKey, uid, provider string) (*storageConfig.StorageConfig, error) { + result, err := l.svcCtx.RedisClient.Get(l.ctx, cacheKey).Result() + if err != nil && !errors.Is(err, redis.Nil) { + return nil, errors.New("get oss config failed") + } + + var ossConfig *storageConfig.StorageConfig + if result != "" { + var redisOssConfig model.ScaStorageConfig + if err = json.Unmarshal([]byte(result), &redisOssConfig); err != nil { + return nil, errors.New("unmarshal oss config failed") + } + return l.decryptConfig(&redisOssConfig) + } + + // 缓存未命中,从数据库中加载 + scaOssConfig := l.svcCtx.DB.ScaStorageConfig + dbOssConfig, err := scaOssConfig.Where(scaOssConfig.UserID.Eq(uid), scaOssConfig.Provider.Eq(provider)).First() + if err != nil { + return nil, err + } + + // 缓存数据库配置 + ossConfig, err = l.decryptConfig(dbOssConfig) + if err != nil { + return nil, err + } + marshalData, err := json.Marshal(dbOssConfig) + if err != nil { + return nil, errors.New("marshal oss config failed") + } + err = l.svcCtx.RedisClient.Set(l.ctx, cacheKey, marshalData, 0).Err() + if err != nil { + return nil, errors.New("set oss config failed") + } + + return ossConfig, nil +} diff --git a/app/auth/api/internal/logic/storage/upload_file_logic.go b/app/auth/api/internal/logic/storage/upload_file_logic.go index 4b58595..e180de5 100644 --- a/app/auth/api/internal/logic/storage/upload_file_logic.go +++ b/app/auth/api/internal/logic/storage/upload_file_logic.go @@ -14,6 +14,7 @@ import ( "io" "mime/multipart" "net/http" + "os" "path" "path/filepath" "schisandra-album-cloud-microservices/app/aisvc/rpc/pb" @@ -22,6 +23,7 @@ import ( "schisandra-album-cloud-microservices/app/auth/model/mysql/model" "schisandra-album-cloud-microservices/common/constant" "schisandra-album-cloud-microservices/common/encrypt" + "schisandra-album-cloud-microservices/common/hybrid_encrypt" "schisandra-album-cloud-microservices/common/storage/config" "strings" "sync" @@ -50,13 +52,16 @@ func (l *UploadFileLogic) UploadFile(r *http.Request) (resp string, err error) { if err != nil { return "", err } - + // 解析上传配置信息 + settingResult, err := l.parseUploadSettingResult(r) + if err != nil { + return "", err + } // 解析上传的文件 file, header, err := l.getUploadedFile(r) if err != nil { return "", err } - defer file.Close() data, err := io.ReadAll(file) if err != nil { @@ -76,12 +81,6 @@ func (l *UploadFileLogic) UploadFile(r *http.Request) (resp string, err error) { return "", err } - // 解析上传配置信息 - settingResult, err := l.parseUploadSettingResult(r) - if err != nil { - return "", err - } - // 使用 `errgroup.Group` 处理并发任务 var ( faceId int64 @@ -110,13 +109,25 @@ func (l *UploadFileLogic) UploadFile(r *http.Request) (resp string, err error) { return nil }) } - var imageBytes []byte + + var uploadReader io.Reader = bytes.NewReader(data) if settingResult.Encrypt { - encryptedData, err := l.svcCtx.XCipher.Encrypt(data, []byte(uid)) + dir, err := os.Getwd() if err != nil { return "", err } - imageBytes = encryptedData + publicKeyPath := filepath.Join(dir, l.svcCtx.Config.Encrypt.PublicKey) + publicKey, err := os.ReadFile(publicKeyPath) + if err != nil { + return "", err + } + + pem, err := hybrid_encrypt.ImportPublicKeyPEM(publicKey) + if err != nil { + return "", err + } + image, err := hybrid_encrypt.EncryptImage(pem, data) + uploadReader = bytes.NewReader(image) } // 上传文件到 OSS @@ -126,7 +137,7 @@ func (l *UploadFileLogic) UploadFile(r *http.Request) (resp string, err error) { } defer sem.Release(1) - fileUrl, thumbUrl, err := l.uploadFileToOSS(uid, header, bytes.NewReader(imageBytes), thumbnail, result) + fileUrl, thumbUrl, err := l.uploadFileToOSS(uid, header, uploadReader, thumbnail, result, settingResult) if err != nil { return err } @@ -210,7 +221,7 @@ func (l *UploadFileLogic) parseUploadSettingResult(r *http.Request) (types.Uploa } // 上传文件到 OSS -func (l *UploadFileLogic) uploadFileToOSS(uid string, header *multipart.FileHeader, file io.Reader, thumbnail io.Reader, result types.File) (string, string, error) { +func (l *UploadFileLogic) uploadFileToOSS(uid string, header *multipart.FileHeader, file io.Reader, thumbnail io.Reader, result types.File, settingResult types.UploadSetting) (string, string, error) { cacheKey := constant.UserOssConfigPrefix + uid + ":" + result.Provider ossConfig, err := l.getOssConfigFromCacheOrDb(cacheKey, uid, result.Provider) if err != nil { @@ -220,7 +231,6 @@ func (l *UploadFileLogic) uploadFileToOSS(uid string, header *multipart.FileHead if err != nil { return "", "", errors.New("get storage failed") } - objectKey := path.Join( constant.ImageSpace, uid, @@ -228,6 +238,15 @@ func (l *UploadFileLogic) uploadFileToOSS(uid string, header *multipart.FileHead l.classifyFile(result.FileType, result.IsScreenshot), fmt.Sprintf("%s_%s%s", strings.TrimSuffix(header.Filename, filepath.Ext(header.Filename)), kgo.SimpleUuid(), filepath.Ext(header.Filename)), ) + if settingResult.Encrypt { + objectKey = path.Join( + constant.ImageSpace, + uid, + time.Now().Format("2006/01"), // 按年/月划分目录 + "encrypted", + fmt.Sprintf("%s_%s%s", strings.TrimSuffix(header.Filename, filepath.Ext(header.Filename)), kgo.SimpleUuid(), ".enc"), + ) + } _, err = service.UploadFileSimple(l.ctx, ossConfig.BucketName, objectKey, file, map[string]string{ "Content-Type": header.Header.Get("Content-Type"), @@ -241,7 +260,7 @@ func (l *UploadFileLogic) uploadFileToOSS(uid string, header *multipart.FileHead uid, time.Now().Format("2006/01"), // 按年/月划分目录 l.classifyFile(result.FileType, result.IsScreenshot), - fmt.Sprintf("%s_%s%s", strings.TrimSuffix(header.Filename, filepath.Ext(header.Filename)), kgo.SimpleUuid(), filepath.Ext(header.Filename)), + fmt.Sprintf("%s_%s", strings.TrimSuffix(header.Filename, filepath.Ext(header.Filename)), kgo.SimpleUuid()), ) _, err = service.UploadFileSimple(l.ctx, ossConfig.BucketName, thumbObjectKey, thumbnail, map[string]string{ "Content-Type": header.Header.Get("Content-Type"), @@ -249,6 +268,7 @@ func (l *UploadFileLogic) uploadFileToOSS(uid string, header *multipart.FileHead if err != nil { return "", "", errors.New("upload thumbnail file failed") } + return objectKey, thumbObjectKey, nil } diff --git a/app/auth/api/internal/logic/system/get_user_list_logic.go b/app/auth/api/internal/logic/system/get_user_list_logic.go new file mode 100644 index 0000000..a664888 --- /dev/null +++ b/app/auth/api/internal/logic/system/get_user_list_logic.go @@ -0,0 +1,30 @@ +package system + +import ( + "context" + + "schisandra-album-cloud-microservices/app/auth/api/internal/svc" + "schisandra-album-cloud-microservices/app/auth/api/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type GetUserListLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewGetUserListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserListLogic { + return &GetUserListLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *GetUserListLogic) GetUserList() (resp *types.UserInfoListResponse, err error) { + // todo: add your logic here and delete this line + + return +} diff --git a/app/auth/api/internal/logic/user/admin_login_logic.go b/app/auth/api/internal/logic/user/admin_login_logic.go new file mode 100644 index 0000000..5db9277 --- /dev/null +++ b/app/auth/api/internal/logic/user/admin_login_logic.go @@ -0,0 +1,59 @@ +package user + +import ( + "context" + "gorm.io/gorm" + "net/http" + "schisandra-album-cloud-microservices/common/captcha/verify" + "schisandra-album-cloud-microservices/common/constant" + "schisandra-album-cloud-microservices/common/errors" + "schisandra-album-cloud-microservices/common/i18n" + "schisandra-album-cloud-microservices/common/utils" + + "schisandra-album-cloud-microservices/app/auth/api/internal/svc" + "schisandra-album-cloud-microservices/app/auth/api/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type AdminLoginLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewAdminLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminLoginLogic { + return &AdminLoginLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *AdminLoginLogic) AdminLogin(r *http.Request, req *types.AdminLoginRequest) (resp *types.LoginResponse, err error) { + captcha := verify.VerifyBasicTextCaptcha(req.Dots, req.Key, l.svcCtx.RedisClient, l.ctx) + if !captcha { + return nil, errors.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "captcha.verificationFailure")) + } + authUser := l.svcCtx.DB.ScaAuthUser + permissionRule := l.svcCtx.DB.ScaAuthPermissionRule + adminUser, err := authUser. + LeftJoin(permissionRule, authUser.UID.EqCol(permissionRule.V0)). + Where(authUser.Username.Eq(req.Account), authUser.Password.Eq(req.Password), permissionRule.V1.Eq(constant.Admin)). + Group(authUser.UID).First() + if err != nil && err != gorm.ErrRecordNotFound { + return nil, err + } + if adminUser == nil { + return nil, errors.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "login.notPermission")) + } + if !utils.Verify(adminUser.Password, req.Password) { + return nil, errors.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "login.invalidPassword")) + } + data, err := HandleLoginJWT(adminUser, l.svcCtx, true, r, l.ctx) + if err != nil { + return nil, err + } + + return data, nil +} diff --git a/app/auth/api/internal/logic/user/phone_login_logic.go b/app/auth/api/internal/logic/user/phone_login_logic.go index 4ad5539..935c660 100644 --- a/app/auth/api/internal/logic/user/phone_login_logic.go +++ b/app/auth/api/internal/logic/user/phone_login_logic.go @@ -60,7 +60,11 @@ func (l *PhoneLoginLogic) PhoneLogin(r *http.Request, req *types.PhoneLoginReque if userInfo == nil { uid := idgen.NextId() uidStr := strconv.FormatInt(uid, 10) - avatar := utils2.GenerateAvatar(uidStr) + avatar, err := l.svcCtx.PN.Generate(uidStr, false).ToBase64() + if err != nil { + tx.Rollback() + return nil, err + } name := randomname.GenerateName() male := constant2.Male user := &model.ScaAuthUser{ diff --git a/app/auth/api/internal/logic/user/wechat_offiaccount_login_logic.go b/app/auth/api/internal/logic/user/wechat_offiaccount_login_logic.go index 0034e0d..4c7075d 100644 --- a/app/auth/api/internal/logic/user/wechat_offiaccount_login_logic.go +++ b/app/auth/api/internal/logic/user/wechat_offiaccount_login_logic.go @@ -12,7 +12,6 @@ import ( errors2 "schisandra-album-cloud-microservices/common/errors" "schisandra-album-cloud-microservices/common/i18n" "schisandra-album-cloud-microservices/common/random_name" - "schisandra-album-cloud-microservices/common/utils" "strconv" "schisandra-album-cloud-microservices/app/auth/api/internal/svc" @@ -58,7 +57,11 @@ func (l *WechatOffiaccountLoginLogic) WechatOffiaccountLogin(r *http.Request, re // 创建用户 uid := idgen.NextId() uidStr := strconv.FormatInt(uid, 10) - avatar := utils.GenerateAvatar(uidStr) + avatar, err := l.svcCtx.PN.Generate(uidStr, false).ToBase64() + if err != nil { + tx.Rollback() + return nil, err + } name := randomname.GenerateName() addUser := &model2.ScaAuthUser{ diff --git a/app/auth/api/internal/svc/service_context.go b/app/auth/api/internal/svc/service_context.go index a0bfb51..c055135 100644 --- a/app/auth/api/internal/svc/service_context.go +++ b/app/auth/api/internal/svc/service_context.go @@ -3,11 +3,12 @@ package svc import ( "github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount" "github.com/casbin/casbin/v2" - "github.com/landaiqing/go-xcipher" + "github.com/landaiqing/go-pixelnebula" "github.com/lionsoul2014/ip2region/binding/golang/xdb" "github.com/minio/minio-go/v7" "github.com/nsqio/go-nsq" "github.com/redis/go-redis/v9" + "github.com/wenlng/go-captcha/v2/click" "github.com/wenlng/go-captcha/v2/rotate" "github.com/wenlng/go-captcha/v2/slide" "github.com/zeromicro/go-zero/rest" @@ -46,13 +47,14 @@ type ServiceContext struct { WechatOfficial *officialAccount.OfficialAccount RotateCaptcha rotate.Captcha SlideCaptcha slide.Captcha + TextCaptcha click.Captcha Sensitive *sensitive.Manager StorageManager *manager.Manager MinioClient *minio.Client GeoRegionData *geo_json.RegionData NSQProducer *nsq.Producer ZincClient *zincx.ZincClient - XCipher *xcipher.XCipher + PN *pixelnebula.PixelNebula } func NewServiceContext(c config.Config) *ServiceContext { @@ -72,6 +74,7 @@ func NewServiceContext(c config.Config) *ServiceContext { WechatOfficial: wechat_official.NewWechatPublic(c.Wechat.AppID, c.Wechat.AppSecret, c.Wechat.Token, c.Wechat.AESKey, c.Redis.Host, c.Redis.Pass, c.Redis.DB), RotateCaptcha: initialize.NewRotateCaptcha(), SlideCaptcha: initialize.NewSlideCaptcha(), + TextCaptcha: initialize.NewTextCaptcha(), Sensitive: sensitivex.NewSensitive(), StorageManager: storage.InitStorageManager(), AiSvcRpc: aiservice.NewAiService(zrpc.MustNewClient(c.AiSvcRpc)), @@ -79,7 +82,7 @@ func NewServiceContext(c config.Config) *ServiceContext { GeoRegionData: geo_json.NewGeoJSON(), NSQProducer: nsqx.NewNsqProducer(c.NSQ.NSQDHost), ZincClient: zincx.NewZincClient(c.Zinc.URL, c.Zinc.Username, c.Zinc.Password), - XCipher: xcipher.NewXCipher([]byte(c.Encrypt.Key)), + PN: pixelnebula.NewPixelNebula().WithDefaultCache(), } return serviceContext } diff --git a/app/auth/api/internal/types/types.go b/app/auth/api/internal/types/types.go index a7846dc..fca7198 100644 --- a/app/auth/api/internal/types/types.go +++ b/app/auth/api/internal/types/types.go @@ -18,6 +18,13 @@ type AddImageToAlbumRequest struct { Bucket string `json:"bucket"` } +type AdminLoginRequest struct { + Account string `json:"account"` + Password string `json:"password"` + Dots string `json:"dots"` + Key string `json:"key"` +} + type Album struct { ID int64 `json:"id"` Name string `json:"name"` @@ -175,6 +182,9 @@ type CoordinateMeta struct { Latitude float64 `json:"latitude"` Longitude float64 `json:"longitude"` ImageCount int64 `json:"image_count"` + Country string `json:"country"` + Province string `json:"province"` + City string `json:"city"` } type DeleteImageRequest struct { @@ -325,6 +335,8 @@ type PrivateImageListRequest struct { Provider string `json:"provider"` Bucket string `json:"bucket"` Password string `json:"password"` + Dots string `json:"dots"` + Key string `json:"key"` } type QueryDeleteRecordRequest struct { @@ -517,6 +529,13 @@ type SingleImageRequest struct { ID int64 `json:"id"` } +type SinglePrivateImageRequest struct { + ID int64 `json:"id"` + Password string `json:"password"` + Provider string `json:"provider"` + Bucket string `json:"bucket"` +} + type SlideCaptchaResponse struct { Key string `json:"key"` Image string `json:"image"` @@ -572,6 +591,12 @@ type StroageNode struct { Children []StorageMeta `json:"children"` } +type TextCaptchaResponse struct { + Key string `json:"key"` + Image string `json:"image"` + Thumb string `json:"thumb"` +} + type ThingDetailListRequest struct { TagName string `json:"tag_name"` Provider string `json:"provider"` @@ -609,6 +634,21 @@ type UploadRequest struct { UserId string `json:"user_id"` } +type UserInfoListResponse struct { + Records []UserMeta `json:"records"` +} + +type UserMeta struct { + ID int64 `json:"id"` + Username string `json:"username"` + Nickname string `json:"nickname"` + Avatar string `json:"avatar"` + Email string `json:"email"` + Phone string `json:"phone"` + Status int64 `json:"status"` + CreatedAt string `json:"created_at"` +} + type UserSecuritySettingResponse struct { BindPhone bool `json:"bind_phone,default=false"` BindEmail bool `json:"bind_email,default=falsel"` @@ -633,3 +673,8 @@ type WechatOffiaccountLoginRequest struct { Openid string `json:"openid"` ClientId string `json:"client_id"` } + +type ImageStreamResponse struct { + ContentType string `json:"content_type"` + Size int64 `json:"size"` +} diff --git a/app/auth/resources/language/active.en.toml b/app/auth/resources/language/active.en.toml index ef0739f..739210b 100644 --- a/app/auth/resources/language/active.en.toml +++ b/app/auth/resources/language/active.en.toml @@ -15,7 +15,7 @@ passwordNotMatch = "password not match!" passwordFormatError = "password format error!" resetPasswordError = "reset password error!" loginSuccess = "login success!" - +notPermission = "not permission!" [sms] smsSendTooFrequently = "sms send too frequently!" smsSendFailed = "sms send failed!" diff --git a/app/auth/resources/language/active.zh.toml b/app/auth/resources/language/active.zh.toml index af7937e..eeeec25 100644 --- a/app/auth/resources/language/active.zh.toml +++ b/app/auth/resources/language/active.zh.toml @@ -15,6 +15,7 @@ passwordNotMatch = "两次输入的密码不一致!" passwordFormatError = "密码格式错误!" resetPasswordError = "重置密码失败!" loginSuccess = "登录成功!" +notPermission = "无权访问!" [sms] smsSendTooFrequently = "验证码发送过于频繁,请稍后再试!" diff --git a/common/captcha/generate/generate_basic_text_captcha.go b/common/captcha/generate/generate_basic_text_captcha.go index 4eeafb0..d3046ce 100644 --- a/common/captcha/generate/generate_basic_text_captcha.go +++ b/common/captcha/generate/generate_basic_text_captcha.go @@ -13,7 +13,7 @@ import ( ) // GenerateBasicTextCaptcha generates a basic text captcha and saves it to redis. -func GenerateBasicTextCaptcha(capt click.Captcha, redis redis.Client, ctx context.Context) (map[string]interface{}, error) { +func GenerateBasicTextCaptcha(capt click.Captcha, redis *redis.Client, ctx context.Context) (map[string]interface{}, error) { captData, err := capt.Generate() if err != nil { return nil, err diff --git a/common/captcha/initialize/text_captcha.go b/common/captcha/initialize/text_captcha.go index bfad5b4..bbb97f1 100644 --- a/common/captcha/initialize/text_captcha.go +++ b/common/captcha/initialize/text_captcha.go @@ -5,8 +5,10 @@ import ( "github.com/wenlng/go-captcha-assets/bindata/chars" "github.com/wenlng/go-captcha-assets/resources/fonts/fzshengsksjw" "github.com/wenlng/go-captcha-assets/resources/images" + "github.com/wenlng/go-captcha-assets/resources/thumbs" "github.com/wenlng/go-captcha/v2/base/option" "github.com/wenlng/go-captcha/v2/click" + "log" ) // NewTextCaptcha 初始化点选验证码 @@ -47,51 +49,18 @@ func NewTextCaptcha() click.Captcha { } // thumb images - // thumbImages, err := thumbs.GetThumbs() - // if err != nil { - // log.Fatalln(err) - // } + thumbImages, err := thumbs.GetThumbs() + if err != nil { + log.Fatalln(err) + } // set resources builder.SetResources( click.WithChars(chars.GetChineseChars()), - // click.WithChars([]string{ - // "1A", - // "5E", - // "3d", - // "0p", - // "78", - // "DL", - // "CB", - // "9M", - // }), - // click.WithChars(chars.GetAlphaChars()), - click.WithFonts([]*truetype.Font{fonts}), - click.WithBackgrounds(imgs), - // click.WithThumbBackgrounds(thumbImages), - ) - // global.TextCaptcha = builder.Make() - - // ============================ - - builder.Clear() - builder.SetOptions( - click.WithRangeLen(option.RangeVal{Min: 4, Max: 6}), - click.WithRangeVerifyLen(option.RangeVal{Min: 2, Max: 4}), - click.WithRangeThumbColors([]string{ - "#4a85fb", - "#d93ffb", - "#56be01", - "#ee2b2b", - "#cd6904", - "#b49b03", - "#01ad90", - }), - ) - builder.SetResources( - click.WithChars(chars.GetChineseChars()), + click.WithChars(chars.GetAlphaChars()), click.WithFonts([]*truetype.Font{fonts}), click.WithBackgrounds(imgs), + click.WithThumbBackgrounds(thumbImages), ) return builder.Make() } diff --git a/common/constant/redis_prefix.go b/common/constant/redis_prefix.go index 7d7fd28..a84cb8b 100644 --- a/common/constant/redis_prefix.go +++ b/common/constant/redis_prefix.go @@ -22,7 +22,6 @@ const ( const ( ImageCachePrefix = "image:cache:" - ImageRecentPrefix = "image:recent:" ImageSinglePrefix = "image:single:" ImageSharePrefix = "image:share:" ImageShareVisitPrefix = "image:share:visit:" diff --git a/common/constant/user_role.go b/common/constant/user_role.go index 2da19f2..dbc78e6 100644 --- a/common/constant/user_role.go +++ b/common/constant/user_role.go @@ -1,7 +1,6 @@ package constant const ( - Root string = "root" Admin string = "admin" User string = "user" ) diff --git a/common/hybrid_encrypt/README.md b/common/hybrid_encrypt/README.md new file mode 100644 index 0000000..76dda69 --- /dev/null +++ b/common/hybrid_encrypt/README.md @@ -0,0 +1,82 @@ +# 混合加密图片方案 + +本模块提供了一套完整的混合加密解决方案,用于图片数据的安全传输。它使用RSA和AES-GCM混合加密方式,确保数据传输的安全性和效率。 + +## 工作原理 + +1. **混合加密**:使用RSA加密AES密钥,使用AES-GCM加密实际数据 + - 生成随机AES-256密钥 + - 使用RSA公钥加密AES密钥 + - 使用AES-GCM加密图片数据 + - 组合加密后的AES密钥、nonce和加密数据 + - 将结果进行Base64编码,便于网络传输 + +2. **混合解密**:使用RSA私钥解密AES密钥,使用AES-GCM解密数据 + - Base64解码密文 + - 分离加密的AES密钥、nonce和加密数据 + - 使用RSA私钥解密AES密钥 + - 使用解密后的AES密钥和nonce解密数据 + +## 后端使用方法 (Go) + +```go +// 生成RSA密钥对 +privateKey, _ := hybrid_encrypt.GenerateRSAKey(2048) + +// 导出公钥和私钥为PEM格式 +pubPEM, _ := hybrid_encrypt.ExportPublicKeyPEM(&privateKey.PublicKey) +privPEM := hybrid_encrypt.ExportPrivateKeyPEM(privateKey) + +// 将PEM格式的密钥转换为Base64编码,便于在网络上传输 +base64PubKey := base64.StdEncoding.EncodeToString(pubPEM) +base64PrivKey := base64.StdEncoding.EncodeToString(privPEM) + +// 读取图片文件 +imageData, _ := ioutil.ReadFile("path/to/image.jpg") + +// 使用公钥加密图片 +encryptedBase64, _ := hybrid_encrypt.EncryptImageWithBase64Key(base64PubKey, imageData) + +// 将加密后的Base64字符串发送到前端 +// ... + +// 如果需要在后端解密 +decryptedData, _ := hybrid_encrypt.DecryptImageWithBase64Key(base64PrivKey, encryptedBase64) +``` + +## 前端使用方法 (JavaScript) + +```javascript +import { decryptImage } from './hybrid_encrypt_js.js'; + +// 从后端接收加密的图片数据和私钥 +const encryptedImageBase64 = '...'; // 从后端接收的Base64编码的加密图片数据 +const privateKeyPEM = '...'; // 从安全渠道获取的PEM格式RSA私钥 + +// 解密图片 +decryptImage(encryptedImageBase64, privateKeyPEM) + .then(decryptedImageBase64 => { + // 使用解密后的图片数据 + const imgElement = document.createElement('img'); + imgElement.src = decryptedImageBase64; // 直接设置为data:image/jpeg;base64,...格式 + document.body.appendChild(imgElement); + }) + .catch(error => { + console.error('图片解密失败:', error); + }); +``` + +## 安全注意事项 + +1. 私钥必须妥善保管,不应在不安全的渠道传输 +2. 对于大型图片,考虑分块加密和解密 +3. 在生产环境中,应使用至少2048位的RSA密钥 +4. 前端解密应在安全的环境中进行,避免私钥泄露 + +## 性能考虑 + +混合加密方案在处理大型数据(如高分辨率图片)时具有明显的性能优势: + +- RSA仅用于加密小型AES密钥(32字节) +- 大型图片数据使用高效的AES-GCM加密 +- 加密后的数据大小增加有限,主要是RSA加密的AES密钥部分 \ No newline at end of file diff --git a/common/hybrid_encrypt/hybrid_encrypt.go b/common/hybrid_encrypt/hybrid_encrypt.go new file mode 100644 index 0000000..b723559 --- /dev/null +++ b/common/hybrid_encrypt/hybrid_encrypt.go @@ -0,0 +1,180 @@ +package hybrid_encrypt + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + "io" +) + +// HybridEncrypt 使用RSA公钥加密AES密钥,并用AES-GCM加密数据 +func HybridEncrypt(publicKey *rsa.PublicKey, plaintext []byte) ([]byte, error) { + // 生成随机AES-256密钥 + aesKey := make([]byte, 32) + if _, err := io.ReadFull(rand.Reader, aesKey); err != nil { + return nil, err + } + + // 创建AES-GCM实例 + block, err := aes.NewCipher(aesKey) + if err != nil { + return nil, err + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + // 生成随机Nonce + nonce := make([]byte, gcm.NonceSize()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return nil, err + } + + // 加密数据 + ciphertext := gcm.Seal(nil, nonce, plaintext, nil) + + // 加密AES密钥 + encryptedKey, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, publicKey, aesKey, nil) + if err != nil { + return nil, err + } + + // 组合最终密文:加密的AES密钥 + nonce + AES加密的数据 + result := make([]byte, len(encryptedKey)+len(nonce)+len(ciphertext)) + copy(result[:len(encryptedKey)], encryptedKey) + copy(result[len(encryptedKey):len(encryptedKey)+len(nonce)], nonce) + copy(result[len(encryptedKey)+len(nonce):], ciphertext) + + return result, nil +} + +// HybridDecrypt 使用RSA私钥解密AES密钥,并用AES-GCM解密数据 +func HybridDecrypt(privateKey *rsa.PrivateKey, ciphertext []byte) ([]byte, error) { + keySize := privateKey.PublicKey.Size() + if len(ciphertext) < keySize+12 { + return nil, errors.New("ciphertext too short") + } + + // 分解密文各部分 + encryptedKey := ciphertext[:keySize] + nonce := ciphertext[keySize : keySize+12] + data := ciphertext[keySize+12:] + + // 解密AES密钥 + aesKey, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, privateKey, encryptedKey, nil) + if err != nil { + return nil, err + } + + // 创建AES-GCM实例 + block, err := aes.NewCipher(aesKey) + if err != nil { + return nil, err + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + // 解密数据 + plaintext, err := gcm.Open(nil, nonce, data, nil) + if err != nil { + return nil, err + } + + return plaintext, nil +} + +// GenerateRSAKey 生成RSA密钥对 +func GenerateRSAKey(bits int) (*rsa.PrivateKey, error) { + return rsa.GenerateKey(rand.Reader, bits) +} + +// ExportPublicKeyPEM 导出公钥为PEM格式 +func ExportPublicKeyPEM(pub *rsa.PublicKey) ([]byte, error) { + pubASN1, err := x509.MarshalPKIXPublicKey(pub) + if err != nil { + return nil, err + } + pubPEM := pem.EncodeToMemory(&pem.Block{ + Type: "RSA PUBLIC KEY", + Bytes: pubASN1, + }) + return pubPEM, nil +} + +// ExportPrivateKeyPEM 导出私钥为PEM格式 +func ExportPrivateKeyPEM(priv *rsa.PrivateKey) []byte { + privPEM := pem.EncodeToMemory(&pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(priv), + }) + return privPEM +} + +// ImportPublicKeyPEM 从PEM导入公钥 +func ImportPublicKeyPEM(pubPEM []byte) (*rsa.PublicKey, error) { + block, _ := pem.Decode(pubPEM) + if block == nil { + return nil, errors.New("failed to parse PEM block") + } + + pub, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return nil, err + } + + rsaPub, ok := pub.(*rsa.PublicKey) + if !ok { + return nil, errors.New("not RSA public key") + } + return rsaPub, nil +} + +// ImportPrivateKeyPEM 从PEM导入私钥 +func ImportPrivateKeyPEM(privPEM []byte) (*rsa.PrivateKey, error) { + block, _ := pem.Decode(privPEM) + if block == nil { + return nil, errors.New("failed to parse PEM block") + } + return x509.ParsePKCS1PrivateKey(block.Bytes) +} + +func main() { + // 生成RSA密钥对 + privateKey, err := GenerateRSAKey(2048) + if err != nil { + panic(err) + } + + // 导出导入测试 + pubPEM, _ := ExportPublicKeyPEM(&privateKey.PublicKey) + publicKey, _ := ImportPublicKeyPEM(pubPEM) + + privPEM := ExportPrivateKeyPEM(privateKey) + privateKey, _ = ImportPrivateKeyPEM(privPEM) + + // 加密测试 + message := []byte("Secret image data") + fmt.Printf("Original: %s\n", message) + + ciphertext, err := HybridEncrypt(publicKey, message) + if err != nil { + panic(err) + } + fmt.Printf("Ciphertext length: %d bytes\n", len(ciphertext)) + + // 解密测试 + plaintext, err := HybridDecrypt(privateKey, ciphertext) + if err != nil { + panic(err) + } + fmt.Printf("Decrypted: %s\n", plaintext) +} diff --git a/common/hybrid_encrypt/hybrid_encrypt_js.js b/common/hybrid_encrypt/hybrid_encrypt_js.js new file mode 100644 index 0000000..3139a3c --- /dev/null +++ b/common/hybrid_encrypt/hybrid_encrypt_js.js @@ -0,0 +1,148 @@ +/** + * hybrid_encrypt_js.js + * 前端混合解密实现,与Go后端的hybrid_encrypt.go配合使用 + */ + +/** + * 使用RSA私钥和AES-GCM解密数据 + * @param {ArrayBuffer} ciphertext - 加密的数据 + * @param {CryptoKey} privateKey - RSA私钥 + * @returns {Promise} - 解密后的数据 + */ +async function hybridDecrypt(ciphertext, privateKey) { + // 将ArrayBuffer转换为Uint8Array以便处理 + const ciphertextArray = new Uint8Array(ciphertext); + + // 获取RSA密钥大小(字节) + const keySize = getPrivateKeySize(privateKey); + + // 检查密文长度是否足够 + if (ciphertextArray.length < keySize + 12) { + throw new Error('密文太短'); + } + + // 分解密文各部分 + const encryptedKey = ciphertextArray.slice(0, keySize); + const nonce = ciphertextArray.slice(keySize, keySize + 12); + const data = ciphertextArray.slice(keySize + 12); + + // 使用RSA私钥解密AES密钥 + const aesKeyBuffer = await window.crypto.subtle.decrypt( + { + name: 'RSA-OAEP', + hash: { name: 'SHA-256' }, + }, + privateKey, + encryptedKey + ); + + // 使用AES密钥和GCM模式解密数据 + const aesKey = await window.crypto.subtle.importKey( + 'raw', + aesKeyBuffer, + { name: 'AES-GCM', length: 256 }, + false, + ['decrypt'] + ); + + const plaintext = await window.crypto.subtle.decrypt( + { + name: 'AES-GCM', + iv: nonce, + }, + aesKey, + data + ); + + return plaintext; +} + +/** + * 从PEM格式导入RSA私钥 + * @param {string} pemString - PEM格式的私钥字符串 + * @returns {Promise} - 导入的RSA私钥 + */ +async function importPrivateKeyFromPEM(pemString) { + // 移除PEM头尾和换行符 + const pemContents = pemString + .replace('-----BEGIN RSA PRIVATE KEY-----', '') + .replace('-----END RSA PRIVATE KEY-----', '') + .replace(/\s+/g, ''); + + // Base64解码 + const binaryDer = window.atob(pemContents); + const derArray = new Uint8Array(binaryDer.length); + + for (let i = 0; i < binaryDer.length; i++) { + derArray[i] = binaryDer.charCodeAt(i); + } + + // 导入私钥 + const privateKey = await window.crypto.subtle.importKey( + 'pkcs8', + derArray.buffer, + { + name: 'RSA-OAEP', + hash: { name: 'SHA-256' }, + }, + false, + ['decrypt'] + ); + + return privateKey; +} + +/** + * 获取RSA私钥的大小(字节) + * @param {CryptoKey} privateKey - RSA私钥 + * @returns {number} - 密钥大小(字节) + */ +function getPrivateKeySize(privateKey) { + // 在实际应用中,可能需要从privateKey对象中提取密钥大小 + // 这里简化处理,假设使用2048位RSA密钥(256字节) + return 256; // 2048位 / 8 = 256字节 +} + +/** + * 解密Base64编码的混合加密图片数据 + * @param {string} base64Data - Base64编码的加密图片数据 + * @param {string} privateKeyPEM - PEM格式的RSA私钥 + * @returns {Promise} - 解密后的图片Base64字符串 + */ +async function decryptImage(base64Data, privateKeyPEM) { + try { + // 解码Base64 + const binaryString = window.atob(base64Data); + const bytes = new Uint8Array(binaryString.length); + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + + // 导入私钥 + const privateKey = await importPrivateKeyFromPEM(privateKeyPEM); + + // 解密数据 + const decryptedData = await hybridDecrypt(bytes.buffer, privateKey); + + // 将解密后的图片数据转换为Base64 + const decryptedArray = new Uint8Array(decryptedData); + let binary = ''; + for (let i = 0; i < decryptedArray.length; i++) { + binary += String.fromCharCode(decryptedArray[i]); + } + + // 返回带有MIME类型的Base64图片数据 + // 注意:这里假设是JPEG图片,实际应用中可能需要根据图片类型动态设置 + return 'data:image/jpeg;base64,' + window.btoa(binary); + } catch (error) { + console.error('图片解密失败:', error); + throw error; + } +} + +// 导出函数 +export { + hybridDecrypt, + importPrivateKeyFromPEM, + decryptImage +}; \ No newline at end of file diff --git a/common/hybrid_encrypt/image_encrypt.go b/common/hybrid_encrypt/image_encrypt.go new file mode 100644 index 0000000..32d43a7 --- /dev/null +++ b/common/hybrid_encrypt/image_encrypt.go @@ -0,0 +1,46 @@ +package hybrid_encrypt + +import ( + "crypto/rsa" + "errors" +) + +// EncryptImage 使用混合加密方式加密图片数据并返回Base64编码的密文 +// 参数: +// - publicKey: RSA公钥 +// - imageData: 原始图片数据 +// +// 返回: +// - string: Base64编码的加密图片数据 +// - error: 错误信息 +func EncryptImage(publicKey *rsa.PublicKey, imageData []byte) ([]byte, error) { + if len(imageData) == 0 { + return nil, errors.New("empty image data") + } + + // 使用混合加密方式加密图片数据 + ciphertext, err := HybridEncrypt(publicKey, imageData) + if err != nil { + return nil, err + } + + return ciphertext, nil +} + +// DecryptImage 解密Base64编码的加密图片数据 +// 参数: +// - privateKey: RSA私钥 +// - base64Ciphertext: Base64编码的加密图片数据 +// +// 返回: +// - []byte: 解密后的原始图片数据 +// - error: 错误信息 +func DecryptImage(privateKey *rsa.PrivateKey, ciphertext []byte) ([]byte, error) { + // 使用混合解密方式解密图片数据 + imageData, err := HybridDecrypt(privateKey, ciphertext) + if err != nil { + return nil, err + } + + return imageData, nil +} diff --git a/common/hybrid_encrypt/image_encrypt_test.go b/common/hybrid_encrypt/image_encrypt_test.go new file mode 100644 index 0000000..8432e19 --- /dev/null +++ b/common/hybrid_encrypt/image_encrypt_test.go @@ -0,0 +1,28 @@ +package hybrid_encrypt + +import ( + "fmt" + "testing" +) + +// 测试生成RSA密钥对、导出公钥和私钥为PEM格式、加密解密图片数据 +// 注意:测试代码仅用于演示,实际应用中请使用更安全的加密算法和密钥长度 +// 请不要在生产环境中使用此测试代码 +func TestHybridEncrypt(t *testing.T) { + // 生成RSA密钥对 + privateKey, err := GenerateRSAKey(2048) + if err != nil { + t.Fatalf("Failed to generate RSA key: %v", err) + } + + // 导出公钥和私钥为PEM格式 + pubPEM, err := ExportPublicKeyPEM(&privateKey.PublicKey) + if err != nil { + t.Fatalf("Failed to export public key: %v", err) + } + + privPEM := ExportPrivateKeyPEM(privateKey) + // 打印公钥和私钥的PEM格式 + fmt.Println(string(pubPEM)) + fmt.Println(string(privPEM)) +} diff --git a/common/utils/encrypt_test.go b/common/utils/encrypt_test.go new file mode 100644 index 0000000..f78e0f3 --- /dev/null +++ b/common/utils/encrypt_test.go @@ -0,0 +1,11 @@ +package utils + +import "testing" + +func TestEncrypt(t *testing.T) { + encrypt, err := Encrypt("LDQ20020618xxx") + if err != nil { + t.Fatal(err) + } + t.Log(encrypt) +} diff --git a/common/utils/generate_avatar.go b/common/utils/generate_avatar.go deleted file mode 100644 index f6da048..0000000 --- a/common/utils/generate_avatar.go +++ /dev/null @@ -1,683 +0,0 @@ -package utils - -import ( - "crypto/sha256" - "encoding/base64" - "encoding/hex" - "errors" - "math" - "regexp" - "strconv" - "strings" -) - -type Options struct { - Theme int - Part int -} - -var svgStart = "" -var svgEnd = "" -var env = "" -var head = "" - -// Themes -var themes [16][3]map[string][]string - -// Shapes -var sp [16]map[string]string - -func init() { - // Robo - themes[0][0] = map[string][]string{ - "env": {"#ff2f2b"}, - "clo": {"#fff", "#000"}, - "head": {"#fff"}, - "mouth": {"#fff", "#000", "#000"}, - "eyes": {"#000", "none", "#0ff"}, - "top": {"#fff", "#fff"}, - } - themes[0][1] = map[string][]string{ - "env": {"#ff1ec1"}, - "clo": {"#000", "#fff"}, - "head": {"#ffc1c1"}, - "mouth": {"#fff", "#000", "#000"}, - "eyes": {"#FF2D00", "#fff", "none"}, - "top": {"#a21d00", "#fff"}, - } - themes[0][2] = map[string][]string{ - "env": {"#0079b1"}, - "clo": {"#0e00b1", "#d1fffe"}, - "head": {"#f5aa77"}, - "mouth": {"#fff", "#000", "#000"}, - "eyes": {"#0c00de", "#fff", "none"}, - "top": {"#acfffd", "#acfffd"}, - } - - // Girl - themes[1][0] = map[string][]string{ - "env": {"#a50000"}, - "clo": {"#f06", "#8e0039"}, - "head": {"#85492C"}, - "mouth": {"#000"}, - "eyes": {"#000", "#ff9809"}, - "top": {"#ff9809", "#ff9809", "none", "none"}, - } - themes[1][1] = map[string][]string{ - "env": {"#40E83B"}, - "clo": {"#00650b", "#62ce5a"}, - "head": {"#f7c1a6"}, - "mouth": {"#6e1c1c"}, - "eyes": {"#000", "#ff833b"}, - "top": {"#67FFCC", "none", "none", "#ecff3b"}, - } - themes[1][2] = map[string][]string{ - "env": {"#ff2c2c"}, - "clo": {"#fff", "#000"}, - "head": {"#ffce8b"}, - "mouth": {"#000"}, - "eyes": {"#000", "#0072ff"}, - "top": {"#ff9809", "none", "#ffc809", "none"}, - } - - // Blonde - themes[2][0] = map[string][]string{ - "env": {"#ff7520"}, - "clo": {"#d12823"}, - "head": {"#fee3c5"}, - "mouth": {"#d12823"}, - "eyes": {"#000", "none"}, - "top": {"#000", "none", "none", "#FFCC00", "red"}, - } - themes[2][1] = map[string][]string{ - "env": {"#ff9700"}, - "clo": {"#000"}, - "head": {"#d2ad6d"}, - "mouth": {"#000"}, - "eyes": {"#000", "#00ffdc"}, - "top": {"#fdff00", "#fdff00", "none", "none", "none"}, - } - themes[2][2] = map[string][]string{ - "env": {"#26a7ff"}, - "clo": {"#d85cd7"}, - "head": {"#542e02"}, - "mouth": {"#f70014"}, - "eyes": {"#000", "magenta"}, - "top": {"#FFCC00", "#FFCC00", "#FFCC00", "#ff0000", "yellow"}, - } - - // Evilnormie - themes[3][0] = map[string][]string{ - "env": {"#6FC30E"}, - "clo": {"#b4e1fa", "#5b5d6e", "#515262", "#a0d2f0", "#a0d2f0"}, - "head": {"#fae3b9"}, - "mouth": {"#fff", "#000"}, - "eyes": {"#000"}, - "top": {"#8eff45", "#8eff45", "none", "none"}, - } - themes[3][1] = map[string][]string{ - "env": {"#00a58c"}, - "clo": {"#000", "#5b00", "#5100", "#a000", "#a000"}, - "head": {"#FAD2B9"}, - "mouth": {"#fff", "#000"}, - "eyes": {"#000"}, - "top": {"#FFC600", "none", "#FFC600", "none"}, - } - themes[3][2] = map[string][]string{ - "env": {"#ff501f"}, - "clo": {"#000", "#ff0000", "#ff0000", "#7d7d7d", "#7d7d7d"}, - "head": {"#fff3dc"}, - "mouth": {"#d2001b", "none"}, - "eyes": {"#000"}, - "top": {"#D2001B", "none", "none", "#D2001B"}, - } - - // Country - themes[4][0] = map[string][]string{ - "env": {"#fc0"}, - "clo": {"#901e0e", "#ffbe1e", "#ffbe1e", "#c55f54"}, - "head": {"#f8d9ad"}, - "mouth": {"#000", "none", "#000", "none"}, - "eyes": {"#000"}, - "top": {"#583D00", "#AF892E", "#462D00", "#a0a0a0"}, - } - themes[4][1] = map[string][]string{ - "env": {"#386465"}, - "clo": {"#fff", "#333", "#333", "#333"}, - "head": {"#FFD79D"}, - "mouth": {"#000", "#000", "#000", "#000"}, - "eyes": {"#000"}, - "top": {"#27363C", "#5DCAD4", "#314652", "#333"}, - } - themes[4][2] = map[string][]string{ - "env": {"#DFFF00"}, - "clo": {"#304267", "#aab0b1", "#aab0b1", "#aab0b1"}, - "head": {"#e6b876"}, - "mouth": {"#50230a", "#50230a", "#50230a", "#50230a"}, - "eyes": {"#000"}, - "top": {"#333", "#afafaf", "#222", "#6d3a1d"}, - } - - // Johnyold - themes[5][0] = map[string][]string{ - "env": {"#a09300"}, - "clo": {"#c7d4e2", "#435363", "#435363", "#141720", "#141720", "#e7ecf2", "#e7ecf2"}, - "head": {"#f5d4a6"}, - "mouth": {"#000", "#cf9f76"}, - "eyes": {"#000", "#000", "#000", "#000", "#000", "#000", "#fff", "#fff", "#fff", "#fff", "#000", "#000"}, - "top": {"none", "#fdff00"}, - } - themes[5][1] = map[string][]string{ - "env": {"#b3003e"}, - "clo": {"#000", "#435363", "#435363", "#000", "none", "#e7ecf2", "#e7ecf2"}, - "head": {"#f5d4a6"}, - "mouth": {"#000", "#af9f94"}, - "eyes": {"#9ff3ffdb", "#000", "#9ff3ffdb", "#000", "#2f508a", "#000", "#000", "#000", "none", "none", "none", "none"}, - "top": {"#ff9a00", "#ff9a00"}, - } - themes[5][2] = map[string][]string{ - "env": {"#884f00"}, - "clo": {"#ff0000", "#fff", "#fff", "#141720", "#141720", "#e7ecf2", "#e7ecf2"}, - "head": {"#c57b14"}, - "mouth": {"#000", "#cf9f76"}, - "eyes": {"none", "#000", "none", "#000", "#5a0000", "#000", "#000", "#000", "none", "none", "none", "none"}, - "top": {"#efefef", "none"}, - } - - // Asian - themes[6][0] = map[string][]string{ - "env": {"#8acf00"}, - "clo": {"#ee2829", "#ff0"}, - "head": {"#ffce73"}, - "mouth": {"#fff", "#000"}, - "eyes": {"#000"}, - "top": {"#000", "#000", "none", "#000", "#ff4e4e", "#000"}, - } - themes[6][1] = map[string][]string{ - "env": {"#00d2a3"}, - "clo": {"#0D0046", "#ffce73"}, - "head": {"#ffce73"}, - "mouth": {"#000", "none"}, - "eyes": {"#000"}, - "top": {"#000", "#000", "#000", "none", "#ffb358", "#000", "none", "none"}, - } - themes[6][2] = map[string][]string{ - "env": {"#ff184e"}, - "clo": {"#000", "none"}, - "head": {"#ffce73"}, - "mouth": {"#ff0000", "none"}, - "eyes": {"#000"}, - "top": {"none", "none", "none", "none", "none", "#ffc107", "none", "none"}, - } - - // Punk - themes[7][0] = map[string][]string{ - "env": {"#00deae"}, - "clo": {"#ff0000"}, - "head": {"#ffce94"}, - "mouth": {"#f73b6c", "#000"}, - "eyes": {"#e91e63", "#000", "#e91e63", "#000", "#000", "#000"}, - "top": {"#dd104f", "#dd104f", "#f73b6c", "#dd104f"}, - } - themes[7][1] = map[string][]string{ - "env": {"#181284"}, - "clo": {"#491f49", "#ff9809", "#491f49"}, - "head": {"#f6ba97"}, - "mouth": {"#ff9809", "#000"}, - "eyes": {"#c4ffe4", "#000", "#c4ffe4", "#000", "#000", "#000"}, - "top": {"none", "none", "#d6f740", "#516303"}, - } - themes[7][2] = map[string][]string{ - "env": {"#bcf700"}, - "clo": {"#ff14e4", "#000", "#14fffd"}, - "head": {"#7b401e"}, - "mouth": {"#666", "#000"}, - "eyes": {"#00b5b4", "#000", "#00b5b4", "#000", "#000", "#000"}, - "top": {"#14fffd", "#14fffd", "#14fffd", "#0d3a62"}, - } - - // Afrohair - themes[8][0] = map[string][]string{ - "env": {"#0df"}, - "clo": {"#571e57", "#ff0"}, - "head": {"#f2c280"}, - "mouth": {"#ff0000"}, - "eyes": {"#795548", "#000"}, - "top": {"#de3b00", "none"}, - } - themes[8][1] = map[string][]string{ - "env": {"#B400C2"}, - "clo": {"#0D204A", "#00ffdf"}, - "head": {"#ca8628"}, - "mouth": {"#1a1a1a"}, - "eyes": {"#cbbdaf", "#000"}, - "top": {"#000", "#000"}, - } - themes[8][2] = map[string][]string{ - "env": {"#ffe926"}, - "clo": {"#00d6af", "#000"}, - "head": {"#8c5100"}, - "mouth": {"#7d0000"}, - "eyes": {"none", "#000"}, - "top": {"#f7f7f7", "none"}, - } - - // Normie female - themes[9][0] = map[string][]string{ - "env": {"#4aff0c"}, - "clo": {"#101010", "#fff", "#fff"}, - "head": {"#dbbc7f"}, - "mouth": {"#000"}, - "eyes": {"#000", "none", "none"}, - "top": {"#531148", "#531148", "#531148", "none"}, - } - themes[9][1] = map[string][]string{ - "env": {"#FFC107"}, - "clo": {"#033c58", "#fff", "#fff"}, - "head": {"#dbc97f"}, - "mouth": {"#000"}, - "eyes": {"none", "#fff", "#000"}, - "top": {"#FFEB3B", "#FFEB3B", "none", "#FFEB3B"}, - } - themes[9][2] = map[string][]string{ - "env": {"#FF9800"}, - "clo": {"#b40000", "#fff", "#fff"}, - "head": {"#E2AF6B"}, - "mouth": {"#000"}, - "eyes": {"none", "#fff", "#000"}, - "top": {"#ec0000", "#ec0000", "none", "none"}, - } - - // Older - themes[10][0] = map[string][]string{ - "env": {"#104c8c"}, - "clo": {"#354B65", "#3D8EBB", "#89D0DA", "#00FFFD"}, - "head": {"#cc9a5c"}, - "mouth": {"#222", "#fff"}, - "eyes": {"#000", "#000"}, - "top": {"#fff", "#fff", "none"}, - } - themes[10][1] = map[string][]string{ - "env": {"#0DC15C"}, - "clo": {"#212121", "#fff", "#212121", "#fff"}, - "head": {"#dca45f"}, - "mouth": {"#111", "#633b1d"}, - "eyes": {"#000", "#000"}, - "top": {"none", "#792B74", "#792B74"}, - } - themes[10][2] = map[string][]string{ - "env": {"#ffe500"}, - "clo": {"#1e5e80", "#fff", "#1e5e80", "#fff"}, - "head": {"#e8bc86"}, - "mouth": {"#111", "none"}, - "eyes": {"#000", "#000"}, - "top": {"none", "none", "#633b1d"}, - } - - // Firehair - themes[11][0] = map[string][]string{ - "env": {"#4a3f73"}, - "clo": {"#e6e9ee", "#f1543f", "#ff7058", "#fff", "#fff"}, - "head": {"#b27e5b"}, - "mouth": {"#191919", "#191919"}, - "eyes": {"#000", "#000", "#57FFFD"}, - "top": {"#ffc", "#ffc", "#ffc"}, - } - themes[11][1] = map[string][]string{ - "env": {"#00a08d"}, - "clo": {"#FFBA32", "#484848", "#4e4e4e", "#fff", "#fff"}, - "head": {"#ab5f2c"}, - "mouth": {"#191919", "#191919"}, - "eyes": {"#000", "#ff23fa63", "#000"}, - "top": {"#ff90f4", "#ff90f4", "#ff90f4"}, - } - themes[11][2] = map[string][]string{ - "env": {"#22535d"}, - "clo": {"#000", "#ff2500", "#ff2500", "#fff", "#fff"}, - "head": {"#a76c44"}, - "mouth": {"#191919", "#191919"}, - "eyes": {"#000", "none", "#000"}, - "top": {"none", "#00efff", "none"}, - } - - // Blond - themes[12][0] = map[string][]string{ - "env": {"#2668DC"}, - "clo": {"#2385c6", "#b8d0e0", "#b8d0e0"}, - "head": {"#ad8a60"}, - "mouth": {"#000", "#4d4d4d"}, - "eyes": {"#7fb5a2", "#d1eddf", "#301e19"}, - "top": {"#fff510", "#fff510"}, - } - themes[12][1] = map[string][]string{ - "env": {"#643869"}, - "clo": {"#D67D1B", "#b8d0e0", "#b8d0e0"}, - "head": {"#CC985A", "none0000"}, - "mouth": {"#000", "#ececec"}, - "eyes": {"#1f2644", "#9b97ce", "#301e19"}, - "top": {"#00eaff", "none"}, - } - themes[12][2] = map[string][]string{ - "env": {"#F599FF"}, - "clo": {"#2823C6", "#b8d0e0", "#b8d0e0"}, - "head": {"#C7873A"}, - "mouth": {"#000", "#4d4d4d"}, - "eyes": {"#581b1b", "#FF8B8B", "#000"}, - "top": {"none", "#9c0092"}, - } - - // Ateam - themes[13][0] = map[string][]string{ - "env": {"#d10084"}, - "clo": {"#efedee", "#00a1e0", "#00a1e0", "#efedee", "#ffce1c"}, - "head": {"#b35f49"}, - "mouth": {"#3a484a", "#000"}, - "eyes": {"#000"}, - "top": {"#000", "none", "#000", "none"}, - } - themes[13][1] = map[string][]string{ - "env": {"#E6C117"}, - "clo": {"#efedee", "#ec0033", "#ec0033", "#efedee", "#f2ff05"}, - "head": {"#ffc016"}, - "mouth": {"#4a3737", "#000"}, - "eyes": {"#000"}, - "top": {"#ffe900", "#ffe900", "none", "#ffe900"}, - } - themes[13][2] = map[string][]string{ - "env": {"#1d8c00"}, - "clo": {"#e000cb", "#fff", "#fff", "#e000cb", "#ffce1c"}, - "head": {"#b96438"}, - "mouth": {"#000", "#000"}, - "eyes": {"#000"}, - "top": {"#53ffff", "#53ffff", "none", "none"}, - } - - // Rasta - themes[14][0] = map[string][]string{ - "env": {"#fc0065"}, - "clo": {"#708913", "#fdea14", "#708913", "#fdea14", "#708913"}, - "head": {"#DEA561"}, - "mouth": {"#444", "#000"}, - "eyes": {"#000"}, - "top": {"#32393f", "#32393f", "#32393f", "#32393f", "#32393f", "#32393f", "#32393f", "#32393f", "#32393f", "#32393f", "#32393f", "#32393f", "#32393f", "#32393f", "#32393f", "#32393f", "#32393f"}, - } - themes[14][1] = map[string][]string{ - "env": {"#81f72e"}, - "clo": {"#ff0000", "#ffc107", "#ff0000", "#ffc107", "#ff0000"}, - "head": {"#ef9831"}, - "mouth": {"#6b0000", "#000"}, - "eyes": {"#000"}, - "top": {"#FFFAAD", "#FFFAAD", "#FFFAAD", "#FFFAAD", "#FFFAAD", "#FFFAAD", "#FFFAAD", "#FFFAAD", "#FFFAAD", "#FFFAAD", "#FFFAAD", "#FFFAAD", "#FFFAAD", "none", "none", "none", "none"}, - } - themes[14][2] = map[string][]string{ - "env": {"#00D872"}, - "clo": {"#590D00", "#FD1336", "#590D00", "#FD1336", "#590D00"}, - "head": {"#c36c00"}, - "mouth": {"#56442b", "#000"}, - "eyes": {"#000"}, - "top": {"#004E4C", "#004E4C", "#004E4C", "#004E4C", "#004E4C", "#004E4C", "#004E4C", "#004E4C", "#004E4C", "none", "none", "none", "none", "none", "none", "none", "none"}, - } - - // Meta - themes[15][0] = map[string][]string{ - "env": {"#111"}, - "clo": {"#000", "#00FFFF"}, - "head": {"#755227"}, - "mouth": {"#fff", "#000"}, - "eyes": {"black", "#008a", "aqua"}, - "top": {"#fff", "#fff", "#fff", "#fff", "#fff"}, - } - themes[15][1] = map[string][]string{ - "env": {"#00D0D4"}, - "clo": {"#000", "#fff"}, - "head": {"#755227"}, - "mouth": {"#fff", "#000"}, - "eyes": {"black", "#1df7ffa3", "#fcff2c"}, - "top": {"#fff539", "none", "#fff539", "none", "#fff539"}, - } - themes[15][2] = map[string][]string{ - "env": {"#DC75FF"}, - "clo": {"#000", "#FFBDEC"}, - "head": {"#997549"}, - "mouth": {"#fff", "#000"}, - "eyes": {"black", "black", "aqua"}, - "top": {"#00fffd", "none", "none", "none", "none"}, - } - - // Robo - sp[0] = map[string]string{ - "clo": "", - "mouth": "", - "eyes": "", - "top": "", - } - - // Girl - sp[1] = map[string]string{ - "clo": "", - "mouth": "", - "eyes": "", - "top": "", - } - - // Blonde - sp[2] = map[string]string{ - "clo": "", - "mouth": "", - "eyes": "", - "top": "", - } - - // Guy - sp[3] = map[string]string{ - "clo": "", - "mouth": "", - "eyes": "", - "top": "", - } - - // Country - sp[4] = map[string]string{ - "clo": "", - "mouth": "", - "eyes": "", - "top": "", - } - - // Geeknot - sp[5] = map[string]string{ - "clo": "", - "mouth": "", - "eyes": "", - "top": "", - } - - // Asian - sp[6] = map[string]string{ - "clo": "", - "mouth": "", - "eyes": "", - "top": "", - } - - // Punk - sp[7] = map[string]string{ - "clo": "", - "mouth": "", - "eyes": "", - "top": "", - } - - // Afrohair - sp[8] = map[string]string{ - "clo": "", - "mouth": "", - "eyes": "", - "top": "", - } - - // Normie Female - sp[9] = map[string]string{ - "clo": "", - "mouth": "", - "eyes": "", - "top": "", - } - - // Older - sp[10] = map[string]string{ - "clo": "", - "mouth": "", - "eyes": "", - "top": "", - } - - // Firehair - sp[11] = map[string]string{ - "clo": "", - "mouth": "", - "eyes": "", - "top": "", - } - - // Blond - sp[12] = map[string]string{ - "clo": "", - "mouth": "", - "eyes": "", - "top": "", - } - - // Ateam - sp[13] = map[string]string{ - "clo": "", - "mouth": "", - "eyes": "", - "top": "", - } - - // Rasta - sp[14] = map[string]string{ - "clo": "", - "mouth": "", - "eyes": "", - "top": "", - } - - // Meta - sp[15] = map[string]string{ - "clo": "", - "mouth": "", - "eyes": "", - "top": "", - } -} - -func SvgCode(avatarId string, sansEnv bool, opts *Options) (svg string, err error) { - if avatarId == "" { - err = errors.New("avatar id is required") - return - } - - h := sha256.New() - h.Write([]byte(avatarId)) - sum := h.Sum(nil) - s := hex.EncodeToString(sum) - reg := regexp.MustCompile("[0-9]") - hash := reg.FindAllString(s, -1)[0:12] - - var p = make(map[string][2]int, 6) - var num int - num, err = strconv.Atoi(strings.Join(hash[:2], "")) - if err != nil { - num = 0 - } - p["env"] = getKey(int(math.Floor(.47*float64(num)+.5)), opts) - - num, err = strconv.Atoi(strings.Join(hash[2:4], "")) - if err != nil { - num = 0 - } - p["clo"] = getKey(int(math.Floor(.47*float64(num)+.5)), opts) - - num, err = strconv.Atoi(strings.Join(hash[4:6], "")) - if err != nil { - num = 0 - } - p["head"] = getKey(int(math.Floor(.47*float64(num)+.5)), opts) - - num, err = strconv.Atoi(strings.Join(hash[6:8], "")) - if err != nil { - num = 0 - } - p["mouth"] = getKey(int(math.Floor(.47*float64(num)+.5)), opts) - - num, err = strconv.Atoi(strings.Join(hash[8:10], "")) - if err != nil { - num = 0 - } - p["eyes"] = getKey(int(math.Floor(.47*float64(num)+.5)), opts) - - num, err = strconv.Atoi(strings.Join(hash[10:], "")) - if err != nil { - num = 0 - } - p["top"] = getKey(int(math.Floor(.47*float64(num)+.5)), opts) - - var final = make(map[string]string, 6) - for k, v := range p { - colors := themes[v[0]][v[1]][k] - var svgPart string - if k == "env" { - svgPart = env - } else if k == "head" { - svgPart = head - } else { - svgPart = sp[v[0]][k] - } - - reg := regexp.MustCompile("#(.*?)+(;)") - match := reg.FindAllString(svgPart, -1) - for i, rm := range match { - svgPart = strings.Replace(svgPart, rm, colors[i]+";", 1) - } - final[k] = svgPart - } - - var builder strings.Builder - builder.WriteString(svgStart) - builder.WriteString(final["env"]) - builder.WriteString(final["head"]) - builder.WriteString(final["clo"]) - builder.WriteString(final["top"]) - builder.WriteString(final["eyes"]) - builder.WriteString(final["mouth"]) - builder.WriteString(svgEnd) - svg = builder.String() - - return -} - -func getKey(v int, opts *Options) [2]int { - if opts != nil { - return [2]int{opts.Part, opts.Theme} - } - - if v > 31 { - return [2]int{v - 32, 2} - } else if v > 15 { - return [2]int{v - 16, 1} - } else { - return [2]int{v, 0} - } -} - -// GenerateAvatar generates an avatar for the given user ID. -func GenerateAvatar(userId string) (baseImg string) { - svg, err := SvgCode(userId, true, nil) - if err != nil { - return "data:image/svg+xml;charset=utf-8;base64,PHN2ZyB0PSIxNzMwNDUwODA4NDIwIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjkyMDMiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48cGF0aCBkPSJNOTc0LjU5NjU0NiA1MDUuOTYwNTg0YzAgMjU1LjQ4NDg0My0yMDcuMTA3NTczIDQ2Ni40MjA0NzctNDYyLjU5NjU0NiA0NjYuNDIwNDc3LTI1NS40ODg5NzMgMC00NjIuNTk2NTQ2LTIxMC45MzU2MzQtNDYyLjU5NjU0Ni00NjYuNDIwNDc3QzQ5LjQwMzQ1NCAyNTAuNDcxNjEyIDI1Ni41MTEwMjcgNDMuMzU5OTA5IDUxMiA0My4zNTk5MDljMjU1LjQ4ODk3MyAwIDQ2Mi41OTY1NDYgMjA3LjExMTcwMyA0NjIuNTk2NTQ2IDQ2Mi42MDA2NzV6IiBmaWxsPSIjRkZFNTc4IiBwLWlkPSI5MjA0Ij48L3BhdGg+PHBhdGggZD0iTTUxMiAxMDAzLjM1MjQyNGMtMjcyLjE1NTY5NiAwLTQ5My41Njc5MDktMjIzLjEzMDA5Mi00OTMuNTY3OTA5LTQ5Ny4zOTE4NEMxOC40MzIwOTEgMjMzLjgwNDg4OCAyMzkuODQ0MzA0IDEyLjM4ODU0NSA1MTIgMTIuMzg4NTQ1czQ5My41Njc5MDkgMjIxLjQxNjM0MyA0OTMuNTY3OTA5IDQ5My41Njc5MWMwIDI3NC4yNjU4NzgtMjIxLjQxMjIxNCA0OTcuMzk1OTctNDkzLjU2NzkwOSA0OTcuMzk1OTY5ek01MTIgNzQuMzMxMjczQzI3My45OTk1MjQgNzQuMzMxMjczIDgwLjM3NDgxOCAyNjcuOTYwMTA4IDgwLjM3NDgxOCA1MDUuOTYwNTg0YzAgMjQwLjExMDY1OCAxOTMuNjI4ODM2IDQzNS40NDkxMTMgNDMxLjYyNTE4MiA0MzUuNDQ5MTEzczQzMS42MjUxODItMTk1LjM0MjU4NCA0MzEuNjI1MTgyLTQzNS40NDkxMTNDOTQzLjYyNTE4MiAyNjcuOTYwMTA4IDc1MC4wMDA0NzYgNzQuMzMxMjczIDUxMiA3NC4zMzEyNzN6IiBmaWxsPSIjNkU2RTk2IiBwLWlkPSI5MjA1Ij48L3BhdGg+PHBhdGggZD0iTTUxMS43OTc2NTQgNDMuMzU5OTA5Yy0yMzUuMDY4NTIgMC00MjkuMTc2Mzc5IDE3NS4zMzkyMTMtNDU4LjY5ODI4NCA0MDIuMzYzNDM4IDEuMDk0MzIyIDIuMTUxNDc3IDEuNzI2MTM3IDMuMjg3MDk0IDEuNzI2MTM4IDMuMjg3MDk0aDI1MC42MDM3NTZsNDIuMDUwODUzLTE4MC4wODgxNTYgMzguMjMxMDUxIDE4MC4wODgxNTZoNTg1LjE4OTQ2MkM5NDIuODMyMzE1IDIyMC4zOTYzNTMgNzQ3Ljk5NzY2MSA0My4zNTk5MDkgNTExLjc5NzY1NCA0My4zNTk5MDl6IiBmaWxsPSIjOUM5Q0JDIiBwLWlkPSI5MjA2Ij48L3BhdGg+PHBhdGggZD0iTTI1MC4wNTY1OTUgNjA5LjE4MTk0NWE4MC4yODYwMzMgNjguODE0MjQgOTAgMSAwIDEzNy42Mjg0ODEgMCA4MC4yODYwMzMgNjguODE0MjQgOTAgMSAwLTEzNy42Mjg0ODEgMFoiIGZpbGw9IiM2RTZFOTYiIHAtaWQ9IjkyMDciPjwvcGF0aD48cGF0aCBkPSJNNjMzLjMyOTI4NCA2MDkuMTgxOTQ1YTgwLjI4NjAzMyA2OC44MTAxMTEgOTAgMSAwIDEzNy42MjAyMjIgMCA4MC4yODYwMzMgNjguODEwMTExIDkwIDEgMC0xMzcuNjIwMjIyIDBaIiBmaWxsPSIjNkU2RTk2IiBwLWlkPSI5MjA4Ij48L3BhdGg+PHBhdGggZD0iTTEwMDUuOTA2NTI5IDQ3OS45ODE4MDRIMzYwLjYyNDM2M2wtMTQuNTg1NDQ3LTY4LjY5NDQ4NC0xNi4wMzkwMzcgNjguNjk0NDg0SDM2LjY2MzlsLTguOTE5NzUyLTE1LjkzOTkyOHMtMC44MjU5MDMtMS40NzAxMDctMi4yNDIzMjctNC4yNjE2NmwtNC4zNTI1MDktOC41NDM5NjcgMS4yMzQ3MjUtOS41MDYxNDNDNTQuMjE0MzQgMTk2Ljk2NTQ4NCAyNjQuNjEzMTM2IDEyLjM4ODU0NSA1MTEuNzk3NjU0IDEyLjM4ODU0NWMyNDguOTY0MzM5IDAgNDU5LjU0ODk2MyAxODYuMDg0MjExIDQ4OS44NDcyMTYgNDMyLjg0NzUxOWw0LjI2MTY1OSAzNC43NDU3NHogbS01OTUuMTEyNjg2LTYxLjk0MjcyN2g1MjMuNjU5Njg2Qzg5My41MjE3NzUgMjE5LjgwMTcwMyA3MTcuNjYyMjQzIDc0LjMzMTI3MyA1MTEuNzk3NjU0IDc0LjMzMTI3MyAzMDYuNDM2ODY2IDc0LjMzMTI3MyAxMzAuMTEwNjk4IDIxOS45ODc1MzEgODkuMTM3NjQ5IDQxOC4wMzkwNzdoMTkxLjcxMjc0MWwzNi40NjM2MTgtMTU2LjE1NzYxNSA2MC40NTYxMDIgMC42MTExNjggMzMuMDIzNzMzIDE1NS41NDY0NDd6IiBmaWxsPSIjNkU2RTk2IiBwLWlkPSI5MjA5Ij48L3BhdGg+PC9zdmc+" - } - svgBytes := []byte(svg) - return "data:image/svg+xml;base64," + base64.StdEncoding.EncodeToString(svgBytes) -} diff --git a/go.mod b/go.mod index 97295c3..5220cad 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module schisandra-album-cloud-microservices -go 1.24 - -toolchain go1.24.1 +go 1.24.1 require ( github.com/ArtisanCloud/PowerLibs/v3 v3.3.2 @@ -10,7 +8,7 @@ require ( github.com/Kagami/go-face v0.0.0-20210630145111-0c14797b4d0e github.com/aliyun/alibabacloud-oss-go-sdk-v2 v1.2.1 github.com/asjdf/gorm-cache v1.2.3 - github.com/casbin/casbin/v2 v2.103.0 + github.com/casbin/casbin/v2 v2.104.0 github.com/casbin/gorm-adapter/v3 v3.32.0 github.com/ccpwcn/kgo v1.2.9 github.com/corona10/goimagehash v1.1.0 @@ -18,7 +16,7 @@ require ( github.com/go-resty/resty/v2 v2.16.5 github.com/golang-jwt/jwt/v5 v5.2.1 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 - github.com/landaiqing/go-xcipher v0.1.0 + github.com/landaiqing/go-pixelnebula v0.1.0 github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20241220152942-06eb5c6e8230 github.com/lxzan/gws v1.8.8 github.com/microcosm-cc/bluemonday v1.0.27 @@ -95,7 +93,7 @@ require ( github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20250302191652-9094ed2288e7 // indirect + github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect @@ -127,7 +125,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect - github.com/onsi/ginkgo/v2 v2.23.0 // indirect + github.com/onsi/ginkgo/v2 v2.23.1 // indirect github.com/openzipkin/zipkin-go v0.4.3 // indirect github.com/orcaman/concurrent-map/v2 v2.0.1 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect @@ -136,7 +134,7 @@ require ( github.com/prometheus/client_golang v1.21.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.63.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect + github.com/prometheus/procfs v0.16.0 // indirect github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/quic-go v0.50.0 // indirect github.com/refraction-networking/utls v1.6.7 // indirect @@ -187,12 +185,12 @@ require ( k8s.io/apimachinery v0.32.3 // indirect k8s.io/client-go v0.32.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20250304201544-e5f78fe3ede9 // indirect + k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect modernc.org/libc v1.61.13 // indirect modernc.org/mathutil v1.7.1 // indirect - modernc.org/memory v1.8.2 // indirect - modernc.org/sqlite v1.36.0 // indirect + modernc.org/memory v1.9.0 // indirect + modernc.org/sqlite v1.36.1 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect diff --git a/go.sum b/go.sum index 8bf9e20..579d225 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,9 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/ArtisanCloud/PowerLibs/v3 v3.3.1 h1:SsxBygxATQpFS92pKuVtGrgdawwsscj9Y0M0jks9rTo= -github.com/ArtisanCloud/PowerLibs/v3 v3.3.1/go.mod h1:xFGsskCnzAu+6rFEJbGVAlwhrwZPXAny6m7j71S/B5k= github.com/ArtisanCloud/PowerLibs/v3 v3.3.2 h1:IInr1YWwkhwOykxDqux1Goym0uFhrYwBjmgLnEwCLqs= github.com/ArtisanCloud/PowerLibs/v3 v3.3.2/go.mod h1:xFGsskCnzAu+6rFEJbGVAlwhrwZPXAny6m7j71S/B5k= github.com/ArtisanCloud/PowerSocialite/v3 v3.0.7 h1:P+erNlErr+X2v7Et+yTWaTfIRhw+HfpAPdvNIEwk9Gw= github.com/ArtisanCloud/PowerSocialite/v3 v3.0.7/go.mod h1:VZQNCvcK/rldF3QaExiSl1gJEAkyc5/I8RLOd3WFZq4= -github.com/ArtisanCloud/PowerWeChat/v3 v3.3.6 h1:63LAZisWFAN+2B1fTPNTkbPjiDn8pIL+yS9lbDxUvhQ= -github.com/ArtisanCloud/PowerWeChat/v3 v3.3.6/go.mod h1:nIs82Blb0W8QoD6qCx01Lp1W/kM+/n18BgX10UcRIkQ= github.com/ArtisanCloud/PowerWeChat/v3 v3.4.1 h1:N8duKMsES4HU+t6P518/BTKPYHd4v2ggVH48TZ1Gg7M= github.com/ArtisanCloud/PowerWeChat/v3 v3.4.1/go.mod h1:ybM3u4Lhso0X+ZsgoRCF4e5W1KT2fBc6plpjPZ2fop4= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= @@ -40,8 +36,6 @@ github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO 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/aliyun/alibabacloud-oss-go-sdk-v2 v1.2.0 h1:gUWBCekWIFWYK7jXhRvPHIa7mRhJih2BGPjzvzodO18= -github.com/aliyun/alibabacloud-oss-go-sdk-v2 v1.2.0/go.mod h1:FTzydeQVmR24FI0D6XWUOMKckjXehM/jgMn1xC+DA9M= github.com/aliyun/alibabacloud-oss-go-sdk-v2 v1.2.1 h1:sOhpJdR/+lbQniznp3cYSfwQlXbVkT0ccuiZScBrI6Y= github.com/aliyun/alibabacloud-oss-go-sdk-v2 v1.2.1/go.mod h1:FTzydeQVmR24FI0D6XWUOMKckjXehM/jgMn1xC+DA9M= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= @@ -61,8 +55,8 @@ 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/casbin/casbin/v2 v2.103.0 h1:dHElatNXNrr8XcseUov0ZSiWjauwmZZE6YMV3eU1yic= -github.com/casbin/casbin/v2 v2.103.0/go.mod h1:Ee33aqGrmES+GNL17L0h9X28wXuo829wnNUnS0edAco= +github.com/casbin/casbin/v2 v2.104.0 h1:qDakyBZ4jUg1VskF1+UzIwkg+uXWcp0u0M9PMm1RsTA= +github.com/casbin/casbin/v2 v2.104.0/go.mod h1:Ee33aqGrmES+GNL17L0h9X28wXuo829wnNUnS0edAco= github.com/casbin/gorm-adapter/v3 v3.32.0 h1:Au+IOILBIE9clox5BJhI2nA3p9t7Ep1ePlupdGbGfus= github.com/casbin/gorm-adapter/v3 v3.32.0/go.mod h1:Zre/H8p17mpv5U3EaWgPoxLILLdXO3gHW5aoQQpUDZI= github.com/casbin/govaluate v1.3.0 h1:VA0eSY0M2lA86dYd5kPPuNZMUD9QkWnOCnavGrw9myc= @@ -95,16 +89,10 @@ github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/ github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/dolthub/maphash v0.1.0 h1:bsQ7JsF4FkkWyrP3oCnFJgrCUAFbFf3kOl4L/QxPDyQ= github.com/dolthub/maphash v0.1.0/go.mod h1:gkg4Ch4CdCDu5h6PMriVLawB7koZ+5ijb9puGMV50a4= -github.com/duke-git/lancet/v2 v2.3.4 h1:8XGI7P9w+/GqmEBEXYaH/XuNiM0f4/90Ioti0IvYJls= -github.com/duke-git/lancet/v2 v2.3.4/go.mod h1:zGa2R4xswg6EG9I6WnyubDbFO/+A/RROxIbXcwryTsc= github.com/duke-git/lancet/v2 v2.3.5 h1:vb49UWkkdyu2eewilZbl0L3X3T133znSQG0FaeJIBMg= github.com/duke-git/lancet/v2 v2.3.5/go.mod h1:zGa2R4xswg6EG9I6WnyubDbFO/+A/RROxIbXcwryTsc= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/elastic/elastic-transport-go/v8 v8.6.1 h1:h2jQRqH6eLGiBSN4eZbQnJLtL4bC5b4lfVFRjw2R4e4= -github.com/elastic/elastic-transport-go/v8 v8.6.1/go.mod h1:YLHer5cj0csTzNFXoNQ8qhtGY1GTvSqPnKWKaqQE3Hk= -github.com/elastic/go-elasticsearch/v8 v8.17.1 h1:bOXChDoCMB4TIwwGqKd031U8OXssmWLT3UrAr9EGs3Q= -github.com/elastic/go-elasticsearch/v8 v8.17.1/go.mod h1:MVJCtL+gJJ7x5jFeUmA20O7rvipX8GcQmo5iBcmaJn4= github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= @@ -122,14 +110,10 @@ 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.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -139,7 +123,6 @@ github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.9.0 h1:Y0zIbQXhQKmQgTp44Y1dp3wTXcn804QoTptLZT1vtvo= github.com/go-sql-driver/mysql v1.9.0/go.mod h1:pDetrLJeA3oMujJuvXc8RJoasr589B6A9fwzD3QMrqw= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= @@ -166,8 +149,6 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS 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 v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= @@ -183,10 +164,8 @@ github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17 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-20250208200701-d0013a598941 h1:43XjGa6toxLpeksjcxs1jIoIyr+vUfOqY2c6HB4bpoc= -github.com/google/pprof v0.0.0-20250208200701-d0013a598941/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= -github.com/google/pprof v0.0.0-20250302191652-9094ed2288e7 h1:+J3r2e8+RsmN3vKfo75g0YSY61ms37qzPglu4p0sGro= -github.com/google/pprof v0.0.0-20250302191652-9094ed2288e7/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -196,9 +175,6 @@ github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= @@ -210,8 +186,6 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/imroc/req/v3 v3.49.1 h1:Nvwo02riiPEzh74ozFHeEJrtjakFxnoWNR3YZYuQm9U= -github.com/imroc/req/v3 v3.49.1/go.mod h1:tsOk8K7zI6cU4xu/VWCZVtq9Djw9IWm4MslKzme5woU= github.com/imroc/req/v3 v3.50.0 h1:n3BVnZiTRpvkN5T1IB79LC/THhFU9iXksNRMH4ZNVaY= github.com/imroc/req/v3 v3.50.0/go.mod h1:tsOk8K7zI6cU4xu/VWCZVtq9Djw9IWm4MslKzme5woU= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= @@ -255,8 +229,8 @@ 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/landaiqing/go-xcipher v0.1.0 h1:VjFFWAqE2B8HCAY8IFq0tyl8k8nh42x1HoxSrB34dqk= -github.com/landaiqing/go-xcipher v0.1.0/go.mod h1:uBtPlTbWPHWRakmQiKFnoqqU/w54StrxZ+PmE7IUWfo= +github.com/landaiqing/go-pixelnebula v0.1.0 h1:vyrhDJuQZZTa2IBHdqA/+NpBEf6ZWI2oOt988uJLEG0= +github.com/landaiqing/go-pixelnebula v0.1.0/go.mod h1:5T9YOpHXLg82/7MeA4UgBfNpEZhwYpCb+jxJf6OkGWY= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20241220152942-06eb5c6e8230 h1:B0oaMTAQKDZd8cwYT0qsAI7+c3KbFeBNA8GhgoBMXWw= @@ -281,8 +255,6 @@ github.com/minio/crc64nvme v1.0.1 h1:DHQPrYPdqK7jQG/Ls5CTBZWeex/2FMS3G5XGkycuFrY github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.87 h1:nkr9x0u53PespfxfUqxP3UYWiE2a41gaofgNnC4Y8WQ= -github.com/minio/minio-go/v7 v7.0.87/go.mod h1:33+O8h0tO7pCeCWwBVa07RhVVfB/3vS4kEX7rwYKmIg= github.com/minio/minio-go/v7 v7.0.88 h1:v8MoIJjwYxOkehp+eiLIuvXk87P2raUtoU5klrAAshs= github.com/minio/minio-go/v7 v7.0.88/go.mod h1:33+O8h0tO7pCeCWwBVa07RhVVfB/3vS4kEX7rwYKmIg= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -311,10 +283,8 @@ github.com/nicksnyder/go-i18n/v2 v2.5.1 h1:IxtPxYsR9Gp60cGXjfuR/llTqV8aYMsC472zD github.com/nicksnyder/go-i18n/v2 v2.5.1/go.mod h1:DrhgsSDZxoAfvVrBVLXoxZn/pN5TXqaDbq7ju94viiQ= github.com/nsqio/go-nsq v1.1.0 h1:PQg+xxiUjA7V+TLdXw7nVrJ5Jbl3sN86EhGCQj4+FYE= github.com/nsqio/go-nsq v1.1.0/go.mod h1:vKq36oyeVXgsS5Q8YEO7WghqidAVXQlcFxzQbQTuDEY= -github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= -github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= -github.com/onsi/ginkgo/v2 v2.23.0 h1:FA1xjp8ieYDzlgS5ABTpdUDB7wtngggONc8a7ku2NqQ= -github.com/onsi/ginkgo/v2 v2.23.0/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM= +github.com/onsi/ginkgo/v2 v2.23.1 h1:Ox0cOPv/t8RzKJUfDo9ZKtRvBOJY369sFJnl00CjqwY= +github.com/onsi/ginkgo/v2 v2.23.1/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM= github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= @@ -342,18 +312,14 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/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.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA= -github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= 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.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= -github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= -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/prometheus/procfs v0.16.0 h1:xh6oHhKwnOJKMYiYBDWmkHqQPyiY40sny36Cmx2bbsM= +github.com/prometheus/procfs v0.16.0/go.mod h1:8veyXUu3nGP7oaCxhX6yeaM5u4stL2FeMXnCqhDthZg= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= github.com/quic-go/quic-go v0.50.0 h1:3H/ld1pa3CYhkcc20TPIyG1bNsdhn9qZBGN3b9/UyUo= @@ -412,22 +378,14 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 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= github.com/zeromicro/go-zero v1.8.1 h1:iUYQEMQzS9Pb8ebzJtV3FGtv/YTjZxAh/NvLW/316wo= github.com/zeromicro/go-zero v1.8.1/go.mod h1:gc54Ad4qt7OJ0PbKajnYsSKsZBYN4JLRIXKlqDX2A2I= github.com/zmexing/go-sensitive-word v1.3.0 h1:dB9S9kNklksOODGLLAov0RaVCwC2w9Kwxz6NZMdM6rk= github.com/zmexing/go-sensitive-word v1.3.0/go.mod h1:wkNIpkq1iPOe3l7l83zvnnV5mm20jfj2x8V8kjOTsUM= -go.etcd.io/etcd/api/v3 v3.5.18 h1:Q4oDAKnmwqTo5lafvB+afbgCDF7E35E4EYV2g+FNGhs= -go.etcd.io/etcd/api/v3 v3.5.18/go.mod h1:uY03Ob2H50077J7Qq0DeehjM/A9S8PhVfbQ1mSaMopU= go.etcd.io/etcd/api/v3 v3.5.19 h1:w3L6sQZGsWPuBxRQ4m6pPP3bVUtV8rjW033EGwlr0jw= go.etcd.io/etcd/api/v3 v3.5.19/go.mod h1:QqKGViq4KTgOG43dr/uH0vmGWIaoJY3ggFi6ZH0TH/U= -go.etcd.io/etcd/client/pkg/v3 v3.5.18 h1:mZPOYw4h8rTk7TeJ5+3udUkfVGBqc+GCjOJYd68QgNM= -go.etcd.io/etcd/client/pkg/v3 v3.5.18/go.mod h1:BxVf2o5wXG9ZJV+/Cu7QNUiJYk4A29sAhoI5tIRsCu4= go.etcd.io/etcd/client/pkg/v3 v3.5.19 h1:9VsyGhg0WQGjDWWlDI4VuaS9PZJGNbPkaHEIuLwtixk= go.etcd.io/etcd/client/pkg/v3 v3.5.19/go.mod h1:qaOi1k4ZA9lVLejXNvyPABrVEe7VymMF2433yyRQ7O0= -go.etcd.io/etcd/client/v3 v3.5.18 h1:nvvYmNHGumkDjZhTHgVU36A9pykGa2K4lAJ0yY7hcXA= -go.etcd.io/etcd/client/v3 v3.5.18/go.mod h1:kmemwOsPU9broExyhYsBxX4spCTDX3yLgPMWtpBXG6E= go.etcd.io/etcd/client/v3 v3.5.19 h1:+4byIz6ti3QC28W0zB0cEZWwhpVHXdrKovyycJh1KNo= go.etcd.io/etcd/client/v3 v3.5.19/go.mod h1:FNzyinmMIl0oVsty1zA3hFeUrxXI/JpEnz4sG+POzjU= go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= @@ -435,45 +393,26 @@ go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeH go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= 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/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= -go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= 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.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 h1:BEj3SPM81McUZHYjRS5pEgNgnmzGJ5tRpU5krWnV8Bs= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0/go.mod h1:9cKLGBDzI/F3NoHLQGm4ZrYdIHsvGt6ej6hUowxY0J4= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.34.0 h1:jBpDk4HAUsrnVO1FsfCfCOTEc/MkInJmvfCHYLFiT80= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.34.0/go.mod h1:H9LUIM1daaeZaz91vZcfeM0fejXPmgCYE8ZhzqfJuiU= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.35.0 h1:T0Ec2E+3YZf5bgTNQVet8iTDW7oIk03tXHq+wkwIDnE= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.35.0/go.mod h1:30v2gqH+vYGJsesLWFov8u47EpYTcIQcBjKpI6pJThg= -go.opentelemetry.io/otel/exporters/zipkin v1.34.0 h1:GSjCkoYqsnvUMCjxF18j2tCWH8fhGZYjH3iYgechPTI= -go.opentelemetry.io/otel/exporters/zipkin v1.34.0/go.mod h1:h830hluwAqgSNnZbxL2rJhmAlE7/0SF9esoHVLU04Gc= go.opentelemetry.io/otel/exporters/zipkin v1.35.0 h1:OAx1AdClqTB3pz+B4osLuGjx8kubys8ByW7yx0lF454= go.opentelemetry.io/otel/exporters/zipkin v1.35.0/go.mod h1:hz5wHI9hmCXzwkXFGZ05ObZw2Q2t/AeAZ18PExd2uSM= -go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= -go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= -go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -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/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= -go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= -go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= @@ -502,17 +441,11 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= -golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= -golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4= -golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs= -golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ= -golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8= golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ= golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -520,8 +453,6 @@ 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/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= -golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -543,12 +474,8 @@ golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= -golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= -golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -557,8 +484,6 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -581,8 +506,6 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -595,8 +518,6 @@ golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= -golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -611,12 +532,8 @@ golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -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/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -627,24 +544,16 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= -golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= 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-20250224174004-546df14abb99 h1:ilJhrCga0AptpJZXmUYG4MCrx/zf3l1okuYz7YK9PPw= -google.golang.org/genproto/googleapis/api v0.0.0-20250224174004-546df14abb99/go.mod h1:Xsh8gBVxGCcbV8ZeTB9wI5XPyZ5RvC6V3CTeeplHbiA= google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4 h1:IFnXJq3UPB3oBREOodn1v1aGQeZYQclEmvWRMN0PSsY= google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:c8q6Z6OCqnfVIqUFJkCzKcrj8eCvUrz+K4KRzSTuANg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250224174004-546df14abb99 h1:ZSlhAUqC4r8TPzqLXQ0m3upBNZeF+Y8jQ3c4CR3Ujms= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250224174004-546df14abb99/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c= google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= -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/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= @@ -693,24 +602,16 @@ gorm.io/plugin/dbresolver v1.5.3 h1:wFwINGZZmttuu9h7XpvbDHd8Lf9bb8GNzp/NpAMV2wU= gorm.io/plugin/dbresolver v1.5.3/go.mod h1:TSrVhaUg2DZAWP3PrHlDlITEJmNOkL0tFTjvTEsQ4XE= gorm.io/plugin/optimisticlock v1.1.3 h1:uFK8zz+Ln6ju3vGkTd1LY3xR2VBmMxjdU12KBb58PBA= gorm.io/plugin/optimisticlock v1.1.3/go.mod h1:S+MH7qnHGQHxDBc9phjgN+DpNPn/qESd1q69fA3dtkg= -k8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw= -k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y= k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls= k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k= -k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ= -k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/client-go v0.32.2 h1:4dYCD4Nz+9RApM2b/3BtVvBHw54QjMFUl1OLcJG5yOA= -k8s.io/client-go v0.32.2/go.mod h1:fpZ4oJXclZ3r2nDOv+Ux3XcJutfrwjKTCHz2H3sww94= k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU= k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg= -k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas= -k8s.io/kube-openapi v0.0.0-20250304201544-e5f78fe3ede9 h1:t0huyHnz6HsokckRxAF1bY0cqPFwzINKCL7yltEjZQc= -k8s.io/kube-openapi v0.0.0-20250304201544-e5f78fe3ede9/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0= @@ -725,14 +626,14 @@ modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8= modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= -modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI= -modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU= +modernc.org/memory v1.9.0 h1:smV8d5mrOAvj5QIYbc2XLSRWvAIyPI+kQHqxZaxEqCM= +modernc.org/memory v1.9.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= -modernc.org/sqlite v1.36.0 h1:EQXNRn4nIS+gfsKeUTymHIz1waxuv5BzU7558dHSfH8= -modernc.org/sqlite v1.36.0/go.mod h1:7MPwH7Z6bREicF9ZVUR78P1IKuxfZ8mRIDHD0iD+8TU= +modernc.org/sqlite v1.36.1 h1:bDa8BJUH4lg6EGkLbahKe/8QqoF8p9gArSc6fTqYhyQ= +modernc.org/sqlite v1.36.1/go.mod h1:7MPwH7Z6bREicF9ZVUR78P1IKuxfZ8mRIDHD0iD+8TU= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= @@ -742,8 +643,6 @@ sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk= -sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=