🚧 improve image encryption and decryption

This commit is contained in:
2025-03-22 13:05:50 +08:00
parent 3a03224f8c
commit 781a71a27c
39 changed files with 1274 additions and 977 deletions

View File

@@ -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)
}

View File

@@ -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 地址

View File

@@ -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-----

View File

@@ -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-----

View File

@@ -15,7 +15,9 @@ type Config struct {
AccessSecret string
}
Encrypt struct {
Key string
Key string
PublicKey string
PrivateKey string
}
Mysql struct {
DataSource string

View File

@@ -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)
}
}
}

View File

@@ -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",

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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
}

View File

@@ -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")
}

View File

@@ -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("分享已过期")
}
}
// 检查访问限制

View File

@@ -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,

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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{

View File

@@ -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{

View File

@@ -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
}

View File

@@ -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"`
}

View File

@@ -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"

View File

@@ -15,6 +15,7 @@ passwordNotMatch = "两次输入的密码不一致!"
passwordFormatError = "密码格式错误!"
resetPasswordError = "重置密码失败!"
loginSuccess = "登录成功!"
notPermission = "无权访问!"
[sms]
smsSendTooFrequently = "验证码发送过于频繁,请稍后再试!"

View File

@@ -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

View File

@@ -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()
}

View File

@@ -22,7 +22,6 @@ const (
const (
ImageCachePrefix = "image:cache:"
ImageRecentPrefix = "image:recent:"
ImageSinglePrefix = "image:single:"
ImageSharePrefix = "image:share:"
ImageShareVisitPrefix = "image:share:visit:"

View File

@@ -1,7 +1,6 @@
package constant
const (
Root string = "root"
Admin string = "admin"
User string = "user"
)

View File

@@ -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密钥部分

View File

@@ -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)
}

View File

@@ -0,0 +1,148 @@
/**
* hybrid_encrypt_js.js
* 前端混合解密实现与Go后端的hybrid_encrypt.go配合使用
*/
/**
* 使用RSA私钥和AES-GCM解密数据
* @param {ArrayBuffer} ciphertext - 加密的数据
* @param {CryptoKey} privateKey - RSA私钥
* @returns {Promise<ArrayBuffer>} - 解密后的数据
*/
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<CryptoKey>} - 导入的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<string>} - 解密后的图片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
};

View File

@@ -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
}

View File

@@ -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))
}

View File

@@ -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)
}

View File

@@ -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 = "<svg xmlns=\"xhttp://www.w3.org/2000/svg\" viewBox=\"0 0 231 231\">"
var svgEnd = "</svg>"
var env = "<path d=\"M33.83,33.83a115.5,115.5,0,1,1,0,163.34,115.49,115.49,0,0,1,0-163.34Z\" style=\"fill:#01;\"/>"
var head = "<path d=\"m115.5 51.75a63.75 63.75 0 0 0-10.5 126.63v14.09a115.5 115.5 0 0 0-53.729 19.027 115.5 115.5 0 0 0 128.46 0 115.5 115.5 0 0 0-53.729-19.029v-14.084a63.75 63.75 0 0 0 53.25-62.881 63.75 63.75 0 0 0-63.65-63.75 63.75 63.75 0 0 0-0.09961 0z\" style=\"fill:#000;\"/>"
// 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": "<path d=\"m141.74 195a114.93 114.93 0 0 1 37.912 16.45l0.07 0.05c-1.17 0.79-2.3601 1.55-3.5601 2.29a115.55 115.55 0 0 1-120.95 0.21q-2.0001-1.23-4.0002-2.54a114.79 114.79 0 0 1 38.002-16.5 116.21 116.21 0 0 1 15.791-2.49v-14.57c1.32 0.22 2.6501 0.39 4.0002 0.51 2.0001 0.19 4.0002 0.28 6.1202 0.29a64.333 64.33 0 0 0 8.8804-0.62c0.67003-0.09 1.3401-0.2 2.0001-0.31v14.69a118 118 0 0 1 15.741 2.54z\" style=\"fill:#fff;\"/><path d=\"m79.292 212a3.4601 3.46 0 0 0 3.8902 5.07 3.3801 3.38 0 0 0 2.1001-1.61 3.4701 3.47 0 0 0-1.2801-4.72 3.4201 3.42 0 0 0-2.6201-0.34 3.5101 3.51 0 0 0-2.0901 1.6zm60.122 0.46a3.4901 3.49 0 0 0 1.21 4.7h0.06a3.4601 3.46 0 0 0 4.7202-1.27l0.07-0.13a3.4601 3.46 0 0 0-1.34-4.6 3.4601 3.46 0 0 0-2.5801-0.32 3.5301 3.53 0 0 0-2.1001 1.61zm9.8004 5.7 5.8602 5.87c-1.39 0.5-2.7901 1-4.2102 1.44l-4.4802-4.47a7.5203 7.52 0 0 1-1.9401 0.81 7.8303 7.83 0 0 1-6.0002-0.79 7.8703 7.87 0 0 1-2.9201-10.69v-0.07a7.8903 7.89 0 0 1 10.77-2.88l0.12 0.07a7.8603 7.86 0 0 1 2.7901 10.62v0.07zm-37.701-2.36-9.5004 9.51v4.9c-1.35-0.16-2.6801-0.33-4.0002-0.54v-6l0.58002-0.58 10.1-10.09a7.8703 7.87 0 1 1 2.8401 2.86zm7.3203-5.91a3.4601 3.46 0 1 0-1.6101 2.1 3.3801 3.38 0 0 0 1.6101-2.1zm-29.741 7.82 3.0901 3.1 0.59002 0.59v7.36c-1.3401-0.26-2.6801-0.55-4.0002-0.87v-4.84l-2.5101-2.51a7.5203 7.52 0 0 1-1.9401 0.81 7.8803 7.88 0 1 1 1.9101-14.43 7.8703 7.87 0 0 1 2.8901 10.75z\" style=\"fill:#1a1a1a;\"/>",
"mouth": "<path d=\"m94.19 136.84h42.632a3.7801 3.78 0 0 1 3.7802 3.78v3.22a15.231 15.23 0 0 1-15.211 15.16h-19.781a15.251 15.25 0 0 1-15.221-15.16v-3.22a3.8002 3.8 0 0 1 3.7802-3.78z\" style=\"fill:#fff;stroke-linecap:round;stroke-linejoin:round;stroke-width:3px;stroke:#1a1a1a;\"/><path d=\"m130.96 136.84v21.16m-30.911-21.16v21.16m10.34-21.16v22.16m10.31-22.2v22.2\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:3px;stroke:#1a1a1a;\"/>",
"eyes": "<path d=\"m83.739 83.92h63.533a19.101 19.1 0 0 1 19.051 19 19.111 19.11 0 0 1-19.051 19h-63.533a19.091 19.09 0 0 1-19.001-19 19.091 19.09 0 0 1 19.001-19z\" style=\"fill:#1a1a1a;\"/><path d=\"m140.23 93.54a9.3804 9.38 0 1 0 9.3804 9.38 9.3804 9.38 0 0 0-9.3804-9.38zm-49.402 0a9.3804 9.38 0 1 0 9.3804 9.38 9.3904 9.39 0 0 0-9.3804-9.38z\" style=\"fill:#e6e7e8;\"/><rect x=\"79.795\" y=\"98.627\" width=\"71.471\" height=\"8.5859\" ry=\"4.2929\" style=\"fill:#b3b3b3;\"/>",
"top": "<path d=\"m32.902 67.662c-0.36295 1.7227-6.2342 30.695 5.6133 52.596 4.5843 8.4743 9.0081 13.239 12.75 15.893a67.7 67.7 0 0 1-3.4688-21.35 67.7 67.7 0 0 1 2.332-17.658c-4.4914-2.4646-10.868-6.9012-13.834-13.52-4.1626-9.285-3.6155-14.673-3.3926-15.961zm165.19 0c0.22292 1.2882 0.77005 6.6759-3.3926 15.961-2.9664 6.6183-9.3426 11.055-13.834 13.52a67.7 67.7 0 0 1 2.332 17.658 67.7 67.7 0 0 1-3.4688 21.35c3.7419-2.6532 8.1657-7.4183 12.75-15.893 11.847-21.9 5.9762-50.873 5.6133-52.596z\" style=\"fill:#fff;\"/><path d=\"m115.73 13.191c-7.3787-0.13351-13.509 5.7888-13.631 13.168-0.10128 5.8827 3.4508 10.518 8.0566 12.52 1.061 0.46115 2.1869 0.78009 3.3418 0.95703v8.4291c0.66778-0.02035 1.3358-0.03077 2.0039-0.03125 0.66547-9e-5 1.3309 0.0097 1.9961 0.0293v-8.4115c2.6002-0.38406 5.1586-1.5484 7.3086-3.625 4.2322-4.0878 4.9991-9.8755 3.1582-14.549-1.8407-4.6726-6.3502-8.3834-12.232-8.4863z\" style=\"fill:#fff;\"/>",
}
// Girl
sp[1] = map[string]string{
"clo": "<path d=\"m141.75 195a114.79 114.79 0 0 1 38 16.5 115.53 115.53 0 0 1-128.46 0 114.79 114.79 0 0 1 38-16.5c0 10.76 11.75 19.48 26.25 19.48s26.25-8.72 26.25-19.48z\" style=\"fill:#1a1a1a;\"/><path d=\"m92.502 194.27v0.70391c0 4.3033 2.4373 8.2583 6.3807 11.183 4.2199 3.1204 10.106 5.0508 16.661 5.0508 6.548 0 12.434-1.9303 16.654-5.0508 3.9434-2.9245 6.388-6.8795 6.388-11.183v-0.67489c1.0768 0.21771 2.1463 0.44994 3.2158 0.69666h-7e-3c1.0695 0.24672 2.1318 0.50798 3.1867 0.791-0.27648 6.103-3.6524 11.553-8.9708 15.493-5.2821 3.9114-12.521 6.328-20.466 6.328-7.9449 0-15.184-2.4165-20.474-6.328-5.333-3.9477-8.7089-9.4194-8.9708-15.544 1.055-0.27577 2.1099-0.53702 3.1722-0.78376 1.0695-0.23947 2.1463-0.46443 3.2304-0.68213z\" style=\"fill:#b3b3b3;\"/>",
"mouth": "<path d=\"m100.35 143.85a7.67 7.67 0 0 0 7.58 7.7v0a7.66 7.66 0 0 0 7.57-7.7 7.66 7.66 0 0 0 7.57 7.7v0a7.67 7.67 0 0 0 7.58-7.7\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:6.3998px;stroke:#333;\"/>",
"eyes": "<path d=\"m78.73 111a10.9 10.9 0 0 1 15.19 0m43.16 0a10.9 10.9 0 0 1 15.19 0\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:6.1999px;stroke:#333;\"/><path d=\"m79.804 123.74h7.07m57.273 0h7.05\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:5.9998px;stroke:#b8b8b8;\"/>",
"top": "<path d=\"m57.534 142.03c-6.9383-31.75-0.57294-52.577 14.174-62.344 22.562-12.283 62.082-12.222 83.484-1.8846 21.348 11.177 22.124 37.396 18.498 63.733 8.1279-14.155 13.164-31.598 14.085-48.902 1.0828-11.795-1.1756-18.866-7.4833-27.972-26.465-37.685-103.45-31.56-129.66-2.8372-7.8504 9.4615-9.6006 17.478-9.275 26.667 1.0024 18.667 6.9688 38.508 16.18 53.54z\" style=\"fill:#b3b3b3;\"/><path d=\"m111.26 3.0423c-6.013 0.1128-12.629 2.6924-15.291 7.9082-1.1676 3.2383-1.6758 6.2069-1.6758 8.8926 0.89228-0.2661 1.8005-0.5164 2.7266-0.7441 3.7502-1.0672 7.4851-1.7135 11.129-1.9981 1.1007-0.086 2.1953-0.1391 3.2773-0.1601h2e-3c5.6969-0.1133 11.09 0.6603 15.904 2.0527 0.0552-3.042-0.70696-5.9824-2.1738-8.5-1.8411-3.1599-4.7033-5.5568-8.4297-6.8262-1.6883-0.4952-3.5163-0.662-5.4688-0.625zm3.0664 17.449c-0.69317-0.01-1.3919-0.01-2.0938 0h-2e-3c-1.1591 0.019-2.3326 0.064-3.5117 0.1386-3.9035 0.246-7.9025 0.8061-11.92 1.7285-15.159 3.0075-26.469 9.9279-22.068 19.682 22.891-8.7773 52.315-10.403 76.023-2.2129 2.1414-9.5529-14.939-19.081-36.428-19.34z\" style=\"fill:#b3b3b3;\"/><path d=\"m165.62 16.981c-0.8575 0-1.9406 0.54389-3.3476 1.3574-7.3382 4.7652-13.452 10.867-19.516 18.363 9.2734 2.1825 17.903 5.6706 25.213 10.604 1.1512-9.1263 1.9137-18.503 0.055-26.996-0.57-2.4184-1.3017-3.3267-2.4043-3.3281zm-104.09 1.6934c-1.1026 0-1.8342 0.91165-2.4043 3.3301-1.8794 8.5869-1.0806 18.078 0.092 27.299 7.0559-4.6638 15.687-8.3667 25.111-10.984-6.043-7.4601-12.139-13.537-19.451-18.285-1.407-0.81353-2.4901-1.3605-3.3477-1.3594z\" style=\"fill:#b3b3b3;\"/><path d=\"m162.45 16.686c-2.3175 2e-3 -4.6276 0.57608-6.8926 1.668-8.4768 6.0155-11.113 13.349-10.133 19.787 10.323 2.7077 19.762 7.0658 27.346 13.279 9.848-4.9363 11.32-17.137 4.6152-25.852-4.7104-6.1222-9.8371-8.8878-14.936-8.8828zm-97.318 4.1387c-2.4569 0.0556-5.1642 0.54474-8.1172 1.5176-13.487 4.4433-19.06 21.215-3.6484 31.84 7.2476-6.0694 16.961-10.896 27.892-14.229 0.2193-3.3241-0.3201-7.0817-1.8691-11.236-2.8049-4.8445-7.2233-7.721-13.221-7.8906-0.3408-0.01-0.6861-0.01-1.0371-2e-3z\" style=\"fill:#b3b3b3;\"/>",
}
// Blonde
sp[2] = map[string]string{
"clo": "<path d=\"m141.75 195a114.79 114.79 0 0 1 38 16.5 115.53 115.53 0 0 1-128.46 0 114.79 114.79 0 0 1 38-16.5c0 10.76 11.75 19.48 26.25 19.48s26.25-8.72 26.25-19.48z\" style=\"fill:#5a5a5a;\"/>",
"mouth": "<path d=\"m115.5 161.71c-8.24 0-14.46-4.15-19.19-11.25 3.37-2.44 6.51-4.57 10-6.79a5.25 5.25 0 0 1 5.48-0.17 28.19 28.19 0 0 1 3.68 2.75 28.19 28.19 0 0 1 3.68-2.75 5.25 5.25 0 0 1 5.48 0.17c3.52 2.22 6.66 4.35 10 6.79-4.74 7.1-11 11.25-19.19 11.25z\" style=\"fill:#5a5a5a;\"/>",
"eyes": "<path d=\"m172.7 90.75h-6.54c-0.14-0.1-0.26-0.22-0.4-0.3-4.48-2.76-22.75-2.11-33.71 1.2-1 0.3-1.91 0.61-2.75 0.94-1.8937 0.79244-3.8739 1.3597-5.9 1.69-5.5051 0.79002-10.403 0.79002-15.908 0-2.0261-0.33034-4.0063-0.89756-5.9-1.69-0.84-0.33-1.76-0.64-2.75-0.94-11-3.31-29.23-4-33.71-1.2-0.13832 0.08869-0.2688 0.18906-0.39 0.3h-6.55c-1.1046 0-2 0.89543-2 2v4.66c-0.0013 0.98185 0.49088 1.8986 1.31 2.44l1.9 1.27c0.59238 0.38889 0.93475 1.0622 0.9 1.77-0.14175 5.4854 0.88072 10.939 3 16 3.58 8.38 16 10.9 24.93 10.9 2.6976 0.0771 5.3921-0.2361 8-0.93 4.35-1.43 8.24-7.36 10.45-12.42 1.7607-3.8506 2.7493-8.009 2.91-12.24 7.3e-4 -0.7138 0.38183-1.3731 1-1.73 3.2281-1.951 6.5798-1.951 9.8079 0 0.61817 0.3569 0.99927 1.0162 1 1.73 0.16067 4.231 1.1493 8.3894 2.91 12.24 2.21 5.06 6.1 11 10.45 12.42 2.6079 0.6939 5.3024 1.0071 8 0.93 8.92 0 21.35-2.52 24.93-10.9 2.1193-5.0614 3.1418-10.515 3-16-0.0348-0.70778 0.30762-1.3811 0.9-1.77l1.9-1.27c0.81913-0.54136 1.3113-1.4582 1.31-2.44v-4.6c0.0336-1.1048-0.83521-2.0274-1.94-2.06z\" style=\"fill:#1a1a1a;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.5;stroke:#b3b3b3;\"/>",
"top": "<path d=\"m124.22 13.61c-19.783 0-36.945 8.0887-39.695 24.106-15.332 0.23539-31.831 2.7712-41.663 15.782-6.0238 7.9604-7.0402 19.901-6.8476 31.724 0.46007 28.503 10.742 64.228-4.3012 89.714 16.584 5.7777 43.086 10.742 73.59 11.662v-8.6558c-1.851-0.35308-3.6592-0.78105-5.4353-1.2732-30.953-8.4632-50.672-36.635-47.259-68.669 1.5514-10.603 4.6221-19.665 10.025-27.69 5.3818-7.9925 13.267-15.717 23.892-21.41 0.40658 0.72757 1.9901 3.5843 2.4074 4.3012 7.5003 12.775 17.986 23.849 33.157 26.866 12.433 2.4609 23.849 3.4666 36.346 1.1555 4.2584-0.78106 10.667-2.3967 14.851-2.4181 14.861 33.404-1.0806 75.035-40.668 87.457-2.2255 0.70616-4.5258 1.316-6.8904 1.8189 0 2.707-0.0428 5.6493-0.0642 8.5274 23.603-0.72757 48.682-4.0444 72.874-11.234-18.521-32.152 0.81315-89.083-10.036-121.46-9.0731-26.973-38.85-40.315-64.282-40.305z\" style=\"fill:#c5c5c5;\"/><path d=\"m33.147 172.32c-2.6535 5.1143-6.088 9.9504-10.1 12.411 7.8427 10.453 17.387 19.516 28.257 26.781 16.038-10.731 35.629-17.055 54-18.606v-9.0089c-30.065-0.94155-56.108-5.8847-72.157-11.577zm164.06 0.55637c-23.731 7.0723-48.361 10.325-71.525 11.042-0.0321 3.1242-0.0535 6.2377-0.0107 9.0517 19.227 1.7226 37.908 7.8534 53.989 18.542 0.0107 0 0.0107 0 0.0214 0.0107 10.731-7.1686 20.179-16.081 27.958-26.374-4.2798-2.3967-7.832-6.9653-10.432-12.272z\" style=\"fill:#c5c5c5;\"/><path d=\"m50.02 46.5c-2.9297 1.9143-6.1313 3.8826-10.154 7.9805-14.091 14.359-16.145 27.701-6.1406 44.018 4.2049 6.8583 6.1414 13.706-0.24609 20.5-7.7143 8.1957-21.559 4.2912-21.537 16.061 0.0214 8.613 15.063 7.9178 22.531 13.984 3.7662 3.0707 5.0836 8.3992 2.0664 12.508-4.2156 5.7456-16.006 7.3715-22.629 8.9336 5.8811 10.843 13.45 20.638 22.355 29.033l0.0039 0.0234 0.0059-0.0137c2e-3 2e-3 0.0038 4e-3 0.0059 6e-3 0.0034-0.0112 0.0063-0.0219 0.0098-0.0332 14.775-12.218 20.268-20.965 49.461-28.434-17.404-10.258-30.68-27.122-24.143-35.34 4.4123-5.5444 5.6612-7.8633 6.4062-12.078 2.3582-13.339-10.208-22.335-9.2363-32.715 1.9432-8.2346 11.379-11.173 16.947-15.115 5.4577-3.9082 9.8014-8.7695 10.799-16.918-13.558-4.8896-17.609-5.8617-36.506-12.4zm140.87 19.357c-3.4404-0.91243-23.311 122.43 4.4121 133.14 8.9661-8.5809 16.552-18.584 22.404-29.658 0-0.31029-25.133-3.9922-25.979-14.018-0.10699-1.1769 0.11822-1.4855 0.86718-2.502 6.6764-9.2122 30.716-11.416 29.646-23.496-0.27818-3.1563-4.1617-5.2334-6.7402-6.4531-12.155-5.767-32.942-9.6494-15.031-24.543 9.2122-7.3505 10.43-8.4323 0.59766-14.691-9.4583-6.0238-9.394-11.993-9.7578-16.326-0.0767-0.93035-0.22089-1.4003-0.41992-1.4531z\" style=\"fill:#c5c5c5;\"/><path d=\"m133.83 39.909c-11.33 1.393-9.5492 16.204-2e-3 16.643-4.5102 10.717 9.0165 16.181 14.441 8.3125 6.562 8.6765 18.596 0.94751 14.457-8.3125 11.718-1.5381 9.2769-16.099 0-16.643 4.503-10.867-9.4883-16.101-14.457-8.3301-6.8832-9.0411-18.509-0.47321-14.439 8.3301z\" style=\"fill:#333;\"/><path d=\"m153.86 48.222c0-3.0528-2.5184-5.5648-5.5791-5.5648-3.0783 0-5.5793 2.512-5.5793 5.5648 0 3.0703 2.501 5.5648 5.5793 5.5648 3.0606 0 5.5791-2.4946 5.5791-5.5648z\" style=\"fill:#f9f9f9;\"/>",
}
// Guy
sp[3] = map[string]string{
"clo": "<path d=\"m141.75 195c13.563 3.1499 26.439 8.7409 38 16.5-38.873 26.001-89.587 26.001-128.46 0 11.561-7.7591 24.437-13.35 38-16.5 8.4869 8.8011 26.21 25.619 26.21 25.619s17.603-16.972 26.25-25.619z\" style=\"fill:#d6d6d6;\"/><path d=\"m109 230.81 1.6836-14.33h9.6328l1.6836 14.33c-2.16 0.12-4.33 0.19-6.51 0.19s-4.35-0.07-6.51-0.19z\" style=\"fill:#5e5e5e;\"/><path d=\"m124.17 210.6h-17.349v5.53a3.8828 3.29 0 0 0 3.8828 3.29h9.583a3.8828 3.29 0 0 0 3.8828-3.29z\" style=\"fill:#535353;\"/><path d=\"m140.57 190.36-25.066 20.245c5.9686 3.2455 11.597 7.0814 16.8 11.45 1.5989 1.3338 3.9762 1.1189 5.31-0.48 0.21005-0.25749 0.38802-0.53956 0.52999-0.84l10.826-23.805-4-6c-0.90256-1.351-2.7298-1.7137-4.08-0.81-0.11612 0.0786-0.22641 0.16549-0.33 0.26z\" style=\"fill:#c6c6c6;\"/><path d=\"m90.434 190.36 25.066 20.245c-5.9686 3.2455-11.597 7.0814-16.8 11.45-1.5989 1.3338-3.9762 1.1189-5.31-0.48-0.21005-0.25749-0.38802-0.53956-0.52999-0.84l-10.826-23.805 4-6c0.90256-1.351 2.7298-1.7137 4.08-0.81 0.11612 0.0786 0.22641 0.16549 0.33 0.26z\" style=\"fill:#c6c6c6;\"/>",
"mouth": "<path d=\"m136.21 147.09a21.77 21.77 0 0 1-40.13 0z\" style=\"fill:#fff;stroke-linecap:round;stroke-linejoin:round;stroke-width:3.4999px;stroke:#000;\"/>",
"eyes": "<path d=\"m145.39 104.7-11.52 11.2h17.26m-65.52-11.2 11.52 11.2h-17.26\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:5.4998px;stroke:#000;\"/>",
"top": "<path d=\"m43.891 77.836c-5.1124 28.237 2.1347 61.004 24.792 81.332-6.2362-12.503-9.5362-33.948-9.4887-45.458-0.50203-37.473 41.439-46.335 56.149-17.614 18.8-31.2 52.825-16.872 54.062 13.714 0.56018 13.844-0.43568 25.598-7.0962 48.966 18.372-12.47 28.012-53.959 23.545-80.941-47.486-2.2552-94.831-2.5724-141.96 0z\" style=\"fill:#1a1a1a;\"/><path d=\"m111.26 12.782c-18.508 0.0791-32.594 3.6163-32.594 3.6163 24.513 5.6002 32.807 10.504 31.743 19.835-0.87227 9.702-11.092 10.875-20.811 11.554-5.2548 0.36414-10.949 0.71523-16.391 1.7525-11.862 2.2818-19.946 4.3736-24.447 11.956-1.7012 2.8662-3.7945 10.428-4.8689 16.34h141.96c-5.7242-38.563-32.557-65.073-74.595-65.054z\" style=\"fill:#1a1a1a;\"/><path d=\"m73.292 44.77c-11.788 2.2816-18.923 5.5444-23.394 13.126-2.8484 6.7586-4.8454 13.238-6.0072 19.939h141.96c-1.9772-14.576-6.8677-28.248-19.277-32.098-28.834-6.3308-63.774-6.3553-93.285-0.96761z\" style=\"fill:#1a1a1a;\"/><path d=\"m165.95 35.642c-11.178 21.829-91.89 19.36-103.98 2.3011-9.703 12.267-15.605 25.883-18.079 39.892h141.96c-3.0096-17.158-9.7424-32.688-19.902-42.193z\" style=\"fill:#1a1a1a;\"/>",
}
// Country
sp[4] = map[string]string{
"clo": "<path d=\"m141.75 195a114.79 114.79 0 0 1 38 16.5 115.53 115.53 0 0 1-128.46 0 114.79 114.79 0 0 1 38-16.5l15.71 15.75h21z\" style=\"fill:#949494;\"/><path d=\"m115.45 211.34-10.55 10.54a2.51 2.51 0 0 1-3.5599 0 2 2 0 0 1-0.26999-0.30994l-18.48-25.4 5.8901-5.8899a2.52 2.52 0 0 1 3.5199-0.0791l23.49 21.14z\" style=\"fill:#c0c0c0;\"/><path d=\"m115.45 211.34 10.55 10.54a2.51 2.51 0 0 0 3.5599 0 2 2 0 0 0 0.26999-0.30994l18.48-25.4-5.8901-5.8899a2.52 2.52 0 0 0-3.4699-0.089l-23.49 21.14z\" style=\"fill:#c0c0c0;\"/><path d=\"m158.41 199.58-10.11-3.2401v29.93q5.1601-1.5299 10.11-3.51zm-75.82 26.66v-29.9l-10.1 3.2401v23.14c3.2901 1.3199 6.67 2.4999 10.1 3.5199z\" style=\"fill:#7c7c7c;\"/>",
"mouth": "<path d=\"m118.05 148.38c-1.5064 0.59192-2.595 2.0264-2.6191 3.9863-0.0574 1.3977 0.53421 3.5611 3.6758 5.7949 8.0544 4.9446 21.507 3.6862 21.255-7.1658-4.664 4.8219-10.021 5.6377-14.773 0.73907-1.2328-1.1599-2.3694-2.4032-3.9294-3.1408-1.0946-0.50424-2.2257-0.61071-3.6096-0.21337z\" style=\"fill:#333;\"/><path d=\"m133.61 154.93c3.0731-0.48816 5.5702-2.8457 5.4438-4.5059-0.47801-4.8311-5.7317-3.0917-4.3369-0.31405-2.8103-1.4445-1.8343-3.8862 0.50427-4.7324 2.0509-0.79942 5.0937 0.34314 6.2002 2.6376 2.2229 7.3422-3.4376 11.68-10.384 12.561z\" style=\"fill:#333;\"/><path d=\"m112.81 148.38c1.5064 0.59192 2.595 2.0264 2.6191 3.9863 0.0574 1.3977-0.53421 3.5611-3.6758 5.7949-8.0544 4.9446-21.507 3.6862-21.255-7.1658 4.664 4.8219 10.021 5.6377 14.773 0.73907 1.2328-1.1599 2.3694-2.4032 3.9294-3.1408 1.0946-0.50424 2.2257-0.61071 3.6096-0.21337z\" style=\"fill:#333;\"/><path d=\"m97.252 154.93c-3.0731-0.48816-5.5702-2.8457-5.4438-4.5059 0.47801-4.8311 5.7317-3.0917 4.3369-0.31405 2.8103-1.4445 1.8343-3.8862-0.50427-4.7324-2.0509-0.79942-5.0937 0.34314-6.2002 2.6376-2.2229 7.3422 3.4376 11.68 10.384 12.561z\" style=\"fill:#333;\"/>",
"eyes": "<path d=\"m131.64 114.09 7.5801-7.5801 7.5801 7.5801m-62.6 0 7.5801-7.5801 7.5799 7.5801\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:6.4998px;stroke:#000;\"/>",
"top": "<path d=\"m137.38 11.148c-12.23 1.9593-18.511 14.606-43.436 9.4915-11.285-3.2054-16.406-3.573-20.389 0.58594-4.1548 4.3384-7.033 12.435-9.8184 21.706-2.1354 7.4136-3.7187 14.381-4.7461 21.646h112.7c-3.4878-24.293-10.822-43.281-25.182-51.061-3.5314-1.623-6.5274-2.2959-9.1289-2.3613z\" style=\"fill:#b3b3b3;\"/><path d=\"m114.37 43.383c-19.445 0.088-38.524 2.0724-52.379 5.6992-1.2766 4.5795-2.4317 10.169-3.2285 16.807h113.11c-0.83731-6.0107-1.9164-11.674-3.3184-16.924-15.229-3.8842-34.873-5.6693-54.18-5.582z\" style=\"fill:#e6e6e6;\"/><path d=\"m115.5 55.773c-58.39 0-105.73 15.476-105.73 34.57h0.0312c0 11.295 16.496 21.319 42.126 27.627-0.10331-7.7704 2.788-21.904 5.2734-31.031 6.0935-1.7168 6.9294-1.8971 13.167-2.9919 14.874-2.8256 29.99-4.2037 45.133-4.1153 15.143-0.0884 30.259 1.2897 45.133 4.1153 6.2372 1.0947 7.2065 1.2751 13.3 2.9919 2.4854 9.1267 5.3768 23.26 5.2734 31.031 25.63-6.3082 41.993-16.332 41.993-27.627h0.0312c0-19.093-47.34-34.57-105.73-34.57z\" style=\"fill:#818181;\"/><path d=\"m72.088 83.533c-6.9765 1.1147-13.357 2.856-18.439 4.3477-1.1861 7.415-2.0038 18.858-1.8926 26.293 4.3278-0.62795 10.155-1.3644 13.295-1.6465-0.40554 0.30198 2.7344-17.827 7.0371-28.994zm86.824 0c4.3028 11.167 7.4426 29.296 7.0371 28.994 3.1396 0.28213 8.9671 1.0185 13.295 1.6465 0.11119-7.4351-0.70652-18.878-1.8926-26.293-5.0822-1.4916-11.463-3.2329-18.439-4.3477z\" style=\"fill:#434343;\"/>",
}
// Geeknot
sp[5] = map[string]string{
"clo": "<path d=\"m141.75 194.98a114.79 114.78 0 0 1 38 16.498 115.53 115.52 0 0 1-128.46 0 114.79 114.78 0 0 1 38-16.498l15.71 15.748h21z\" style=\"fill:#d2d2d2;\"/><path d=\"m70 200.88v20.77c-2.22-0.95325-4.3999-1.9698-6.5399-3.0496h-0.10088v-14.621c2.17-1.1 4.39-2.1399 6.64-3.0996z\" style=\"fill:#505050;\"/><path d=\"m161 200.88v20.77c1.9-0.80986 3.7702-1.6798 5.6201-2.5898l0.0989-0.0494 0.82005-0.40997h0.10088v-14.621c-2.17-1.1-4.39-2.1399-6.6402-3.0996z\" style=\"fill:#505050;\"/><polygon transform=\"matrix(1 0 0 .99987 4e-5 -3e-5)\" points=\"97.32 201.93 115.5 223.72 133.68 201.93\" style=\"fill:#171717;\"/><path d=\"m111.2 230.88 1.31-16.908c0.32992 1.2798 5.6399 1.2798 5.9999 0l1.3201 16.938c-1.4301 0.0494-2.8601 0.089-4.3 0.089s-2.87 0-4.3-0.089z\" style=\"fill:#171717;\"/><path d=\"m115.49 201.79v0.0692l-7.55 12.678-7.0001 11.809-19.19-26.487c0.60999-0.42995 1.22-0.89985 1.8001-1.3899a52 51.993 0 0 0 10.07-10.619l21.79 13.878z\" style=\"fill:#ebebeb;\"/><path d=\"m149.24 199.86-19.08 26.517-7.0001-11.809-7.57-12.678-0.0593-0.10086 21.94-13.998a52.21 52.203 0 0 0 10.08 10.699c0.58013 0.47009 1.1502 0.92002 1.7301 1.3399z\" style=\"fill:#ebebeb;\"/>",
"mouth": "<path d=\"m122.83 151.88a10.49 10.489 0 0 1-14.66 0\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:6.1996px;stroke:#333;\"/>",
"eyes": "<path d=\"m70.959 94.985h35.031c2.4086 1e-5 4.3612 1.9523 4.3612 4.3606l-2.5864 17.511c-0.3515 2.3799-1.7218 4.3606-3.8457 4.3606h-30.9c-2.1239-1e-5 -3.8457-1.9523-3.8457-4.3606l-2.5864-17.511c1e-5 -2.4082 1.9526-4.3606 4.3612-4.3606z\" style=\"fill:#1a1a1a;stroke-linecap:round;stroke-linejoin:round;stroke-width:3.0045px;stroke:#333;\"/><path d=\"m160.05 94.985h-35.031c-2.4086 1e-5 -4.3612 1.9523-4.3612 4.3606l2.5864 17.511c0.35149 2.3799 1.7218 4.3606 3.8457 4.3606h30.9c2.1239-1e-5 3.8457-1.9523 3.8457-4.3606l2.5864-17.511c-1e-5 -2.4082-1.9526-4.3606-4.3612-4.3606z\" style=\"fill:#1a1a1a;stroke-linecap:round;stroke-linejoin:round;stroke-width:3.0045px;stroke:#333;\"/><path d=\"m90.607 102.35a4.6337 4.6332 0 1 0 4.6892 4.6337 4.6337 4.6332 0 0 0-4.6892-4.6337zm49.72 0a4.6337 4.6332 0 1 0 4.6444 4.6337 4.6337 4.6332 0 0 0-4.6444-4.6337z\" style=\"fill:#1a1a1a;\"/><path d=\"m70.66 94.985h-11.775\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:3.0045px;stroke:#333;\"/><path d=\"m172.13 94.985h-19.484\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:3.0045px;stroke:#333;\"/><path d=\"m109.32 106.2c4.2045-2.427 9.3036-1.913 12.353-0.0258\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:3.0045px;stroke:#333;\"/><path d=\"m148.33 109.79-5.7626-8.2324\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:4;stroke:#fff;\"/><path d=\"m156.27 105-2.403-3.4328\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:4;stroke:#fff;\"/><path d=\"m82.748 114.34-8.9489-12.784\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:4;stroke:#fff;\"/><path d=\"m91.408 109.79-5.7626-8.2324\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:4;stroke:#fff;\"/>",
"top": "<path d=\"m41.835 75.131c-2.8674 12.582 1.2304 27.241 6.0238 39.031 0.25861 0.63658 0.51208 1.3075 0.79989 1.9683 0.71726 1.658 2.1184 3.9751 3.0038 3.9266 0.56895-0.0312 0.71637-1.5512 1.0228-3.1562 2.1988-19.097 8.8981-27.915 15.636-38.107 2.8783-4.0645 3.8616-7.2293 1.0644-9.9325-6.3236-3.5596-14.924-2.8574-21.367-0.67406-3.2312 1.4765-5.2427 3.4773-6.1842 6.9439zm125.65-8.5679c7.65-0.70616 19.714-0.1307 21.694 8.5679 1.455 6.4083 0.26915 17.747-1.0542 24.579-1.1961 5.3203-3.8066 14.231-7.8782 19.75-0.5565 0.44544-0.96888 0.13656-1.4159-1.1606-0.90692-3.0353-1.4298-7.8372-2.2556-10.727-3.4822-12.79-8.2195-21.875-14.429-29.94-5.5782-6.8415-4.2152-9.7207 5.3393-11.069z\" style=\"fill:#4d4d4d;\"/><path d=\"m112.27 73.826c-18.585-7.5217-34.987-14.797-48.939 5.018-4.9752 7.083-3.7876 8.8056-4.9217 0.0749-1.637-12.476-4.7505-34.174 1.9259-45.194 7.6822-12.7 19.323-13.128 31.039-5.3818 10.796 7.7784 24.277 14.647 38.015 12.219 12.732-2.2576 15.835-7.7464 15.707-19.912-0.0215-2.6-0.0963-5.2106-0.2033-7.7999 13.631 3.9267 24.609 14.776 26.513 29.049 0.88804 6.6336 0.26749 12.722-1.9259 19.013-5.9702 17.108-30.119 20.896-45.74 16.841-3.9588-1.0378-7.6822-2.4181-11.47-3.9267z\" style=\"fill:#4d4d4d;\"/>",
}
// Asian
sp[6] = map[string]string{
"clo": "<path d=\"m115.5 231a115 115 0 0 0 64.23-19.5 114.79 114.79 0 0 0-38-16.5l-2.41-9a125.19 125.19 0 0 0-13.32-2.28v8.75q3.52 0.32 7 0.84l-17.5 17.48-17.5-17.48q3.45-0.52 7-0.84v-8.75a125.55 125.55 0 0 0-13.34 2.28l-2.41 9a114.79 114.79 0 0 0-38 16.5 114.94 114.94 0 0 0 64.25 19.5z\" style=\"fill:#646464;\"/><path d=\"m132.98 193.33-36.185 36.155-2.4-0.42 36.108-36.081z\" style=\"fill:#e3e3e3;\"/>",
"mouth": "<path d=\"m127.84 146.73c-2.24 8.93-6.92 15.08-12.34 15.08s-10.1-6.15-12.34-15.08z\" style=\"fill:#fff;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.9999px;stroke:#1a1a1a;\"/>",
"eyes": "<path d=\"m129.31 114.14 20-5.37m-47.66 5.37-20-5.37\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:4.9998px;stroke:#1a1a1a;\"/>",
"top": "<path d=\"m169.65 90.998c3.137 11.94 4.9371 36.484-3.4118 58.213l5.129 3.1164c10.044-15.199 14.959-39.163 13.943-61.33z\" style=\"fill:#1a1a1a;\"/><path d=\"m45.081 90.989c-0.88085 4.9304-0.87534 14.953-0.15027 21.75 2.1318 19.98 16.671 42.505 16.671 42.505l5.7352-4.4331s-13.244-31.348-6.0571-52.751c0.52108-1.5517 0.95592-2.916 1.3462-4.1835z\" style=\"fill:#1a1a1a;\"/><path d=\"m117 3.4883c-8.2136-0.19887-19.13 7.933-18.494 9.3516 1.6214 3.6186 11.176 22.55 11.889 23.963h10.148c2.6022-6.3102 11.32-26.531 11.32-26.531s-4.1382-4.138-12.416-6.4375c-0.77605-0.21556-1.5976-0.32513-2.4473-0.3457z\" style=\"fill:#1a1a1a;\"/><path d=\"m115.95 4.5428c-3.1563 0-6.3123 0.57462-9.2165 1.715-5.8084 2.2817-10.532 6.808-12.779 12.245v-5e-3c-1.8166 4.397-2.0233 9.3441-0.58058 13.857 0.69352 2.1687 1.7693 4.2296 3.1533 6.0968h38.893c0.71032-0.95769 1.3441-1.9641 1.8787-3.0144 2.6811-5.2673 2.9296-11.542 0.67253-16.975-2.257-5.4337-6.9893-9.9522-12.802-12.224-2.9064-1.1335-6.0633-1.6987-9.2196-1.6956z\" style=\"fill:#1a1a1a;\"/><path d=\"m92.512 28.125c0.13387 1.4318 0.41877 2.8511 0.85962 4.2306 1.4429 4.5127 4.5278 8.5654 8.6411 11.353 4.1135 2.7873 9.2311 4.2913 14.336 4.2165 5.1052-0.0764 10.168-1.7333 14.181-4.6419 2.8754-2.0834 5.2132-4.7932 6.7665-7.8447 1.2005-2.3586 1.9085-4.9188 2.127-7.5156-15.037-2.6407-31.421-3.4671-46.912 0.20253z\" style=\"fill:#b3b3b3;\"/><path d=\"m34.426 90.63c14.714 4.0779 22.683 6.4085 45.254 7.4257 2.5318-18.185 4.6689-28.672 10.023-38.352 3.2025 13.403 3.8346 25.22 2.9106 42.253l11.172-0.23161c1.4706-11.886 3.8989-29.213 2.1636-42.021 10.416 12.631 11.373 23.624 13.077 39.726 30.174-0.76004 59.808-4.5121 77.845-10.128-10.76-38.608-41.475-55.66-80.38-56.104-38.182-0.45134-74.543 22.405-82.065 57.432z\" style=\"fill:#1a1a1a;\"/>",
}
// Punk
sp[7] = map[string]string{
"clo": "<path d=\"m88.18 194.11c-4.2079 1.021-8.3545 2.2792-12.42 3.7695v26.072a115.5 115.5 0 0 0 79.48 0v-26.072c-4.0858-1.4904-8.2529-2.7486-12.48-3.7695v8.7051c0 9.3888-7.6112 17-17 17h-20.58c-9.3888 0-17-7.6112-17-17v-8.7051z\" style=\"fill:#efefef;\"/>",
"mouth": "<polygon points=\"121.61 160.74 109.39 160.74 115.5 171.31\" style=\"fill:#797979;\"/><path d=\"m132.64 144.06a34.42 34.42 0 0 1-34.24 0\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:5.9998px;stroke:#000;\"/>",
"eyes": "<path d=\"m170.25 100c1.69 9.62-4.79 29.23-22.4 29.23-6.81 0-15-3.66-20.23-10-4.34-5.33-7.56-12.87-6.2-19.45 1.63-7.89 7.07-11.45 14.67-12.92a68.16 68.16 0 0 1 12.52-1c10.77 0 19.78 3.61 21.64 14.22z\" style=\"fill:#565656;stroke-width:3.99px;stroke:#000;\"/><path d=\"m60.75 100c-1.69 9.62 4.79 29.23 22.4 29.23 6.81 0 15-3.66 20.23-10 4.34-5.33 7.56-12.87 6.2-19.45-1.63-7.89-7.07-11.45-14.67-12.92a68.16 68.16 0 0 0-12.52-1c-10.77 0-19.78 3.61-21.64 14.22z\" style=\"fill:#565656;stroke-width:3.99px;stroke:#000;\"/><line x1=\"100.2\" x2=\"130.8\" y1=\"87.92\" y2=\"87.92\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:3.99px;stroke:#000;\"/><path d=\"m109.87 101.73c0-2.59 2.52-4.69 5.63-4.69s5.63 2.1 5.63 4.69\" style=\"fill:none;stroke-width:3.99px;stroke:#000;\"/>",
"top": "<path d=\"m30.622 70.381c2.0971-3.9374 4.6649-7.9604 7.6822-12.037 3.0172-4.0765 6.0987-7.6929 9.2229-10.817l22.897 22.897c-4.4402 4.4403-8.2278 9.5439-11.213 15.14z\" style=\"fill:#999;\"/><path d=\"m160.58 70.423 22.907-22.897c3.1242 3.1242 6.2056 6.7406 9.2229 10.817 3.0065 4.0765 5.5744 8.0994 7.6715 12.037l-28.578 15.182c-2.9851-5.5958-6.7727-10.689-11.224-15.14z\" style=\"fill:#999;\"/><path d=\"m92.411 15.247c3.8197-0.87736 7.6715-1.5407 11.534-1.9794 4.0765-0.46007 7.9282-0.69546 11.555-0.69546 1.53 0 3.1563 0.0428 4.8682 0.1391l1.851 22.255 5.767-21.57c3.1028 0.37449 6.0666 0.86666 8.8912 1.4658l-10.55 49.763c-1.9259-0.41729-3.702-0.70617-5.3176-0.87736-1.423-0.14979-3.2633-0.22468-5.5102-0.22468-2.2362 0-4.237 0.10699-5.981 0.29958-1.9473 0.22469-3.8732 0.55636-5.767 0.99504z\" style=\"fill:#999;\"/><path d=\"m92.411 15.247c1.9152-0.43869 4.023-0.84526 6.3233-1.2304 2.065-0.34238 4.1514-0.62057 6.2698-0.84525l5.1785 50.565c-1.0913 0.10699-2.1827 0.25679-3.2954 0.43868-0.86665 0.14979-1.9152 0.36378-3.1349 0.64196z\" style=\"fill:#4d4d4d;\"/>",
}
// Afrohair
sp[8] = map[string]string{
"clo": "<path d=\"m141.89 195a114.79 114.79 0 0 1 38 16.5 115.55 115.55 0 0 1-128.47 0 114.79 114.79 0 0 1 38-16.5l15.75 15.75h21z\" style=\"fill:#353535;\"/><path d=\"m146.4 196.14-17.4 17.44-1.17 1.17h-24.34l-1.18-1.17-17.43-17.44c1.49-0.41 3-0.79 4.51-1.14l4.67-1 12.74 12.74h17.69l12.73-12.74 4.67 1c1.52 0.35 3 0.73 4.51 1.14z\" style=\"fill:#919191;\"/>",
"mouth": "<path d=\"m115.68 160.64c7.08 0 13.11-4.93 15.46-11.84a2.14 2.14 0 0 0-1.51-2.6101 2.3 2.3 0 0 0-0.73995-0.0593h-26.42a2.12 2.12 0 0 0-2.31 1.9099 1.85 1.85 0 0 0 0.0593 0.73995c2.3401 6.9301 8.3802 11.86 15.46 11.86z\" style=\"fill:#2f2f2f;\"/>",
"eyes": "<path d=\"m145.38 95.628c-5.1601 2.2597-11.03 2.2597-16.19 0m-47.29 1.75c5.1755-2.2694 11.065-2.2694 16.24 0\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:5.9998px;stroke:#5e5e5e;\"/><path d=\"m90.016 106.28c-4.4506-0.0105-6.6902 5.3657-3.5508 8.5195 3.1394 3.1539 8.5252 0.93887 8.5352-3.5117 0.0063-2.7522-2.2204-4.9898-4.9727-4.9961l-0.011719-0.01172zm47.281 0c-4.4506-0.0105-6.6902 5.3657-3.5508 8.5195 3.1394 3.1539 8.5252 0.93887 8.5352-3.5117 6e-3 -2.7522-2.2204-4.9898-4.9727-4.9961l-0.01171-0.01172z\" style=\"fill:#1a1a1a;\"/>",
"top": "<path d=\"m108.37 22.019c-6.2698-12.829-17.151-13.396-18.949 1.1769-11.448-9.4583-26.021-4.483-20.361 12.422-12.251-7.9282-24.919 1.7761-17.076 20.853-27.08 2.3646-22.715 24.726-10.111 31.435-9.9002 3.3566-10.701 9.4006-8.464 14.497 2.6574 4.7842 9.0126 6.4737 11.545 9.6519-6.624 0.59419-8.4112 5.6011-5.7404 9.5192 1.6896 2.4787 5.2756 4.2218 8.5971 5.5455 1.0485 0.40658 3.702 1.2732 3.9053 2.4181 0.18744 1.2156-6.7884 3.0055-5.7281 5.2612 0.60648 1.4227 1.7764 2.7151 2.6466 3.7156 1.2807 1.6595 10.755 8.0351 9.4583 4.2049-1.0271-3.7234-2.2148-7.4682-3.1456-11.192-1.1662-5.3069-1.7868-10.721-1.102-16.156 1.4223-5.455 5.069-4.4265 7.7837-8.3588 3.5264-5.7505 2.0296-11.614 2.124-13.575 0.107-1.7868 1.5407-1.1876 3.1884-1.4337 4.3868-0.64196 7.0081-2.1185 8.8377-6.2698 0.77035-1.9259 0.62057-9.7578 0.52426-11.78 0.36378-4.6328 4.1835 0 6.548 0.64196 3.2633 0.88805 6.8797 0.21399 9.0731-2.5037 1.7547-2.3753 2.0864-2.8888 4.6114-0.80245 2.6856 2.2148 4.0979 3.1349 7.6929 3.274 5.5637 0.20329 8.7735-6.2698 11.32-5.6386 3.5201 0.87735 3.6057 5.4567 10.261 4.8682 2.386-0.20329 3.8304-0.86665 5.4032-2.6428 0.88805-0.99505 1.958-2.5037 3.4345-2.6214 1.4658-0.1177 2.3218 2.3646 3.0065 3.4452 1.1926 2.6755 4.0295 3.6513 6.2377 3.3168 1.958-0.17119 3.854-1.4115 5.4268-2.4707 0.99679-0.66102 1.8284-0.81128 1.9256 0.2071 0.29592 2.2271 0.0862 7.7025 0.1596 8.4821 0.10556 8.4609 5.37 10.569 13.223 10.333-0.31871 3.7464 0.0583 11.28 5.4353 14.562 3.9481 2.7604 6.6657 1.2732 6.7299 7.8534 7e-3 6.1914-0.43693 13.061-1.2946 18.189-0.69547 4.0444-1.2412 6.4838-2.5251 10.378-0.64196 1.9152-0.81315 1.9687 1.4123 1.0699 7.1472-3.1456 10.539-11.48 8.3562-18.842-0.43869-2.0436 0.84525-1.7226 2.8781-2.6106 9.5248-4.2363 8.1264-11.335-0.75967-14.273 11.988-3.0926 13.886-8.9002 6.6871-15.375 7.3077-5.9168 3.6378-16.177-2.8032-16.991 12.422-7.0937 5.7349-22.062-5.1036-18.499 4.1728-12.037-5.5637-26.203-21.121-16.894 6.9653-11.373 2.065-22.661-12.101-10.785-3.4559-18.382-15.14-16.584-23.902-5.018 0.09435-20.075-16.001-17.42-18.146-2.5892z\" style=\"fill:#1a1a1a;\"/><path d=\"m5.4353 80.502c7.4468 9.1373 15.632 8.8912 15.632 8.8912s-6.0772 3.7983-6.8369 9.8755c-0.75966 6.088 4.5579 9.6295 8.0994 10.646 3.5522 1.0058 7.0937-2.7925 7.0937-2.7925s-5.8312 10.646-1.5193 15.964c4.3012 5.3176 11.908 3.0386 11.908 3.0386s-5.3283 10.132 1.0057 14.187c5.8312 3.7234 18.542 7.6715 20.511 8.2706-6.0666-9.7472-9.576-21.249-9.576-33.575v-0.0428c0-35.201 28.546-63.747 63.747-63.747 35.212 0 63.758 28.546 63.758 63.747 0 12.476-3.5843 24.116-9.7899 33.949h0.53496s13.931-1.0057 16.21-9.3727c2.279-8.3562 0.75967-9.8756 0.75967-9.8756s10.635 2.0329 13.417-7.5966l2.7926-9.6295s10.132 0 10.892-7.083c0.75963-7.0937-7.0295-12.411-7.0295-12.411s11.459 0.82385 14.498-10.453c1.0164-3.7555 0.83456-8.2171 0.1391-12.497-17.665-41.161-58.569-69.995-106.18-69.995-30.632 0-60.034 12.187-81.679 33.831v0.0107c-13.171 13.171-22.833 29.22-28.386 46.66z\" style=\"fill:#1a1a1a;\"/>",
}
// Normie Female
sp[9] = map[string]string{
"clo": "<path d=\"m141.75 195a114.79 114.79 0 0 1 38 16.5 115.53 115.53 0 0 1-128.46 0 114.79 114.79 0 0 1 38-16.5l13.85 13.85v-1.2h17.86v3.1h5z\" style=\"fill:#333;\"/><polygon points=\"115.36 207.65 123.37 224.2 148.3 196.86 143.08 189.95\" style=\"fill:#fff;\"/><polygon points=\"115.36 207.65 107.35 224.2 82.42 196.86 87.63 189.95\" style=\"fill:#fff;\"/>",
"mouth": "<path d=\"m126.28 149.82c-6.16 2.43-15.52 2.42-21.56 0\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:5.9998px;stroke:#1c1c1c;\"/>",
"eyes": "<path d=\"m83.527 103.98v10h10v-10h-10zm53.945 0v10h10v-10h-10z\" style=\"fill:#1a1a1a;\"/><path d=\"m56.621 94.906v11.688h5.3418v6.4922h5.3418v6.1055h5.3223v6.2324h26.846v-6.2324h5.3047v-6.1055h5.1445v-6.0039h11.154v6.0039h5.1446v6.1055h5.3066v6.2324h26.846v-6.2324h5.3203v-6.1055h5.3438v-6.4922h5.3418v-11.688z\" style=\"fill:#1a1a1a;\"/><path d=\"m67.387 100.65v5.9394h5.1992v-5.9394zm5.1992 5.9394v6.4922h5.4238v-6.4922zm5.4238 0h5.1992v-5.9394h-5.1992zm5.1992 0v6.4922h5.4258v-6.4922zm5.4258 6.4922v6.1055h5.1426v-6.1055zm-10.625 0v6.1055h5.1445v-6.1055zm48.281-12.432v5.9394h5.1992v-5.9394zm5.1992 5.9394v6.4922h5.4238v-6.4922zm5.4238 0h5.1992v-5.9394h-5.1992zm5.1992 0v6.4922h5.4258v-6.4922zm5.4258 6.4922v6.1055h5.1426v-6.1055zm-10.625 0v6.1055h5.1445v-6.1055z\" style=\"fill:#fff;\"/>",
"top": "<path d=\"m157.79 67.5a61.31 61.31 0 0 1-42.79 17.43h-55.7c18.16-37.74 68.27-46.85 98.49-17.43z\" style=\"fill:#4d4d4d;\"/><path d=\"m122.93 7.0078c-10.503-0.15729-21.09 1.6448-29.545 5.4316-17.141 7.8999-32.169 23.297-43.973 38.779-5.1703 6.8631-8.7779 13.46-8.1855 18.395 0.93114 12.312 10.372 26.483 11.068 36.9 15.663-72.081 105.99-70.452 124.91-7.0525l4e-3 0.0156c5.616-10.926 8.0682-20.188 8.352-27.653 0.43654-15.607-7.8088-21.149-21.735-28.249 1.7934-3.7704 1.7273-7.5023 2.0625-10.154-0.79964-7.8568-3.6796-13.51-10.43-17.758-5.9434-3.7404-13.06-6.0867-18.463-7.2266-4.5319-0.87895-9.2901-1.3562-14.064-1.4277z\" style=\"fill:#4d4d4d;\"/><path d=\"m42.426 75.338c0.52158 18.689 10.557 74.338-18.115 101.25 12.38 10.603 28.352 19.061 46.025 24.594 11.032-4.6874 22.88-7.4147 34.817-8.5046l0.0633-14.477c-22.49-4.3813-40.766-18.898-48.862-39.967-8.096-21.07-4.7931-44.72 9.2478-62.393zm124.67 2.7207c7.8997 10.886 11.743 24.64 11.787 37.441-0.36632 30.178-22.389 57.576-53.12 62.708l0.0238 14.471c12.282 1.1216 24.518 3.9888 35.825 8.9128 15.488-5.1448 30.007-13.325 42.396-25.043-13.136-22.051-23.282-63.045-18.694-101.55z\" style=\"fill:#4d4d4d;\"/><path d=\"m143.61 46.383c-11.639 0.12482-20.998 1.8906-20.998 1.8906l-9 3.5059c0.63003-0.0191 1.2603-0.0289 1.8906-0.0293h0.0996c35.169 0.055 60.959 27.235 63.283 63.383 7.4e-4 31.157-22.742 57.213-53.106 63.079l-0.0216 14.498c11.567 1.0563 23.154 3.6067 33.887 8.0463 35.952-15.315 55.082-52.303 36.709-68.279-5.018-7.9035-10.44-15.409-9.5544-23.03 5.0545-50.452 0.39626-63.561-43.189-63.064zm-69.966 21.09c-15.286 3.244-17.096 3.73-31.734 6.6953 3.0304 13.081 3.0583 22.274 1.2085 30.012-3.8004 11.361-8.9712 19.787-12.286 28.764-6.8823 22.459-2.9157 31.982 12.093 46.165 8.6595 8.0693 19.861 16.209 30.939 20.647 2.669-1.0316 5.3729-1.9628 8.106-2.792 7.4979-2.275 15.388-3.6535 23.206-4.3673l0.0433-14.393c-23.933-4.5937-44.283-21.98-50.77-45.817-6.3319-23.265 0.51104-48.752 19.195-64.914z\" style=\"fill:#4d4d4d;\"/>",
}
// Older
sp[10] = map[string]string{
"clo": "<path d=\"m141.75 195a114.79 114.79 0 0 1 38 16.5 115.53 115.53 0 0 1-128.46 0 114.79 114.79 0 0 1 38-16.5l15.71 15.75h21z\" style=\"fill:#666;\"/><path d=\"m89.291 195a114.79 114.79 0 0 0-38.002 16.5 115.53 115.53 0 0 0 38.002 16.482zm52.434 0v32.982a115.53 115.53 0 0 0 38-16.482 114.79 114.79 0 0 0-38-16.5z\" style=\"fill:#999;\"/><path d=\"m157.15 199.75c0.2548 7.4501 1.54 14.855 4.9512 21.432a115.53 115.53 0 0 0 17.619-9.6797 114.79 114.79 0 0 0-22.57-11.752zm-83.295 2e-3a114.79 114.79 0 0 0-22.57 11.75 115.53 115.53 0 0 0 17.621 9.6797c3.411-6.5765 4.6944-13.98 4.9492-21.43z\" style=\"fill:#ccc;\"/><path d=\"m99.197 204.97v2e-3l16.302 16.301 16.301-16.301v-2e-3z\" style=\"fill:#fff;\"/>",
"mouth": "<path d=\"m100.19 152.09c2.8726 4.0616 9.8095 4.7232 15.119-0.45432 5.0656 4.5134 11.167 5.6898 15.495 0.31458\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:5.8949;stroke:#333;\"/><path d=\"m109.67 135.53c-0.9758 0.0743-2.05 0.45327-3.1485 0.99414-4.3235 2.1399-7.3862 4.2557-10.639 7.1406-0.6251 0.5715 0.1168 0.77785 1.4238 0.87304 5.6967 0.0536 14.384 0.41404 15.098-0.875 1.9251-2.0788 1.7969-5.3303-0.1816-7.3008-0.701-0.67533-1.5769-0.90632-2.5527-0.83203zm11.656 0c-0.9758-0.0743-1.8517 0.1567-2.5527 0.83203-1.9785 1.9705-2.1067 5.222-0.1817 7.3008 0.7142 1.289 9.401 0.9286 15.098 0.875 1.307-0.0952 2.0489-0.30154 1.4238-0.87304-3.2524-2.8849-6.3151-5.0007-10.639-7.1406-1.0985-0.54087-2.1727-0.91985-3.1485-0.99414z\" style=\"fill:#333;\"/>",
"eyes": "<path d=\"m97.56 107.84a10.63 10.63 0 0 1-15 0.13l-0.13-0.13\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:6.3px;stroke:#000;\"/><path d=\"m148.59 107.84a10.63 10.63 0 0 1-15 0.13l-0.13-0.13\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:6.3px;stroke:#000;\"/>",
"top": "<path d=\"m41.668 87.073c-9.2319-0.0231-11.63 6.5104 2.2676 17.66-14.015 1.1231-4.3662 16.457 4.875 24.66 4.0686 3.0199 6.4647 5.4657 5.5078 1.1348-1.2079-4.9178-1.8184-9.9634-1.8184-15.027 3.26e-4 -7.5692 1.2547-15.016 3.7883-22.183 0.57048-1.7876 1.0689-2.0306-0.37721-2.6839-5.5405-2.4478-10.375-3.5511-14.243-3.5608z\" style=\"fill:#ccc;\"/><path d=\"m185.48 89.513c-2.4418-0.11189-5.4618 0.81187-9.5148 3.2121-1.314 0.81729-0.70075 1.995-0.32301 3.2653 3.194 10.982 3.8215 22.462 1.2538 33.628-0.31613 1.688-0.47649 3.569 2.6953 1.3516 7.7016-5.371 19.17-18.734 16.918-26.105-1.4251-3.9177-11.4-0.35546-11.4-0.35546s4.987-4.2755 5.3437-9.6191c0.20048-3.0057-1.5237-5.2189-4.9726-5.377z\" style=\"fill:#ccc;\"/><path d=\"m91.689 36.108c-3.7298-7.3864-9.5859-10.504-17.578-6.7891-9.5194 4.5907-15.629 18.444-13.416 29.232 0 0-8.5511-4.9878-18.17-3.5625-19.623 8.094-1.4102 29.869 10.817 37.342 2.075 1.297 2.5792 1.7432 3.4291-0.37685 2.6746-6.5374 6.1886-12.722 11.297-17.709 4.1039 8.7427 14.629 4.1809 20.006-0.14062 4.4873 9.6838 10.377 6.3535 15.377 3.4785 4.0764 7.8829 10.756 7.25 17.631 0.0625 4.875 4.5625 14.713 4.1867 15.555-3.426 8.4753 2.6244 14.012 10.437 22.962-1.4764 8.8552 6.8221 14.407 16.853 17.122 27.51 0.34 1.554 1.175 0.85565 2.2212 0.44315 10.255-4.286 22.842-15.749 15.705-23.975-3.5623-3.5623-13.539-2.1387-13.539-2.1387s6.77-7.1233 9.2637-18.168c2.4936-11.043-23.514-4.9883-23.514-4.9883s7.4818-5.6993 12.113-13.537c4.6314-7.8378-2.4943-11.756-11.045-11.043-8.5496 0.71204-17.1 7.4805-17.1 7.4805s3.3946-7.8055-3.5625-12.826c-9.5935-6.9234-23.869 6.4121-23.869 6.4121-4.2562-26.835-24.872-6.386-31.707 8.1953z\" style=\"fill:#ccc;\"/>",
}
// Firehair
sp[11] = map[string]string{
"clo": "<path d=\"m116 203.13c-0.12 0-0.25 0.12-0.49 0.12s-0.25-0.12-0.49-0.12zm-27.29-8c0.87-0.25 1.72-0.47 2.56-0.69a32.37 32.37 0 0 0 0.3 8.57 21.5 21.5 0 0 0 7 6.88c6.41-6 16.8-6.64 16.8-6.64s10.5 0.58 17 6.69a21.61 21.61 0 0 0 6.93-6.66 32.34 32.34 0 0 0 0.35-8.84l2.13 0.56a114.79 114.79 0 0 1 38 16.5 115.53 115.53 0 0 1-128.46 0 114.64 114.64 0 0 1 37.38-16.37z\" style=\"fill:#e9e9e9;\"/><path d=\"m126.15 206-3.92 7.83h-13.46l-3.92-7.83a36.59 36.59 0 0 1 10.65-2.7 35.66 35.66 0 0 1 10.65 2.7z\" style=\"fill:#818181;\"/><path d=\"m124.54 230.65-2.18-16.74h-13.47l-2.19 16.76c2.9 0.22 5.84 0.33 8.8 0.33s6.06-0.12 9-0.35z\" style=\"fill:#989898;\"/><path d=\"m134.84 186s0.86 9.8-19.34 17.26c0 0 15.79 0.86 20.57 11.76 0.12 0.49 9.3-23.26-1.23-29z\" style=\"fill:#fff;\"/><path d=\"m96.16 186c-10.41 5.76-1.35 29.39-1.1 29 4.65-10.78 20.56-11.76 20.56-11.76-20.32-7.45-19.46-17.24-19.46-17.24z\" style=\"fill:#fff;\"/>",
"mouth": "<path d=\"m118.57 165.14a8.66 8.66 0 0 0-2.76-4.23h-0.62a8 8 0 0 0-2.76 4.22c-0.52 1.89 2.07 10.61 2.76 12.53h0.62c0.64-1.76 3.19-10.82 2.76-12.52z\" style=\"fill:#333;\"/><path d=\"m102.81 152.24a2.4921 2.4921 0 1 1 1.19-4.84l0.21 0.06a37.1 37.1 0 0 0 5.43 1.12 44.52 44.52 0 0 0 11.76 0 37.1 37.1 0 0 0 5.43-1.12 2.4903 2.4903 0 0 1 1.59 4.72l-0.21 0.06a43.08 43.08 0 0 1-6.15 1.29 48.55 48.55 0 0 1-13.08 0 42.79 42.79 0 0 1-6.17-1.29z\" style=\"fill:#333;\"/>",
"eyes": "<path d=\"m86.851 100.39a4.94 4.94 0 1 0 4.9297 5 5 5 0 0 0-4.9297-5zm57.221 0a4.94 4.94 0 1 0 4.9394 4.9394 4.94 4.94 0 0 0-4.9394-4.9394z\" style=\"fill:#333;\"/><path d=\"m86.207 89.365c-25.504 0-21.503 6.8561-21.035 19.596 0.80177 18.121 17.763 16.514 21.201 16.639 14.758-0.041 20.518-8.227 22.951-22.932 1.8166-10.731-9.251-13.174-23.117-13.303zm58.598 0c-13.866 0.1284-24.936 2.5717-23.119 13.303 2.4332 14.705 8.1936 22.891 22.951 22.932 3.4383-0.125 20.399 1.4828 21.201-16.639 0-18.965-0.47958-19.596-21.033-19.596z\" style=\"fill:#4d4d4d;\"/><path d=\"m169.87 90.255a0.51 0.51 0 0 0-0.43991-0.52 167.64 167.64 0 0 0-22.6-1.6801c-12 0-27.47 3.7601-30.17 3.7601h-2.4c-1.2499 0-5.29-0.80996-10.45-1.6801a124.35 124.35 0 0 0-19.72-2.08 166.18 166.18 0 0 0-19.31 1.24c-1.56 0.17999-2.69 0.35009-3.2899 0.44009a0.51 0.51 0 0 0-0.44007 0.52l-0.091 6.4501a0.57 0.57 0 0 0 0.33012 0.52l0.73994 0.23992c1.08 0.41992 1.0001 19.85 6.78 24.71 3.4401 2.8599 6.51 4.4899 19.42 4.4899 7.4699 0 12.17-1.9999 16.63-8 3.21-4.32 6.0999-14.55 6.0999-14.55 0.82006-4.07 3.7702-4.52 4.43-4.5801h0.12068c0.11078 0 3.66 0.0593 4.57 4.5801 0 0 2.8599 10.22 6.0699 14.54 4.4601 5.9999 9.1601 8 16.63 8 12.91 0 16-1.63 19.42-4.4901 5.7898-4.86 5.6998-24.29 6.78-24.71l0.73994-0.23993a0.57 0.57 0 0 0 0.32996-0.52l-0.12068-6.4501zm-65 23c-1.9101 4.5-6.8 10.29-13.7 10.64-20.7 0.99985-21.65-4.7401-23-9.3201a31.45 31.45 0 0 1-1.2099-13.18c0.53997-4.5799 1.7-7.2699 3.7801-8.6201a9.3 9.3 0 0 1 4.3499-1.51 85.07 85.07 0 0 1 11.4-0.52 59.23 59.23 0 0 1 9.2099 0.69999c7.37 1.2 12.35 3.7001 12.35 6.1601a46.12 46.12 0 0 1-3.23 15.64zm58 1.3201c-1.34 4.5799-2.29 10.36-23 9.3201-6.91-0.3501-11.81-6.1401-13.71-10.64a46.35 46.35 0 0 1-3.22-15.64c0-3.39 9.43-6.8599 21.56-6.8599 12.13 0 14 0.89996 15.75 1.9999 2.08 1.3502 3.2398 4 3.77 8.6201a31.23 31.23 0 0 1-1.1601 13.17z\" style=\"fill:#333;\"/>",
"top": "<path d=\"m156.1 15.879c-0.38556 5.3015-1.7049 9.4762-3.6602 12.76-0.41226 23.773-9.2343 35.229-15.154 42.797l15.062-4.6641c-0.66253 2.8135-2.4628 7.156-0.34766 12.137 1.6334-2.3144 7.9395-5.807 13-3.3477-0.43442 3.5532-0.95271 7.094-1.4512 10.639l8.9648 0.85937c0.83453 3.8792 0.51719 9.3449-0.59961 11.736l5.5508 2.0098c0.20764 2.7646 0.10001 5.4906-0.74609 8.875 8.4545-1.7225 14.213-4.3896 19.641-13.188 2.8639-4.7524 4.9018-10.483 4.7305-17.242-4.1612 4.916-9.6484 7.2485-15.26 10.109 6.507-11.065 8.8648-22.768 8.1367-30.58-7.3456 10.251-11.649 13.06-19.918 16.9 1.2386-11.4 5.5249-18.582 12.461-27.27-11.392-1.3025-16.301 1.4749-24.891 6.4395 4.5466-14.036 2.2208-26.679-5.5195-38.971zm-117.76 28.682c9.3378 3.6366 19.581 9.0234 21.129 18.549-7.6182 0.0414-14.897-3.5072-20.242-7.1894-0.15967 8.2309 2.8451 12.252 6.7734 19.08-7.2127 1.6129-12.084 4.8315-17.471 9.4805 7.2948-0.15715 12.299-1.0502 16.891 4.2793-6.0512 5.0164-11.99 10.79-11.99 19.24 9.257-6.1688 12.495-5.9486 21.137-2.2012 1.2906-8.0996 2.3978-14.872 2.7869-16.435 2.4719-0.73247 3.5247-0.94807 5.9221-1.2938-2.1556-7.4281 1.0996-9.5176 2.4141-11.6l7.543 1.5059c-3.9093-6.1699 2.6565-12.483 7.1445-15.51-4.4474-7.2082-5.6649-11.558-7.377-16.797-11.198-8.2947-23.895-6.2742-34.66-1.1094z\" style=\"fill:#f9f9f9;\"/><path d=\"m101.9 7.6408c-10.047 6.2416-12.441 28.646-12.131 33.289-6.9249-5.8258-7.8992-13.75-7.7695-19.203-9.6235 6.0158-10.666 14.421-9 23.943 1.1061 5.1411 2.3972 10.461 7.377 16.797 2e-3 -1e-3 4e-3 -3e-3 6e-3 -4e-3 2.7742 2.8742 5.4644 5.5941 8.3477 8.3574 0.41187-6.971 0.45449-13.622 7.1856-15.824 3.9532 2.8169 7.4123 5.9388 11.084 9.1035l10.559-10.25c5.6447 3.961 5.4531 6.5652 6.5215 14.104 2.153-1.7546 8.719-9.0037 15.844-10.139 0.98706 4.1261-0.99388 10.308-2.6387 13.621 0 0 14.32-11.846 15.195-27.971 0.33968-6.2599 0.2237-11.146-0.041-14.826-3.2125 5.5652-8.7118 8.7799-13.789 10.15-4.2715-9.2486-2.4785-21.435-0.48047-29.309-12.21 3.0195-20.932 18.337-22.172 25.07-9.2678-7.397-13.605-16.146-14.098-26.91z\" style=\"fill:#f9f9f9;\"/>",
}
// Blond
sp[12] = map[string]string{
"clo": "<path d=\"m141.75 195a114.79 114.79 0 0 1 38 16.5 115.53 115.53 0 0 1-128.46 0 114.79 114.79 0 0 1 38-16.5l26.23 13 26.27-13z\" style=\"fill:#131111;\"/><polygon points=\"115.5 208.03 115.5 207.74 82.72 188.91 80.45 198.86 101.46 222.72\" style=\"fill:#cbcbcb;\"/><polygon points=\"115.5 208.03 115.5 207.74 148.28 188.91 150.55 198.86 129.54 222.72\" style=\"fill:#cbcbcb;\"/>",
"mouth": "<path d=\"m123.07 154.05a10.61 10.61 0 0 1-15 0.14l-0.14-0.14\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:6.3px;stroke:#000;\"/><path d=\"m120.1 142.22 0.19-0.11c3-1.87 5.45-2.4 7.3-1.46 2.15 1.1 3.12 3.84 4.84 5.5a5.18 5.18 0 0 0 6.68 0.73m-28.21-4.66-0.19-0.11c-3-1.87-5.45-2.4-7.3-1.46-2.15 1.1-3.12 3.84-4.84 5.5a5.18 5.18 0 0 1-6.68 0.73\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:5.9998px;stroke:#4d4d4d;\"/>",
"eyes": "<path d=\"m161.73 86.016h-92.51c-3.37 0-6.0001 2.3998-6.0001 5.2999v28.45c0 3.0002 2.74 5.3001 6.0001 5.3001h32.36c7.0901 0 7.44-19.43 13.82-19.43s6.8801 19.44 13.83 19.44h32.36c3.37 0 5.9999-2.4 5.9999-5.3001v-28.46c0.14043-2.9001-2.6-5.2999-5.9-5.2999z\" style=\"fill:#8f8f8f;\"/><path d=\"m161.73 86.016h-92.51c-3.37 0-6.0001 2.3998-6.0001 5.2999v28.45l104.55-28.45c0-2.9001-2.74-5.2999-5.9999-5.2999z\" style=\"fill:#e3e3e3;\"/><path d=\"m161.73 86.016h-92.51c-3.37 0-6.0001 2.3998-6.0001 5.2999v28.45c0 3.0002 2.74 5.3001 6.0001 5.3001h32.36c7.0901 0 7.44-19.43 13.82-19.43s6.8801 19.44 13.83 19.44h32.36c3.37 0 5.9999-2.4 5.9999-5.3001v-28.46c0.14043-2.9001-2.6-5.2999-5.9-5.2999z\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:4.0026px;stroke:#232323;\"/>",
"top": "<path d=\"m69.834 33.826c-8.2001-0.0626-16.444 2.6753-23.152 7.7038-8.5298 6.9899-12.159 19.61-12.329 32.68-0.2041 15.476 1.6092 34.752 1.7464 51.915 0.10414 13.047 0.53485 25.984-2.9197 33.995-2.4994 5.81-9.0955 9.6006-16.196 12.311 7.9599 2.8301 25.009 2.8094 33.58 1.5393 10.8-1.59 17.238-6.5294 17.159-22.699-0.0911-15.93-1.3894-29.23-1.559-45.83-0.3208-11.983-1.569-24.291 4.9774-33.987 4.2139-6.1265 10.452-10.521 17.116-13.588 3.9292-1.8575 8.0384-3.3083 12.263-4.3297-6.8718-13.574-18.732-19.618-30.687-19.709z\" style=\"fill:#b3b3b3;\"/><path d=\"m90.8 76.246c11.918-17.125 31.996-23.218 49.743-17.488 11.81 3.9496 20.692 13.389 22.313 28.237 0.51051 6.2098 0.63413 12.445 0.37007 18.67-0.23973 11.2-0.72946 23.82-1.0995 34.08-0.82005 22.43 0.0593 35.1 24.589 36.3 8.5635 0.32122 17.137-0.22845 25.59-1.6405h-0.0198c-10.74-3.3799-17.98-15.609-19.3-26.289-1.29-10.41-0.6098-23.43-0.7898-38.091-0.1701-14.96 1.0398-29.819 0.28008-42.089-1.414-22.777-14.947-38.505-34.126-45.152-27.813-7.35-51.083 0.091-61.672 17.343-5.4698 8.9112-7.7413 20.07-5.8788 36.121z\" style=\"fill:#b3b3b3;\"/>",
}
// Ateam
sp[13] = map[string]string{
"clo": "<path d=\"M61.11,205.59l3.49,3.69-6.26,6.6A115.45,115.45,0,0,0,72,222.51v-22a115.19,115.19,0,0,0-10.85,5.1Z\" style=\"fill:#eee;\"/><path d=\"M93.24,228.85V199l-4-4A114.43,114.43,0,0,0,72,200.49v22a114.43,114.43,0,0,0,21.28,6.34Z\" style=\"fill:#787878;\"/><path d=\"m159 222.51v-22a114.63 114.63 0 0 0-17.25-5.51l-4 4v29.86a114.16 114.16 0 0 0 21.25-6.35z\" style=\"fill:#787878;\"/><path d=\"m169.89 205.59-3.49 3.69 6.26 6.6a115.45 115.45 0 0 1-13.66 6.63v-22a115.19 115.19 0 0 1 10.85 5.1z\" style=\"fill:#eee;\"/><path d=\"M115.5,219.62A28.5,28.5,0,0,1,87.25,195c2.93-.74,5.92-1.36,8.94-1.87a19.41,19.41,0,0,0,38.62,0c3,.51,6,1.13,8.94,1.87a28.49,28.49,0,0,1-28.25,24.63Z\" style=\"fill:#c9c9c9;\"/>",
"mouth": "<path d=\"m115.5 153.93a14 14 0 0 1-10.5-4.69 3.4209 3.4209 0 0 1 5-4.67l0.08 0.08 0.08 0.09a7.35 7.35 0 0 0 10.39 0.37l0.37-0.37a3.4206 3.4206 0 1 1 5.23 4.41l-0.08 0.09a14 14 0 0 1-10.53 4.69z\" /><path d=\"m115.27 127.32c-7.6627-0.03-15.251 1.4419-20.646 5.1465-7.62 5.33-9.9053 11.512-14.127 18.109-3.4379 5.2447-9.326 10.024-13.467 6.334 25.425 29.755 71.409 29.786 96.875 0.0664-6.8104 3.9305-11.545-2.47-13.508-6.4004-10.697-17.605-14.115-22.656-35.127-23.256zm-0.26758 8.3984c7.457 0.0802 14.986 1.2966 17.146 5.9522 2.5765 11.319-7.5878 17.454-16.681 17.515-6.09-0.05-12.2-2.3802-15.26-7.7402-6.36-11.16 3.6349-15.607 14.795-15.727z\" style=\"fill:#404040;\"/>",
"eyes": "<path d=\"m91.72 97.36v11.4m47.56-11.4v11.4\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:7.9999px;stroke:#333;\"/>",
"top": "<path d=\"m52.107 57.293c-1.3411 14.839-3.8707 52.771 1.3145 72.715-0.67572-43.829 12.389-70.177 62.078-70.187 49.689 0.010061 62.754 26.359 62.078 70.187 5.1852-19.944 2.6556-57.876 1.3145-72.715h-63.393-63.393z\" style=\"fill:#4d4d4d;\"/><path d=\"m52.339 30.629c-1.3825 24.448-2.1216 45.905-1.4497 66.517 9.4643-48.304 112.77-54.916 129.22 0 0.67191-20.612-0.3798-47.256-1.4928-66.517-32.241 14.296-91.346 18.861-126.28 0z\" style=\"fill:#4d4d4d;\"/><path d=\"m115.5 24.92c-22.25 0-44.5 4.2296-56.72 12.69-3.32 2.3-5.0602 6.4392-5.5903 10.269-0.45275 3.23-0.84043 6.7561-1.1785 10.461h126.98c-0.33704-3.7047-0.72492-7.2306-1.1775-10.461-0.53009-3.8301-2.2697-7.9992-5.5897-10.269-12.22-8.4601-34.47-12.69-56.72-12.69z\" style=\"fill:#4d4d4d;\"/><path d=\"m76.521 39.139c21.233 3.3965 33.116-13.392 37.59-31.72 4.3614 17.158 14.175 34.968 36.577 31.584-33.921 20.594-57.646 11.594-74.167 0.1345z\" style=\"fill:#4d4d4d;\"/>",
}
// Rasta
sp[14] = map[string]string{
"clo": "<path d=\"m91.92 194.41a101.47 101.47 0 0 1 23.58 17.09 101.47 101.47 0 0 1 23.58-17.09c0.89 0.19 1.78 0.38 2.67 0.59a114.79 114.79 0 0 1 38 16.5 115.53 115.53 0 0 1-128.46 0 114.79 114.79 0 0 1 38-16.5c0.88-0.21 1.78-0.4 2.67-0.59z\" style=\"fill:#757575;\"/><path d=\"m73.65 199.82c16.59 8.23 28.72 18.91 34.27 30.93a114.86 114.86 0 0 1-56.65-19.25 115.06 115.06 0 0 1 22.38-11.68z\" style=\"fill:#d8d8d8;\"/><path d=\"m60.63 205.85c12.35 5.94 21.93 13.44 27.59 21.91a114.7 114.7 0 0 1-36.95-16.26q4.53-3 9.36-5.65z\" style=\"fill:#757575;\"/><path d=\"m157.35 199.82c-16.6 8.23-28.72 18.91-34.27 30.93a114.86 114.86 0 0 0 56.65-19.25 115.06 115.06 0 0 0-22.38-11.68z\" style=\"fill:#d8d8d8;\"/><path d=\"m170.37 205.85c-12.35 5.94-21.93 13.44-27.59 21.91a114.7 114.7 0 0 0 36.95-16.26q-4.53-3-9.36-5.65z\" style=\"fill:#757575;\"/>",
"mouth": "<path d=\"m115.5 131c-17.71 0.65-27 9.41-29.61 23.69-1 5.62-0.43 7.06 2.76 7.17 22.76 0.76 22.23 18.21 26.85 18.89 4.62-0.68 4.09-18.13 26.85-18.89 3.19-0.11 3.79-1.55 2.76-7.17-2.62-14.28-11.9-23-29.61-23.69zm0 29.31c-10 0-18-5-18-11.17s8.08-11.17 18-11.17 18 5 18 11.17-8.08 11.17-18 11.17z\" style=\"fill:#333;\"/><path d=\"m123.54 148.46a11.53 11.53 0 0 1-16.09 0\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:6.7998px;stroke:#000;\"/>",
"eyes": "<path d=\"m133 108.17h14.17m-63.26 0h14.09m-20.69-8.93a21.31 21.31 0 0 1 27.29 0m21.8 0a21.31 21.31 0 0 1 27.29 0\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:4.8243px;stroke:#000;\"/>",
"top": "<path d=\"m115.5 51.75c-38.702 5.3101-54.215 18.038-59.863 35.101\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:12;stroke:#333;\"/><path d=\"m115.5 51.75c-7.8393 3.6337-5.5974 16.583-14.341 23.452\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:12;stroke:#333;\"/><path d=\"m111.35 48.614c-22.634-6.9181-42.457-3.1988-55.733 2.5105\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:12;stroke:#333;\"/><path d=\"m115.47 54.008c0.1965-6.7774-0.1436-26.309 0.05-38.184\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:12;stroke:#333;\"/><path d=\"m68.874 28.177c34.115-3.382 41.987 13.321 45.17 19.602\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:12;stroke:#333;\"/><path d=\"m116.49 48.69c2.8876-6.3019 10.358-21.518 43.469-22.326\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:12;stroke:#333;\"/><path d=\"m116.92 51.766c1.5094 6.3991 3.4988 15.595 10.088 23.058\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:12;stroke:#333;\"/><path d=\"m113.81 51.532c22.03-7.8674 46.709-7.3614 59.444-2.0465\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:12;stroke:#333;\"/><path d=\"m114.53 52.278c36.226 4.8583 52.414 17.092 59.373 33.347\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:12;stroke:#333;\"/><path d=\"m55.637 86.851c-4.1213 12.452-2.9877 27.213-1.777 43.084\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:12;stroke:#333;\"/><path d=\"m55.614 51.124c-13.422 5.5019-21.908 16.409-24.712 28.774-1.8322 8.4632-1.9809 18.156-1.6096 28.486\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:12;stroke:#333;\"/><path d=\"m173.26 49.486c24.917 10.399 26.707 36.537 27.209 59.62\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:12;stroke:#333;\"/><path d=\"m173.9 85.625c5.4042 12.625 5.2413 27.675 4.5745 43.58\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:12;stroke:#333;\"/><path d=\"m53.86 129.93c1.293 16.951 2.6738 35.169-2.1664 53.193\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:12;stroke:#333;\"/><path d=\"m29.292 108.38c0.6173 17.177 2.6722 36.119 0.8158 54.108\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:12;stroke:#333;\"/><path d=\"m200.47 109.11c0.3586 18.529-1.2751 36.94 1.9231 48.985\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:12;stroke:#333;\"/><path d=\"m178.48 129.2c-0.7279 17.362-2.0563 35.743 2.6011 53.099\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:12;stroke:#333;\"/>",
}
// Meta
sp[15] = map[string]string{
"clo": "<path d=\"m141.75 195a114.79 114.79 0 0 1 38 16.5 115.53 115.53 0 0 1-128.46 0 114.79 114.79 0 0 1 38-16.5 115.77 115.77 0 0 1 15.71-2.53v-14.09a63.8 63.8 0 0 0 21 0v14.09a116.6 116.6 0 0 1 15.75 2.53z\" style=\"fill:#1a1a1a;\"/><path d=\"m60.984 205.66 6.2675 2.2051 3.4074-6.819 2.8018-1.1353-3.9911 7.9907 27.222-3.0857 3.2541-11.739 2.1451-0.2692-3.2833 11.819 20.393-1.6011-14.191-15.945v-2.4379l17.606-5.7274 3.3855-0.473v1.47l-19.167 6.2295 14.731 16.542 19.839-7.7432 3.3636 0.8223-21.371 8.34 20.532 13.842 2.6777-21.687 1.9481 0.5604-2.7726 22.378 0.0584 0.0364 8.5075 4.9923-2.4807 0.85145-6.4718-3.7916-1.2987 6.0622-2.1524 0.53125 1.3425-6.2804-17.037 8.8348-5.0271 0.35661 21.59-11.193-20.962-14.133-7.5006 25.457-2.0721-0.0364 7.6392-25.915-21.05 1.652 9.0109 24.052-1.4155-0.0946-0.49615-0.0437-0.073-7e-3 -0.2043-0.0145-8.3688-22.342-10.127 19.242-1.9846-0.52399 10.514-19.962-26.04 2.9547 13.425 16.418-3.4438-1.0625-12.083-14.781-8.1645 5.9675-1.9043-1.077 8.128-5.9385-6.9898-2.4598 2.3348-1.2881zm92.509-7.2556 14.228 20.093-1.8095 0.89514-15.614-22.043z\" style=\"fill:#b2b2b2;\"/>",
"mouth": "<path d=\"m97.06 144.59a20.15 20.15 0 0 0 36.88 4.53z\" style=\"fill:#fff;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.9999px;stroke:#000;\"/>",
"eyes": "<line x1=\"85.29\" x2=\"85.29\" y1=\"98.73\" y2=\"109.79\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:8.7999px;stroke:#000;\"/><path d=\"m108.28 72.16h62.18c9.19 0 13.32 1.21 14.71 8.52 3.61 18.95 2.2 33.49-0.44 43.75a65.07 65.07 0 0 1-5.89 14.78 73.52 73.52 0 0 1-7.06 10.26c-1.8 2.27-5.17 1.21-4.19-1.09 0.14-0.47 0.27-1 0.4-1.48a14.29 14.29 0 0 0 0.52-6.62 12.52 12.52 0 0 0-3.88-6.3c-4.17-3.9-12.81-8.71-32.53-13.66-6.4-1.6-10.69-2.24-11.76-2.79a7.08 7.08 0 0 1-3.85-6.31v-9c0-2.39 0.18-4.55-1.56-6.57s-4.16-2.13-6.65-2.14a6 6 0 0 1-6-6v-9.35a6 6 0 0 1 6-6z\" style=\"fill:#1a1a1a;\"/><path d=\"m135.9 98.73v9.27m15.22-9.29v9.29\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:7.7998px;stroke:#b2b2b2;\"/>",
"top": "<path d=\"m109.99 15.57c-13.46 3.6301-19.789 11.95-24.069 24.08-6.9996-7-8.7307-10.82-7.5606-21.43a41 41 0 0 0-9.2698 24.988c0.0366 7.6776 5.6462 13.939 12.697 15.297-13.315 5.8106-15.258 22.033-14.045 33.524 5.7687-11.861 14.254-20.981 27.258-22.951-0.43017 6.6-2.5099 10.22-7.29 17.66 18.29-2.8601 25.119-7.8199 37.15-18.24 0.46001 0 1.0001 0.089 1.4606 0.12058-0.33023 3.5601-1.0906 6.5598-5.0004 12.46 9.5298-1.32 14.721-5.8006 17.539-11.671 8.8862 0.95314 15.836 6.785 21.26 14.818 1.928-15.211-4.4766-26.6-19.807-34.036 1.4167-2.6974 8.0143-11.925 17.661-15.721-1.424-0.28569-2.8883-0.49486-4.4033-0.61125-5.71-0.41992-13.62-0.99982-24.89 4.1703 2.8501-8.5101 10.21-11 18.05-13.12-15.131-1.2501-28.61-2.5898-40.53 8.1801-1.8997-6.21-0.18055-12.54 3.7889-17.52z\" style=\"fill:#111;\"/><path d=\"m172.63 69.954c1.2292 14.064 0.93841 29.96 0.34635 45.169 1.7887 6.796 3.0379 13.235 3.8842 18.388l0.13973-0.011c1.0001 6.56 2.3597 13.18 3.2698 19.73 2.0002-6.5699 2.5303-18.25 3.2405-25.43 1.2597-13 1.8296-29.311-0.43017-41.931-0.85041-4.72-2.0007-7.6896-2.0007-8.4796 4.6205 3.5601 8.6606 9.2204 13.001 14.15-0.6751-3.4318-1.347-6.6004-2.0567-9.5273-4.047-5.7183-13.726-12.154-19.393-12.06z\" style=\"fill:#111;\"/><path d=\"m157.97 34.471c-10.339 2.7579-17.715 13.543-19.132 16.24 15.33 7.4361 20.783 17.96 21.278 33.517 5.9534 8.8179 10.066 20.289 12.857 30.895 0.87636-13.178 1.8186-27.726 0.26566-44.28 2.5698 0.44857 9.1372 1.3934 18.781 11.17-2.1158-8.7321-4.5671-15.31-8.4539-20.283-4.5598-5.8401-10.999-10.431-23.809-13 9.6502-3.34 16.27-0.76993 25.5 2.1301-8.1388-7.4315-16.474-14.219-27.287-16.389z\" style=\"fill:#111;\"/><path d=\"m61.473 73.354c-7.256-0.77501-13.024 2.3746-16.262 5.3879 0.73789-0.45409 1.3868-0.74208 1.8489-0.74208 0 0-1.5198 10.359-1.6197 11.519-1.56 19.73 0.99957 43.401 6.37 62.471 1.3099 4.6899 1.1895 3.0893 1.8898-0.9107 1.7526-10.061 3.3891-24.703 6.9739-38.864-5.068-17.627-4.2508-32.403 0.79937-38.861z\" style=\"fill:#111;\"/><path d=\"m69.09 43.21c-0.0253 1.0803-8e-3 2.1612 0.0523 3.2402-3.8402 0-12.46 0.71984-16 2.1598-4.4504 1.8001-8.48 5.4801-11.67 11.83 7.2999-3.94 11.899-3.8502 16.66-1.8102-10.39 3.45-19.52 11.37-20.32 26.9 1.1456-1.5053 4.6079-4.9789 7.1393-6.6285 0.09-0.0587 0.17427-0.10556 0.26167-0.15946 3.7141-2.3211 9.0494-5.1247 15.181-4.9553-5.0501 6.4577-6.6824 20.434 0.28207 38.428 1.7866-7.0567 4.0574-13.994 7.0681-20.184-1e-3 -11.664 2.0764-27.774 15.391-33.585-7.0508-2.1538-12.709-7.991-14.043-15.236z\" style=\"fill:#111;\"/>",
}
}
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)
}

20
go.mod
View File

@@ -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

135
go.sum
View File

@@ -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=