updated

This commit is contained in:
landaiqing
2024-11-14 01:58:20 +08:00
parent c95d5fc041
commit 3b8e3df27a
83 changed files with 172212 additions and 343 deletions

View File

@@ -0,0 +1,35 @@
package verify
import (
"context"
"encoding/json"
"fmt"
"strconv"
"github.com/redis/go-redis/v9"
"github.com/wenlng/go-captcha/v2/rotate"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
)
// VerifyRotateCaptcha verify rotate captcha
func VerifyRotateCaptcha(context context.Context, redis *redis.Client, angle int64, key string) bool {
cacheDataByte := redis.Get(context, constant.UserCaptchaPrefix+key).Val()
if len(cacheDataByte) == 0 {
return false
}
var dct *rotate.Block
if err := json.Unmarshal([]byte(cacheDataByte), &dct); err != nil {
return false
}
sAngle, err := strconv.ParseFloat(fmt.Sprintf("%v", angle), 64)
if err != nil {
return false
}
chkRet := rotate.CheckAngle(int64(sAngle), int64(dct.Angle), 2)
if chkRet {
return true
}
return false
}

View File

@@ -0,0 +1,36 @@
package verify
import (
"context"
"encoding/json"
"fmt"
"strconv"
"github.com/redis/go-redis/v9"
"github.com/wenlng/go-captcha/v2/slide"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
)
// VerifySlideCaptcha verify slide captcha
func VerifySlideCaptcha(context context.Context, redis *redis.Client, point []int64, key string) bool {
cacheDataByte := redis.Get(context, constant.UserCaptchaPrefix+key).Val()
if len(cacheDataByte) == 0 {
return false
}
var dct *slide.Block
if err := json.Unmarshal([]byte(cacheDataByte), &dct); err != nil {
return false
}
chkRet := false
if 2 == len(point) {
sx, _ := strconv.ParseFloat(fmt.Sprintf("%v", point[0]), 64)
sy, _ := strconv.ParseFloat(fmt.Sprintf("%v", point[1]), 64)
chkRet = slide.CheckPoint(int64(sx), int64(sy), int64(dct.X), int64(dct.Y), 4)
}
if chkRet {
return true
}
return false
}

View File

@@ -0,0 +1,8 @@
package constant
const (
UserClientPrefix string = "user:client:"
UserSessionPrefix string = "user:session:"
UserCaptchaPrefix string = "user:captcha:"
UserTokenPrefix string = "user:token:"
)

View File

@@ -0,0 +1,3 @@
package constant
const SESSION_KEY = "SESSION"

View File

@@ -0,0 +1,16 @@
package i18n
import (
i18n2 "github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/pelletier/go-toml/v2"
"golang.org/x/text/language"
)
func NewBundle(tag language.Tag, configs ...string) *i18n2.Bundle {
bundle := i18n2.NewBundle(tag)
bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)
for _, file := range configs {
bundle.LoadMessageFile(file)
}
return bundle
}

View File

@@ -0,0 +1,62 @@
package i18n
import (
"context"
"net/http"
i18n2 "github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
"google.golang.org/grpc/metadata"
)
/*
type Cache interface {
GetLocalizer() (*i18n2.Localizer, bool)
SetLocalizer(l *i18n2.Localizer)
}
*/
func getLocalizer(ctx context.Context) (*i18n2.Localizer, bool) {
v := ctx.Value(I18nKey)
if l, b := v.(*i18n2.Localizer); b {
return l, true
}
return nil, false
}
func withRequest(r *http.Request, currentLang language.Tag, bundle *i18n2.Bundle) *http.Request {
accept := r.Header.Get(defaultLangHeaderKey)
localizer := i18n2.NewLocalizer(bundle, accept)
ctx := setLocalizer(r.Context(), localizer)
ctx = setCurrentLang(ctx, currentLang)
ctx = metadata.AppendToOutgoingContext(ctx, defaultLangHeaderKey, accept)
return r.WithContext(ctx)
}
func setCurrentLang(ctx context.Context, currentLang language.Tag) context.Context {
return context.WithValue(ctx, I18nCurrentLangKey, currentLang)
}
func setLocalizer(ctx context.Context, l *i18n2.Localizer) context.Context {
return context.WithValue(ctx, I18nKey, l)
}
func IsHasI18n(ctx context.Context) bool {
v := ctx.Value(I18nKey)
if v != nil {
return true
}
return false
}
// func isHasI18n(ctx context.Context) bool {
// if use, exist := ctx.Value(isUseI18n).(bool); exist {
// return use
// }
// return false
// }
//
// func setHasI18n(ctx context.Context, use bool) context.Context {
// return context.WithValue(ctx, isUseI18n, use)
// }

View File

@@ -0,0 +1,61 @@
package i18n
import (
"context"
i18n2 "github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
)
func FormatText(ctx context.Context, msgId string, defaultText string) string {
return FormatTextWithData(ctx, msgId, defaultText, nil)
}
func FormatTextWithData(ctx context.Context, msgId string, defaultText string, args map[string]interface{}) string {
return FormatMessage(ctx, &i18n2.Message{
ID: msgId,
Other: defaultText,
}, args)
}
func FormatMessage(ctx context.Context, message *i18n2.Message, args map[string]interface{}) string {
if localizer, ok := getLocalizer(ctx); ok {
return localizer.MustLocalize(&i18n2.LocalizeConfig{
DefaultMessage: message,
TemplateData: args,
})
}
return formatInternalMessage(message, args)
}
func formatInternalMessage(message *i18n2.Message, args map[string]interface{}) string {
if args == nil {
return message.Other
}
tpl := i18n2.NewMessageTemplate(message)
msg, err := tpl.Execute("other", args, nil)
if err != nil {
panic(err)
}
return msg
}
func FetchCurrentLanguageFromCtx(ctx context.Context) (*language.Tag, bool) {
v := ctx.Value(I18nCurrentLangKey)
if l, b := v.(language.Tag); b {
return &l, true
}
return nil, false
}
func LocalizedString(ctx context.Context, defaultValue string, langMap map[language.Tag]string) string {
langTag, tagExists := FetchCurrentLanguageFromCtx(ctx)
if !tagExists {
return defaultValue
}
str, ok := langMap[*langTag]
if !ok {
return defaultValue
}
return str
}

View File

@@ -0,0 +1,21 @@
package i18n
const I18nKey = "i18n"
const I18nCurrentLangKey = "lang"
var (
defaultLangHeaderKey = "Accept-Language"
defaultErrCode uint32 = 10001
)
// SetDefaultLangHeaderKey sets the default value of the lang header key.
// need to be set before use
func SetDefaultLangHeaderKey(key string) {
defaultLangHeaderKey = key
}
// SetDefaultErrCode sets the default value of the err code.
// need to be set before use
func SetDefaultErrCode(code uint32) {
defaultErrCode = code
}

View File

@@ -0,0 +1,28 @@
package i18n
import (
"net/http"
"golang.org/x/text/language"
)
type I18nMiddleware struct {
supportTags []language.Tag
localizationFiles []string
}
func NewI18nMiddleware(supportTags []language.Tag, localizationFiles []string) *I18nMiddleware {
return &I18nMiddleware{
supportTags: supportTags,
localizationFiles: localizationFiles,
}
}
func (m *I18nMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
lang := r.Header.Get(defaultLangHeaderKey)
langTag := MatchCurrentLanguageTag(lang, m.supportTags)
bundle := NewBundle(langTag, m.localizationFiles...)
next(w, withRequest(r, langTag, bundle))
}
}

View File

@@ -0,0 +1,14 @@
package i18n
import "golang.org/x/text/language"
func MatchCurrentLanguageTag(accept string, supportTags []language.Tag) language.Tag {
langTags, _, err := language.ParseAcceptLanguage(accept)
if err != nil {
langTags = []language.Tag{language.English}
}
var matcher = language.NewMatcher(supportTags)
_, i, _ := matcher.Match(langTags...)
tag := supportTags[i]
return tag
}

View File

@@ -0,0 +1,74 @@
package i18n
import (
"context"
"log"
i18n2 "github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
var rpcInterceptorDebug = false
func WithRpcInterceptorDebug(debug bool) {
rpcInterceptorDebug = debug
}
type I18nGrpcInterceptor struct {
supportTags []language.Tag
localizationFiles []string
}
func NewI18nGrpcInterceptor(supportTags []language.Tag, localizationFiles []string) *I18nGrpcInterceptor {
if len(supportTags) == 0 {
panic("supportTags can not be empty")
}
return &I18nGrpcInterceptor{
supportTags: supportTags,
localizationFiles: localizationFiles,
}
}
func (i *I18nGrpcInterceptor) Interceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
ctx, err = i.saveLocalize(ctx)
if err != nil {
return nil, err
}
return handler(ctx, req)
}
func (i *I18nGrpcInterceptor) saveLocalize(ctx context.Context) (context.Context, error) {
respHeader, ok := metadata.FromIncomingContext(ctx)
// lang := "en-US" //language.English
langTag := i.supportTags[0]
lang := langTag.String()
if ok {
langs := respHeader.Get(defaultLangHeaderKey)
if len(langs) == 0 {
// return nil, status.Error(codes.Code(defaultErrCode), "can not correct get language")
if rpcInterceptorDebug {
log.Printf("can not correct get language")
}
} else {
lang = langs[0]
langTag = MatchCurrentLanguageTag(lang, i.supportTags)
}
} else {
if rpcInterceptorDebug {
log.Printf("can not correct get metadata1")
}
// return nil, status.Error(codes.Code(defaultErrCode), "can not correct get metadata1")
}
// lang := langs[0]
// langTag := MatchCurrentLanguageTag(lang, i.supportTags)
bundle := NewBundle(langTag, i.localizationFiles...)
localizer := i18n2.NewLocalizer(bundle, lang)
ctx = setLocalizer(ctx, localizer)
// Append the language metadata to the outgoing context
ctx = metadata.AppendToOutgoingContext(ctx, defaultLangHeaderKey, lang)
return ctx, nil
}

View File

@@ -0,0 +1,51 @@
package jwt
import (
"context"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/zeromicro/go-zero/core/logc"
)
type AccessJWTPayload struct {
UserID string `json:"user_id"`
Type string `json:"type" default:"access"`
}
type AccessJWTClaims struct {
AccessJWTPayload
jwt.RegisteredClaims
}
func GenerateAccessToken(secret string, payload AccessJWTPayload) string {
claims := AccessJWTClaims{
AccessJWTPayload: payload,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 15)),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
accessToken, err := token.SignedString([]byte(secret))
if err != nil {
logc.Error(context.Background(), err)
return ""
}
return accessToken
}
// ParseAccessToken parses a JWT token and returns the payload
func ParseAccessToken(secret string, tokenString string) *AccessJWTPayload {
token, err := jwt.ParseWithClaims(tokenString, &AccessJWTClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(secret), nil
})
if err != nil {
logc.Error(context.Background(), err)
return nil
}
if claims, ok := token.Claims.(*AccessJWTClaims); ok && token.Valid {
return &claims.AccessJWTPayload
}
return nil
}

View File

@@ -0,0 +1,52 @@
package jwt
import (
"context"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/zeromicro/go-zero/core/logc"
)
type RefreshJWTPayload struct {
UserID string `json:"user_id"`
Type string `json:"type" default:"refresh"`
}
type RefreshJWTClaims struct {
RefreshJWTPayload
jwt.RegisteredClaims
}
// GenerateRefreshToken generates a JWT token with the given payload, and returns the accessToken and refreshToken
func GenerateRefreshToken(secret string, payload RefreshJWTPayload, days time.Duration) string {
refreshClaims := RefreshJWTClaims{
RefreshJWTPayload: payload,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(days)),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
},
}
refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, refreshClaims)
refreshTokenString, err := refreshToken.SignedString([]byte(secret))
if err != nil {
logc.Error(context.Background(), err)
return ""
}
return refreshTokenString
}
// ParseRefreshToken parses a JWT token and returns the payload
func ParseRefreshToken(secret string, refreshTokenString string) *RefreshJWTPayload {
token, err := jwt.ParseWithClaims(refreshTokenString, &RefreshJWTClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(secret), nil
})
if err != nil {
logc.Error(context.Background(), err)
return nil
}
if claims, ok := token.Claims.(*RefreshJWTClaims); ok && token.Valid {
return &claims.RefreshJWTPayload
}
return nil
}

View File

@@ -0,0 +1,12 @@
package middleware
import "net/http"
func CORSMiddleware() func(http.Header) {
return func(header http.Header) {
header.Set("Access-Control-Allow-Origin", "*")
header.Add("Access-Control-Allow-Headers", "UserHeader1, UserHeader2")
header.Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH")
header.Set("Access-Control-Expose-Headers", "Content-Length, Content-Type")
}
}

View File

@@ -0,0 +1,24 @@
package middleware
import (
"net/http"
"os"
"path/filepath"
"golang.org/x/text/language"
"schisandra-album-cloud-microservices/app/core/api/common/i18n"
)
func I18nMiddleware(next http.HandlerFunc) http.HandlerFunc {
cwd, err := os.Getwd()
if err != nil {
panic(err)
}
zhPath := filepath.Join(cwd, "/resources/language/", "active.zh.toml")
enPath := filepath.Join(cwd, "/resources/language/", "active.en.toml")
return i18n.NewI18nMiddleware([]language.Tag{
language.English,
language.Chinese,
}, []string{enPath, zhPath}).Handle(next)
}

View File

@@ -0,0 +1,13 @@
package middleware
import "net/http"
func SecurityHeadersMiddleware(w http.ResponseWriter, r *http.Request) {
r.Header.Set("X-Frame-Options", "DENY")
r.Header.Set("Content-Security-Policy", "default-src 'self'; connect-src *; font-src *; script-src-elem * 'unsafe-inline'; img-src * data:; style-src * 'unsafe-inline';")
r.Header.Set("X-XSS-Protection", "1; mode=block")
r.Header.Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload")
r.Header.Set("Referrer-Policy", "strict-origin")
r.Header.Set("X-Content-Type-Options", "nosniff")
r.Header.Set("Permissions-Policy", "geolocation=(),midi=(),sync-xhr=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=()")
}

View File

@@ -0,0 +1,48 @@
package response
import "schisandra-album-cloud-microservices/app/core/api/internal/types"
// Success returns a success response with the given data.
func Success[T any](data T) *types.Response {
return &types.Response{
Code: 200,
Message: "success",
Data: data,
}
}
// SuccessWithMessage returns a success response with the given message.
func SuccessWithMessage(message string) *types.Response {
return &types.Response{
Code: 200,
Message: message,
Data: nil,
}
}
// Error returns an error response with the given message.
func Error() *types.Response {
return &types.Response{
Code: 500,
Message: "error",
Data: nil,
}
}
// ErrorWithCode returns an error response with the given code and message.
func ErrorWithCode(code int64, message string) *types.Response {
return &types.Response{
Code: code,
Message: message,
Data: nil,
}
}
// ErrorWithMessage returns an error response with the given message.
func ErrorWithMessage(message string) *types.Response {
return &types.Response{
Code: 500,
Message: message,
Data: nil,
}
}

View File

@@ -0,0 +1,20 @@
package utils
import "golang.org/x/crypto/bcrypt"
// Encrypt 加密
func Encrypt(val string) (string, error) {
// 使用bcrypt库的GenerateFromPassword函数进行哈希处理
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(val), bcrypt.DefaultCost)
if err != nil {
return "", err
}
return string(hashedPassword), err
}
// Verify 验证
func Verify(hashedVal string, val string) bool {
// 使用bcrypt库的CompareHashAndPassword函数比较密码
err := bcrypt.CompareHashAndPassword([]byte(hashedVal), []byte(val))
return err == nil
}

View File

@@ -0,0 +1,683 @@
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=\"http://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)
}

View File

@@ -0,0 +1,26 @@
package utils
import (
"net"
"net/http"
"strings"
)
// GetClientIP returns the client IP address from the request.
func GetClientIP(r *http.Request) string {
xForwardedFor := strings.TrimSpace(r.Header.Get("X-Forwarded-For"))
if xForwardedFor != "" {
return strings.Split(xForwardedFor, ",")[0]
}
ip := strings.TrimSpace(r.Header.Get("X-Real-Ip"))
if ip != "" {
return ip
}
ip, _, err := net.SplitHostPort(strings.TrimSpace(r.RemoteAddr))
if err != nil {
return ""
}
return ip
}

View File

@@ -0,0 +1,90 @@
package utils
import (
"encoding/base64"
"errors"
"io"
"regexp"
"strings"
"sync"
)
var wg sync.WaitGroup
// GetMimeType 获取 MIME 类型
func GetMimeType(data []byte) string {
if len(data) < 4 {
return "application/octet-stream" // 默认类型
}
// 判断 JPEG
if data[0] == 0xFF && data[1] == 0xD8 {
return "image/jpeg"
}
// 判断 PNG
if len(data) >= 8 && data[0] == 0x89 && data[1] == 0x50 && data[2] == 0x4E && data[3] == 0x47 &&
data[4] == 0x0D && data[5] == 0x0A && data[6] == 0x1A && data[7] == 0x0A {
return "image/png"
}
// 判断 GIF
if len(data) >= 6 && data[0] == 'G' && data[1] == 'I' && data[2] == 'F' {
return "image/gif"
}
// 判断 WEBP
if len(data) >= 12 && data[0] == 0x52 && data[1] == 0x49 && data[2] == 0x46 && data[3] == 0x46 &&
data[8] == 0x57 && data[9] == 0x45 && data[10] == 0x42 && data[11] == 0x50 {
return "image/webp"
}
// 判断svg
if len(data) >= 4 && data[0] == '<' && data[1] == '?' && data[2] == 'x' && data[3] == 'm' {
return "image/svg+xml"
}
// 判断JPG
if len(data) >= 3 && data[0] == 0xFF && data[1] == 0xD8 && data[2] == 0xFF {
return "image/jpeg"
}
return "application/octet-stream" // 默认类型
}
// ProcessImages 处理图片,将 base64 字符串转换为字节数组
func ProcessImages(images []string) ([][]byte, error) {
var imagesData [][]byte
dataChan := make(chan []byte, len(images)) // 创建一个带缓冲的 channel
re := regexp.MustCompile(`^data:image/\w+;base64,`)
for _, img := range images {
wg.Add(1) // 增加 WaitGroup 的计数
go func(img string) {
defer wg.Done() // 函数结束时减少计数
imgWithoutPrefix := re.ReplaceAllString(img, "")
data, err := Base64ToBytes(imgWithoutPrefix)
if err != nil {
return // 出错时直接返回
}
dataChan <- data // 将结果发送到 channel
}(img)
}
wg.Wait() // 等待所有 goroutine 完成
close(dataChan) // 关闭 channel
for data := range dataChan { // 收集所有结果
imagesData = append(imagesData, data)
}
return imagesData, nil
}
// Base64ToBytes 将base64字符串转换为字节数组
func Base64ToBytes(base64Str string) ([]byte, error) {
reader := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64Str))
data, err := io.ReadAll(reader)
if err != nil {
return nil, errors.New("failed to decode base64 string")
}
return data, nil
}

View File

@@ -0,0 +1,21 @@
package utils
import (
"regexp"
"strings"
)
func RemoveZeroAndAdjust(s string) string {
// 正则表达式匹配 "|0|" 或 "|0" 或 "0|" 并替换为 "|"
re := regexp.MustCompile(`(\|0|0\||0)`)
result := re.ReplaceAllString(s, "|")
// 移除可能出现的连续 "|"
re = regexp.MustCompile(`\|+`)
result = re.ReplaceAllString(result, "|")
// 移除字符串开头和结尾可能出现的 "|"
result = strings.Trim(result, "|")
return result
}

View File

@@ -0,0 +1,39 @@
package utils
import "regexp"
// IsEmail 判断是否为邮箱
func IsEmail(email string) bool {
// 邮箱的正则表达式
emailRegex := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
match, _ := regexp.MatchString(emailRegex, email)
return match
}
// IsPhone 判断是否为手机号
func IsPhone(phone string) bool {
// 手机号的正则表达式,这里以中国大陆的手机号为例
phoneRegex := `^1[3-9]\d{9}$`
match, _ := regexp.MatchString(phoneRegex, phone)
return match
}
// IsUsername 用户名的正则表达式
func IsUsername(username string) bool {
/**
1.用户名仅能使用数字,大小写字母和下划线。
2.用户名中的数字必须在最后。 数字可以有零个或多个。
3.用户名不能以数字开头。 用户名字母可以是小写字母和大写字母。
4.用户名长度必须至少为3个字符。 两位用户名只能使用字母最多20个字符
*/
phoneRegex := `^[a-zA-Z_]{2,18}[0-9]*$`
match, _ := regexp.MatchString(phoneRegex, username)
return match
}
// IsPassword 密码的正则表达式
func IsPassword(password string) bool {
phoneRegex := `^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{6,18}$`
match, _ := regexp.MatchString(phoneRegex, password)
return match
}

View File

@@ -35,10 +35,19 @@ type (
LoginResponse {
AccessToken string `json:"access_token"`
UID string `json:"uid"`
Username string `json:"username,optional"`
Username string `json:"username,omitempty"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
Status int64 `json:"status"`
Status int8 `json:"status"`
}
)
// 统一响应参数
type (
Response {
Code int64 `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,optional"`
}
)
@@ -56,14 +65,30 @@ type (
service core {
// 账户登录
@handler accountLogin
post /login (AccountLoginRequest) returns (LoginResponse)
post /login (AccountLoginRequest) returns (Response)
// 手机号登录
@handler phoneLogin
post /phone_login (PhoneLoginRequest) returns (LoginResponse)
post /phone_login (PhoneLoginRequest) returns (Response)
// 重置密码
@handler resetPassword
post /reset_password (ResetPasswordRequest) returns (string)
post /reset_password (ResetPasswordRequest) returns (Response)
}
// 客户端服务
@server (
group: client // 微服务分组
prefix: /api/client // 微服务前缀
timeout: 10s // 超时时间
maxBytes: 1048576 // 最大请求大小
signature: false // 是否开启签名验证
middleware: SecurityHeadersMiddleware // 注册中间件
MaxConns: true // 是否开启最大连接数限制
Recover: true // 是否开启自动恢复
)
service core {
@handler generateClientId
get /generate_client_id returns (Response)
}

View File

@@ -4,10 +4,10 @@ import (
"flag"
"fmt"
"schisandra-album-cloud-microservices/app/core/api/common/middleware"
"schisandra-album-cloud-microservices/app/core/api/internal/config"
"schisandra-album-cloud-microservices/app/core/api/internal/handler"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/common/middleware"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/rest"
@@ -21,7 +21,7 @@ func main() {
var c config.Config
conf.MustLoad(*configFile, &c)
server := rest.MustNewServer(c.RestConf, rest.WithCustomCors(middleware.CORSMiddleware(), nil, "*"))
server := rest.MustNewServer(c.RestConf, rest.WithCors())
defer server.Stop()
// i18n middleware
server.Use(middleware.I18nMiddleware)

View File

@@ -1,6 +1,10 @@
Name: main
Host: 0.0.0.0
Port: 8888
Port: 80
Web:
Addr: localhost:8080
Middlewares:
Log: true
Mysql:
DataSource: root:1611@tcp(localhost:3306)/schisandra-cloud-album?charset=utf8mb4&parseTime=True&loc=Local
Auth:
@@ -8,9 +12,8 @@ Auth:
AccessExpire: 86400
Redis:
Host: 1.95.0.111:6379
Type: node
Pass: LDQ20020618xxx
Tls: false
DB: 0
Mongo:
Uri: mongodb://1.95.0.111:27017
Username: landaiqing

View File

@@ -13,9 +13,8 @@ type Config struct {
}
Redis struct {
Host string
Type string
Pass string
Tls bool
DB int
}
Mongo struct {
Uri string

View File

@@ -0,0 +1,24 @@
package client
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"schisandra-album-cloud-microservices/app/core/api/common/utils"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/client"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
)
func GenerateClientIdHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
clientIP := utils.GetClientIP(r)
l := client.NewGenerateClientIdLogic(r.Context(), svcCtx)
resp, err := l.GenerateClientId(clientIP)
if err != nil || resp.Code == 500 {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

View File

@@ -7,6 +7,7 @@ import (
"net/http"
"time"
client "schisandra-album-cloud-microservices/app/core/api/internal/handler/client"
user "schisandra-album-cloud-microservices/app/core/api/internal/handler/user"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
@@ -14,6 +15,22 @@ import (
)
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware},
[]rest.Route{
{
Method: http.MethodGet,
Path: "/generate_client_id",
Handler: client.GenerateClientIdHandler(serverCtx),
},
}...,
),
rest.WithPrefix("/api/client"),
rest.WithTimeout(10000*time.Millisecond),
rest.WithMaxBytes(1048576),
)
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware},

View File

@@ -19,8 +19,8 @@ func AccountLoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
}
l := user.NewAccountLoginLogic(r.Context(), svcCtx)
resp, err := l.AccountLogin(&req)
if err != nil {
resp, err := l.AccountLogin(w, r, &req)
if err != nil || resp.Code == 500 {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)

View File

@@ -20,7 +20,7 @@ func PhoneLoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
l := user.NewPhoneLoginLogic(r.Context(), svcCtx)
resp, err := l.PhoneLogin(&req)
if err != nil {
if err != nil || resp.Code == 500 {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)

View File

@@ -20,7 +20,7 @@ func ResetPasswordHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
l := user.NewResetPasswordLogic(r.Context(), svcCtx)
resp, err := l.ResetPassword(&req)
if err != nil {
if err != nil || resp.Code == 500 {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)

View File

@@ -0,0 +1,43 @@
package client
import (
"context"
"time"
"github.com/ccpwcn/kgo"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
"schisandra-album-cloud-microservices/app/core/api/common/response"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GenerateClientIdLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGenerateClientIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GenerateClientIdLogic {
return &GenerateClientIdLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GenerateClientIdLogic) GenerateClientId(clientIP string) (resp *types.Response, err error) {
clientId := l.svcCtx.RedisClient.Get(l.ctx, constant.UserClientPrefix+clientIP).Val()
if clientId != "" {
return response.Success(clientId), nil
}
simpleUuid := kgo.SimpleUuid()
if err = l.svcCtx.RedisClient.SetEx(l.ctx, constant.UserClientPrefix+clientIP, simpleUuid, time.Hour*24*7).Err(); err != nil {
l.Error(err)
return response.Error(), err
}
return response.Success(simpleUuid), nil
}

View File

@@ -2,14 +2,27 @@ package user
import (
"context"
"net/http"
"time"
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
"github.com/mssola/useragent"
"github.com/zeromicro/go-zero/core/logc"
"github.com/zeromicro/go-zero/core/logx"
"schisandra-album-cloud-microservices/app/core/api/common/captcha/verify"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
"schisandra-album-cloud-microservices/app/core/api/common/i18n"
"schisandra-album-cloud-microservices/app/core/api/common/jwt"
"schisandra-album-cloud-microservices/app/core/api/common/response"
"schisandra-album-cloud-microservices/app/core/api/common/utils"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"schisandra-album-cloud-microservices/app/core/api/repository/mongodb/collection"
"schisandra-album-cloud-microservices/app/core/api/repository/mongodb/model"
"schisandra-album-cloud-microservices/common/i18n"
"github.com/zeromicro/go-zero/core/logx"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent/scaauthuser"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent/scaauthuserdevice"
types3 "schisandra-album-cloud-microservices/app/core/api/repository/redis_session/types"
types2 "schisandra-album-cloud-microservices/app/core/api/repository/redisx/types"
)
type AccountLoginLogic struct {
@@ -26,13 +39,160 @@ func NewAccountLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Acco
}
}
func (l *AccountLoginLogic) AccountLogin(req *types.AccountLoginRequest) (resp *types.LoginResponse, err error) {
// todo: add your logic here and delete this line
i18n.IsHasI18n(l.ctx)
text := i18n.FormatText(l.ctx, "user.name", "landaiqing")
collection.MustNewCollection[model.CommentImage](l.svcCtx, "comment_image")
func (l *AccountLoginLogic) AccountLogin(w http.ResponseWriter, r *http.Request, req *types.AccountLoginRequest) (resp *types.Response, err error) {
verifyResult := verify.VerifyRotateCaptcha(l.ctx, l.svcCtx.RedisClient, req.Angle, req.Key)
if !verifyResult {
return response.ErrorWithMessage(i18n.FormatText(l.ctx, "captcha.verificationFailure", "验证失败!")), nil
}
var user *ent.ScaAuthUser
var query *ent.ScaAuthUserQuery
return &types.LoginResponse{
AccessToken: text,
}, nil
switch {
case utils.IsPhone(req.Account):
query = l.svcCtx.MySQLClient.ScaAuthUser.Query().Where(scaauthuser.PhoneEQ(req.Account), scaauthuser.DeletedEQ(0))
case utils.IsEmail(req.Account):
query = l.svcCtx.MySQLClient.ScaAuthUser.Query().Where(scaauthuser.EmailEQ(req.Account), scaauthuser.DeletedEQ(0))
case utils.IsUsername(req.Account):
query = l.svcCtx.MySQLClient.ScaAuthUser.Query().Where(scaauthuser.UsernameEQ(req.Account), scaauthuser.DeletedEQ(0))
default:
return response.ErrorWithMessage(i18n.FormatText(l.ctx, "login.invalidAccount", "无效账号!")), nil
}
user, err = query.First(l.ctx)
if err != nil {
if ent.IsNotFound(err) {
return response.ErrorWithMessage(i18n.FormatText(l.ctx, "login.notFoundAccount", "无效账号!")), nil
}
return nil, err
}
if !utils.Verify(user.Password, req.Password) {
return response.ErrorWithMessage(i18n.FormatText(l.ctx, "login.invalidPassword", "密码错误!")), nil
}
data, result := HandleUserLogin(user, l, req.AutoLogin, r, w, l.svcCtx.Ip2Region, l.svcCtx.MySQLClient)
if !result {
return response.ErrorWithMessage(i18n.FormatText(l.ctx, "login.loginFailed", "登录失败!")), nil
}
return response.Success(data), nil
}
// HandleUserLogin 处理用户登录
func HandleUserLogin(user *ent.ScaAuthUser, l *AccountLoginLogic, autoLogin bool, r *http.Request, w http.ResponseWriter, ip2location *xdb.Searcher, entClient *ent.Client) (*types.LoginResponse, bool) {
// 生成jwt token
accessToken := jwt.GenerateAccessToken(l.svcCtx.Config.Auth.AccessSecret, jwt.AccessJWTPayload{
UserID: user.UID,
})
var days time.Duration
if autoLogin {
days = 7 * 24 * time.Hour
} else {
days = time.Hour * 24
}
refreshToken := jwt.GenerateRefreshToken(l.svcCtx.Config.Auth.AccessSecret, jwt.RefreshJWTPayload{
UserID: user.UID,
}, days)
data := types.LoginResponse{
AccessToken: accessToken,
UID: user.UID,
Username: user.Username,
Nickname: user.Nickname,
Avatar: user.Avatar,
Status: user.Status,
}
redisToken := types2.RedisToken{
AccessToken: accessToken,
UID: user.UID,
}
err := l.svcCtx.RedisClient.SetEx(l.ctx, constant.UserTokenPrefix+user.UID, redisToken, days).Err()
if err != nil {
logc.Error(l.ctx, err)
return nil, false
}
sessionData := types3.SessionData{
RefreshToken: refreshToken,
UID: user.UID,
}
session, err := l.svcCtx.Session.Get(r, constant.SESSION_KEY)
if err != nil {
logc.Error(l.ctx, err)
return nil, false
}
session.Values[constant.SESSION_KEY] = sessionData
if err = session.Save(r, w); err != nil {
logc.Error(l.ctx, err)
return nil, false
}
// 记录用户登录设备
if !getUserLoginDevice(user.UID, r, ip2location, entClient, l.ctx) {
return nil, false
}
return &data, true
}
// getUserLoginDevice 获取用户登录设备
func getUserLoginDevice(userId string, r *http.Request, ip2location *xdb.Searcher, entClient *ent.Client, ctx context.Context) bool {
userAgent := r.Header.Get("User-Agent")
if userAgent == "" {
return false
}
ip := utils.GetClientIP(r)
location, err := ip2location.SearchByStr(ip)
if err != nil {
return false
}
location = utils.RemoveZeroAndAdjust(location)
ua := useragent.New(userAgent)
isBot := ua.Bot()
browser, browserVersion := ua.Browser()
os := ua.OS()
mobile := ua.Mobile()
mozilla := ua.Mozilla()
platform := ua.Platform()
engine, engineVersion := ua.Engine()
device, err := entClient.ScaAuthUserDevice.Query().
Where(scaauthuserdevice.UserID(userId), scaauthuserdevice.IP(ip), scaauthuserdevice.Agent(userAgent)).
Only(ctx)
// 如果有错误,表示设备不存在,执行插入
if ent.IsNotFound(err) {
// 创建新的设备记录
entClient.ScaAuthUserDevice.Create().
SetBot(isBot).
SetAgent(userAgent).
SetBrowser(browser).
SetBrowserVersion(browserVersion).
SetEngineName(engine).
SetEngineVersion(engineVersion).
SetIP(ip).
SetLocation(location).
SetOperatingSystem(os).
SetMobile(mobile).
SetMozilla(mozilla).
SetPlatform(platform).
SaveX(ctx)
} else if err == nil {
// 如果设备存在,执行更新
device.Update().
SetBot(isBot).
SetAgent(userAgent).
SetBrowser(browser).
SetBrowserVersion(browserVersion).
SetEngineName(engine).
SetEngineVersion(engineVersion).
SetIP(ip).
SetLocation(location).
SetOperatingSystem(os).
SetMobile(mobile).
SetMozilla(mozilla).
SetPlatform(platform).
SaveX(ctx)
} else {
logc.Error(ctx, err)
return false
}
return true
}

View File

@@ -3,10 +3,10 @@ package user
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type PhoneLoginLogic struct {
@@ -23,7 +23,7 @@ func NewPhoneLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PhoneL
}
}
func (l *PhoneLoginLogic) PhoneLogin(req *types.PhoneLoginRequest) (resp *types.LoginResponse, err error) {
func (l *PhoneLoginLogic) PhoneLogin(req *types.PhoneLoginRequest) (resp *types.Response, err error) {
// todo: add your logic here and delete this line
return

View File

@@ -3,10 +3,10 @@ package user
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type ResetPasswordLogic struct {
@@ -23,7 +23,7 @@ func NewResetPasswordLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Res
}
}
func (l *ResetPasswordLogic) ResetPassword(req *types.ResetPasswordRequest) (resp string, err error) {
func (l *ResetPasswordLogic) ResetPassword(req *types.ResetPasswordRequest) (resp *types.Response, err error) {
// todo: add your logic here and delete this line
return

View File

@@ -3,7 +3,7 @@ package middleware
import (
"net/http"
"schisandra-album-cloud-microservices/common/middleware"
"schisandra-album-cloud-microservices/app/core/api/common/middleware"
)
type SecurityHeadersMiddleware struct {

View File

@@ -1,24 +1,31 @@
package svc
import (
"github.com/zeromicro/go-zero/core/stores/redis"
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
"github.com/rbcervilla/redisstore/v9"
"github.com/redis/go-redis/v9"
"github.com/zeromicro/go-zero/rest"
"go.mongodb.org/mongo-driver/v2/mongo"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent"
"schisandra-album-cloud-microservices/app/core/api/internal/config"
"schisandra-album-cloud-microservices/app/core/api/internal/middleware"
"schisandra-album-cloud-microservices/app/core/api/repository/ip2region"
"schisandra-album-cloud-microservices/app/core/api/repository/mongodb"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent"
"schisandra-album-cloud-microservices/app/core/api/repository/redis_session"
"schisandra-album-cloud-microservices/app/core/api/repository/redisx"
)
type ServiceContext struct {
Config config.Config
SecurityHeadersMiddleware rest.Middleware
MySQLClient *ent.Client
RedisClient *redis.Redis
RedisClient *redis.Client
MongoClient *mongo.Database
Session *redisstore.RedisStore
Ip2Region *xdb.Searcher
}
func NewServiceContext(c config.Config) *ServiceContext {
@@ -26,12 +33,9 @@ func NewServiceContext(c config.Config) *ServiceContext {
Config: c,
SecurityHeadersMiddleware: middleware.NewSecurityHeadersMiddleware().Handle,
MySQLClient: mysql.NewMySQL(c.Mysql.DataSource),
RedisClient: redis.MustNewRedis(redis.RedisConf{
Host: c.Redis.Host,
Pass: c.Redis.Pass,
Type: c.Redis.Type,
Tls: c.Redis.Tls,
}),
MongoClient: mongodb.NewMongoDB(c.Mongo.Uri, c.Mongo.Username, c.Mongo.Password, c.Mongo.AuthSource, c.Mongo.Database),
RedisClient: redisx.NewRedis(c.Redis.Host, c.Redis.Pass, c.Redis.DB),
MongoClient: mongodb.NewMongoDB(c.Mongo.Uri, c.Mongo.Username, c.Mongo.Password, c.Mongo.AuthSource, c.Mongo.Database),
Session: redis_session.NewRedisSession(c.Redis.Host, c.Redis.Pass),
Ip2Region: ip2region.NewIP2Region(),
}
}

View File

@@ -14,10 +14,10 @@ type AccountLoginRequest struct {
type LoginResponse struct {
AccessToken string `json:"access_token"`
UID string `json:"uid"`
Username string `json:"username,optional"`
Username string `json:"username,omitempty"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
Status int64 `json:"status"`
Status int8 `json:"status"`
}
type PhoneLoginRequest struct {
@@ -32,3 +32,9 @@ type ResetPasswordRequest struct {
Password string `json:"password"`
Repassword string `json:"repassword"`
}
type Response struct {
Code int64 `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,optional"`
}

View File

@@ -0,0 +1,27 @@
package ip2region
import (
"os"
"path/filepath"
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
)
// NewIP2Region creates a new IP2Region searcher instance.
func NewIP2Region() *xdb.Searcher {
cwd, err := os.Getwd()
if err != nil {
panic(err)
}
dbPath := filepath.Join(cwd, "resources/ip2region", "ip2region.xdb")
cBuff, err := xdb.LoadContentFromFile(dbPath)
if err != nil {
panic(err)
}
searcher, err := xdb.NewWithBuffer(cBuff)
if err != nil {
panic(err)
return nil
}
return searcher
}

View File

@@ -1,4 +1,4 @@
package model
package types
import "github.com/chenmingyong0423/go-mongox/v2"

View File

@@ -107,8 +107,8 @@ var schemaGraph = func() *sqlgraph.Schema {
scaauthuserdevice.FieldBrowser: {Type: field.TypeString, Column: scaauthuserdevice.FieldBrowser},
scaauthuserdevice.FieldOperatingSystem: {Type: field.TypeString, Column: scaauthuserdevice.FieldOperatingSystem},
scaauthuserdevice.FieldBrowserVersion: {Type: field.TypeString, Column: scaauthuserdevice.FieldBrowserVersion},
scaauthuserdevice.FieldMobile: {Type: field.TypeInt, Column: scaauthuserdevice.FieldMobile},
scaauthuserdevice.FieldBot: {Type: field.TypeInt, Column: scaauthuserdevice.FieldBot},
scaauthuserdevice.FieldMobile: {Type: field.TypeBool, Column: scaauthuserdevice.FieldMobile},
scaauthuserdevice.FieldBot: {Type: field.TypeBool, Column: scaauthuserdevice.FieldBot},
scaauthuserdevice.FieldMozilla: {Type: field.TypeString, Column: scaauthuserdevice.FieldMozilla},
scaauthuserdevice.FieldPlatform: {Type: field.TypeString, Column: scaauthuserdevice.FieldPlatform},
scaauthuserdevice.FieldEngineName: {Type: field.TypeString, Column: scaauthuserdevice.FieldEngineName},
@@ -622,13 +622,13 @@ func (f *ScaAuthUserDeviceFilter) WhereBrowserVersion(p entql.StringP) {
f.Where(p.Field(scaauthuserdevice.FieldBrowserVersion))
}
// WhereMobile applies the entql int predicate on the mobile field.
func (f *ScaAuthUserDeviceFilter) WhereMobile(p entql.IntP) {
// WhereMobile applies the entql bool predicate on the mobile field.
func (f *ScaAuthUserDeviceFilter) WhereMobile(p entql.BoolP) {
f.Where(p.Field(scaauthuserdevice.FieldMobile))
}
// WhereBot applies the entql int predicate on the bot field.
func (f *ScaAuthUserDeviceFilter) WhereBot(p entql.IntP) {
// WhereBot applies the entql bool predicate on the bot field.
func (f *ScaAuthUserDeviceFilter) WhereBot(p entql.BoolP) {
f.Where(p.Field(scaauthuserdevice.FieldBot))
}

View File

@@ -109,8 +109,8 @@ var (
{Name: "browser", Type: field.TypeString, Size: 20, Comment: "浏览器"},
{Name: "operating_system", Type: field.TypeString, Size: 20, Comment: "操作系统"},
{Name: "browser_version", Type: field.TypeString, Size: 20, Comment: "浏览器版本"},
{Name: "mobile", Type: field.TypeInt, Comment: "是否为手机 0否1是"},
{Name: "bot", Type: field.TypeInt, Comment: "是否为bot 0否1是"},
{Name: "mobile", Type: field.TypeBool, Comment: "是否为手机 0否1是"},
{Name: "bot", Type: field.TypeBool, Comment: "是否为bot 0否1是"},
{Name: "mozilla", Type: field.TypeString, Size: 10, Comment: "火狐版本"},
{Name: "platform", Type: field.TypeString, Size: 20, Comment: "平台"},
{Name: "engine_name", Type: field.TypeString, Size: 20, Comment: "引擎名称"},

View File

@@ -3193,10 +3193,8 @@ type ScaAuthUserDeviceMutation struct {
browser *string
operating_system *string
browser_version *string
mobile *int
addmobile *int
bot *int
addbot *int
mobile *bool
bot *bool
mozilla *string
platform *string
engine_name *string
@@ -3708,13 +3706,12 @@ func (m *ScaAuthUserDeviceMutation) ResetBrowserVersion() {
}
// SetMobile sets the "mobile" field.
func (m *ScaAuthUserDeviceMutation) SetMobile(i int) {
m.mobile = &i
m.addmobile = nil
func (m *ScaAuthUserDeviceMutation) SetMobile(b bool) {
m.mobile = &b
}
// Mobile returns the value of the "mobile" field in the mutation.
func (m *ScaAuthUserDeviceMutation) Mobile() (r int, exists bool) {
func (m *ScaAuthUserDeviceMutation) Mobile() (r bool, exists bool) {
v := m.mobile
if v == nil {
return
@@ -3725,7 +3722,7 @@ func (m *ScaAuthUserDeviceMutation) Mobile() (r int, exists bool) {
// OldMobile returns the old "mobile" field's value of the ScaAuthUserDevice entity.
// If the ScaAuthUserDevice object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *ScaAuthUserDeviceMutation) OldMobile(ctx context.Context) (v int, err error) {
func (m *ScaAuthUserDeviceMutation) OldMobile(ctx context.Context) (v bool, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldMobile is only allowed on UpdateOne operations")
}
@@ -3739,38 +3736,18 @@ func (m *ScaAuthUserDeviceMutation) OldMobile(ctx context.Context) (v int, err e
return oldValue.Mobile, nil
}
// AddMobile adds i to the "mobile" field.
func (m *ScaAuthUserDeviceMutation) AddMobile(i int) {
if m.addmobile != nil {
*m.addmobile += i
} else {
m.addmobile = &i
}
}
// AddedMobile returns the value that was added to the "mobile" field in this mutation.
func (m *ScaAuthUserDeviceMutation) AddedMobile() (r int, exists bool) {
v := m.addmobile
if v == nil {
return
}
return *v, true
}
// ResetMobile resets all changes to the "mobile" field.
func (m *ScaAuthUserDeviceMutation) ResetMobile() {
m.mobile = nil
m.addmobile = nil
}
// SetBot sets the "bot" field.
func (m *ScaAuthUserDeviceMutation) SetBot(i int) {
m.bot = &i
m.addbot = nil
func (m *ScaAuthUserDeviceMutation) SetBot(b bool) {
m.bot = &b
}
// Bot returns the value of the "bot" field in the mutation.
func (m *ScaAuthUserDeviceMutation) Bot() (r int, exists bool) {
func (m *ScaAuthUserDeviceMutation) Bot() (r bool, exists bool) {
v := m.bot
if v == nil {
return
@@ -3781,7 +3758,7 @@ func (m *ScaAuthUserDeviceMutation) Bot() (r int, exists bool) {
// OldBot returns the old "bot" field's value of the ScaAuthUserDevice entity.
// If the ScaAuthUserDevice object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *ScaAuthUserDeviceMutation) OldBot(ctx context.Context) (v int, err error) {
func (m *ScaAuthUserDeviceMutation) OldBot(ctx context.Context) (v bool, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldBot is only allowed on UpdateOne operations")
}
@@ -3795,28 +3772,9 @@ func (m *ScaAuthUserDeviceMutation) OldBot(ctx context.Context) (v int, err erro
return oldValue.Bot, nil
}
// AddBot adds i to the "bot" field.
func (m *ScaAuthUserDeviceMutation) AddBot(i int) {
if m.addbot != nil {
*m.addbot += i
} else {
m.addbot = &i
}
}
// AddedBot returns the value that was added to the "bot" field in this mutation.
func (m *ScaAuthUserDeviceMutation) AddedBot() (r int, exists bool) {
v := m.addbot
if v == nil {
return
}
return *v, true
}
// ResetBot resets all changes to the "bot" field.
func (m *ScaAuthUserDeviceMutation) ResetBot() {
m.bot = nil
m.addbot = nil
}
// SetMozilla sets the "mozilla" field.
@@ -4246,14 +4204,14 @@ func (m *ScaAuthUserDeviceMutation) SetField(name string, value ent.Value) error
m.SetBrowserVersion(v)
return nil
case scaauthuserdevice.FieldMobile:
v, ok := value.(int)
v, ok := value.(bool)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetMobile(v)
return nil
case scaauthuserdevice.FieldBot:
v, ok := value.(int)
v, ok := value.(bool)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
@@ -4298,12 +4256,6 @@ func (m *ScaAuthUserDeviceMutation) AddedFields() []string {
if m.adddeleted != nil {
fields = append(fields, scaauthuserdevice.FieldDeleted)
}
if m.addmobile != nil {
fields = append(fields, scaauthuserdevice.FieldMobile)
}
if m.addbot != nil {
fields = append(fields, scaauthuserdevice.FieldBot)
}
return fields
}
@@ -4314,10 +4266,6 @@ func (m *ScaAuthUserDeviceMutation) AddedField(name string) (ent.Value, bool) {
switch name {
case scaauthuserdevice.FieldDeleted:
return m.AddedDeleted()
case scaauthuserdevice.FieldMobile:
return m.AddedMobile()
case scaauthuserdevice.FieldBot:
return m.AddedBot()
}
return nil, false
}
@@ -4334,20 +4282,6 @@ func (m *ScaAuthUserDeviceMutation) AddField(name string, value ent.Value) error
}
m.AddDeleted(v)
return nil
case scaauthuserdevice.FieldMobile:
v, ok := value.(int)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.AddMobile(v)
return nil
case scaauthuserdevice.FieldBot:
v, ok := value.(int)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.AddBot(v)
return nil
}
return fmt.Errorf("unknown ScaAuthUserDevice numeric field %s", name)
}

View File

@@ -8,7 +8,7 @@ import (
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent/scaauthuser"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent/scaauthuserdevice"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent/scaauthusersocial"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/schema"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/model"
"time"
)
@@ -16,7 +16,7 @@ import (
// (default values, validators, hooks and policies) and stitches it
// to their package variables.
func init() {
scaauthpermissionruleFields := schema.ScaAuthPermissionRule{}.Fields()
scaauthpermissionruleFields := model.ScaAuthPermissionRule{}.Fields()
_ = scaauthpermissionruleFields
// scaauthpermissionruleDescPtype is the schema descriptor for ptype field.
scaauthpermissionruleDescPtype := scaauthpermissionruleFields[1].Descriptor()
@@ -46,10 +46,10 @@ func init() {
scaauthpermissionruleDescV5 := scaauthpermissionruleFields[7].Descriptor()
// scaauthpermissionrule.V5Validator is a validator for the "v5" field. It is called by the builders before save.
scaauthpermissionrule.V5Validator = scaauthpermissionruleDescV5.Validators[0].(func(string) error)
scaauthroleMixin := schema.ScaAuthRole{}.Mixin()
scaauthroleMixin := model.ScaAuthRole{}.Mixin()
scaauthroleMixinFields0 := scaauthroleMixin[0].Fields()
_ = scaauthroleMixinFields0
scaauthroleFields := schema.ScaAuthRole{}.Fields()
scaauthroleFields := model.ScaAuthRole{}.Fields()
_ = scaauthroleFields
// scaauthroleDescCreatedAt is the schema descriptor for created_at field.
scaauthroleDescCreatedAt := scaauthroleMixinFields0[0].Descriptor()
@@ -75,10 +75,10 @@ func init() {
scaauthroleDescRoleKey := scaauthroleFields[2].Descriptor()
// scaauthrole.RoleKeyValidator is a validator for the "role_key" field. It is called by the builders before save.
scaauthrole.RoleKeyValidator = scaauthroleDescRoleKey.Validators[0].(func(string) error)
scaauthuserMixin := schema.ScaAuthUser{}.Mixin()
scaauthuserMixin := model.ScaAuthUser{}.Mixin()
scaauthuserMixinFields0 := scaauthuserMixin[0].Fields()
_ = scaauthuserMixinFields0
scaauthuserFields := schema.ScaAuthUser{}.Fields()
scaauthuserFields := model.ScaAuthUser{}.Fields()
_ = scaauthuserFields
// scaauthuserDescCreatedAt is the schema descriptor for created_at field.
scaauthuserDescCreatedAt := scaauthuserMixinFields0[0].Descriptor()
@@ -144,10 +144,10 @@ func init() {
scaauthuserDescCompany := scaauthuserFields[13].Descriptor()
// scaauthuser.CompanyValidator is a validator for the "company" field. It is called by the builders before save.
scaauthuser.CompanyValidator = scaauthuserDescCompany.Validators[0].(func(string) error)
scaauthuserdeviceMixin := schema.ScaAuthUserDevice{}.Mixin()
scaauthuserdeviceMixin := model.ScaAuthUserDevice{}.Mixin()
scaauthuserdeviceMixinFields0 := scaauthuserdeviceMixin[0].Fields()
_ = scaauthuserdeviceMixinFields0
scaauthuserdeviceFields := schema.ScaAuthUserDevice{}.Fields()
scaauthuserdeviceFields := model.ScaAuthUserDevice{}.Fields()
_ = scaauthuserdeviceFields
// scaauthuserdeviceDescCreatedAt is the schema descriptor for created_at field.
scaauthuserdeviceDescCreatedAt := scaauthuserdeviceMixinFields0[0].Descriptor()
@@ -209,10 +209,10 @@ func init() {
scaauthuserdeviceDescEngineVersion := scaauthuserdeviceFields[13].Descriptor()
// scaauthuserdevice.EngineVersionValidator is a validator for the "engine_version" field. It is called by the builders before save.
scaauthuserdevice.EngineVersionValidator = scaauthuserdeviceDescEngineVersion.Validators[0].(func(string) error)
scaauthusersocialMixin := schema.ScaAuthUserSocial{}.Mixin()
scaauthusersocialMixin := model.ScaAuthUserSocial{}.Mixin()
scaauthusersocialMixinFields0 := scaauthusersocialMixin[0].Fields()
_ = scaauthusersocialMixinFields0
scaauthusersocialFields := schema.ScaAuthUserSocial{}.Fields()
scaauthusersocialFields := model.ScaAuthUserSocial{}.Fields()
_ = scaauthusersocialFields
// scaauthusersocialDescCreatedAt is the schema descriptor for created_at field.
scaauthusersocialDescCreatedAt := scaauthusersocialMixinFields0[0].Descriptor()

View File

@@ -2,7 +2,7 @@
package runtime
// The schema-stitching logic is generated in schisandra-album-cloud-microservices/app/main/api/repository/mysql/ent/runtime.go
// The schema-stitching logic is generated in schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent/runtime.go
const (
Version = "v0.14.1" // Version of ent codegen.

View File

@@ -40,9 +40,9 @@ type ScaAuthUserDevice struct {
// 浏览器版本
BrowserVersion string `json:"browser_version,omitempty"`
// 是否为手机 0否1是
Mobile int `json:"mobile,omitempty"`
Mobile bool `json:"mobile,omitempty"`
// 是否为bot 0否1是
Bot int `json:"bot,omitempty"`
Bot bool `json:"bot,omitempty"`
// 火狐版本
Mozilla string `json:"mozilla,omitempty"`
// 平台
@@ -83,7 +83,9 @@ func (*ScaAuthUserDevice) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns))
for i := range columns {
switch columns[i] {
case scaauthuserdevice.FieldID, scaauthuserdevice.FieldDeleted, scaauthuserdevice.FieldMobile, scaauthuserdevice.FieldBot:
case scaauthuserdevice.FieldMobile, scaauthuserdevice.FieldBot:
values[i] = new(sql.NullBool)
case scaauthuserdevice.FieldID, scaauthuserdevice.FieldDeleted:
values[i] = new(sql.NullInt64)
case scaauthuserdevice.FieldUserID, scaauthuserdevice.FieldIP, scaauthuserdevice.FieldLocation, scaauthuserdevice.FieldAgent, scaauthuserdevice.FieldBrowser, scaauthuserdevice.FieldOperatingSystem, scaauthuserdevice.FieldBrowserVersion, scaauthuserdevice.FieldMozilla, scaauthuserdevice.FieldPlatform, scaauthuserdevice.FieldEngineName, scaauthuserdevice.FieldEngineVersion:
values[i] = new(sql.NullString)
@@ -173,16 +175,16 @@ func (saud *ScaAuthUserDevice) assignValues(columns []string, values []any) erro
saud.BrowserVersion = value.String
}
case scaauthuserdevice.FieldMobile:
if value, ok := values[i].(*sql.NullInt64); !ok {
if value, ok := values[i].(*sql.NullBool); !ok {
return fmt.Errorf("unexpected type %T for field mobile", values[i])
} else if value.Valid {
saud.Mobile = int(value.Int64)
saud.Mobile = value.Bool
}
case scaauthuserdevice.FieldBot:
if value, ok := values[i].(*sql.NullInt64); !ok {
if value, ok := values[i].(*sql.NullBool); !ok {
return fmt.Errorf("unexpected type %T for field bot", values[i])
} else if value.Valid {
saud.Bot = int(value.Int64)
saud.Bot = value.Bool
}
case scaauthuserdevice.FieldMozilla:
if value, ok := values[i].(*sql.NullString); !ok {

View File

@@ -106,12 +106,12 @@ func BrowserVersion(v string) predicate.ScaAuthUserDevice {
}
// Mobile applies equality check predicate on the "mobile" field. It's identical to MobileEQ.
func Mobile(v int) predicate.ScaAuthUserDevice {
func Mobile(v bool) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldEQ(FieldMobile, v))
}
// Bot applies equality check predicate on the "bot" field. It's identical to BotEQ.
func Bot(v int) predicate.ScaAuthUserDevice {
func Bot(v bool) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldEQ(FieldBot, v))
}
@@ -721,85 +721,25 @@ func BrowserVersionContainsFold(v string) predicate.ScaAuthUserDevice {
}
// MobileEQ applies the EQ predicate on the "mobile" field.
func MobileEQ(v int) predicate.ScaAuthUserDevice {
func MobileEQ(v bool) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldEQ(FieldMobile, v))
}
// MobileNEQ applies the NEQ predicate on the "mobile" field.
func MobileNEQ(v int) predicate.ScaAuthUserDevice {
func MobileNEQ(v bool) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldNEQ(FieldMobile, v))
}
// MobileIn applies the In predicate on the "mobile" field.
func MobileIn(vs ...int) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldIn(FieldMobile, vs...))
}
// MobileNotIn applies the NotIn predicate on the "mobile" field.
func MobileNotIn(vs ...int) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldNotIn(FieldMobile, vs...))
}
// MobileGT applies the GT predicate on the "mobile" field.
func MobileGT(v int) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldGT(FieldMobile, v))
}
// MobileGTE applies the GTE predicate on the "mobile" field.
func MobileGTE(v int) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldGTE(FieldMobile, v))
}
// MobileLT applies the LT predicate on the "mobile" field.
func MobileLT(v int) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldLT(FieldMobile, v))
}
// MobileLTE applies the LTE predicate on the "mobile" field.
func MobileLTE(v int) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldLTE(FieldMobile, v))
}
// BotEQ applies the EQ predicate on the "bot" field.
func BotEQ(v int) predicate.ScaAuthUserDevice {
func BotEQ(v bool) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldEQ(FieldBot, v))
}
// BotNEQ applies the NEQ predicate on the "bot" field.
func BotNEQ(v int) predicate.ScaAuthUserDevice {
func BotNEQ(v bool) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldNEQ(FieldBot, v))
}
// BotIn applies the In predicate on the "bot" field.
func BotIn(vs ...int) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldIn(FieldBot, vs...))
}
// BotNotIn applies the NotIn predicate on the "bot" field.
func BotNotIn(vs ...int) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldNotIn(FieldBot, vs...))
}
// BotGT applies the GT predicate on the "bot" field.
func BotGT(v int) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldGT(FieldBot, v))
}
// BotGTE applies the GTE predicate on the "bot" field.
func BotGTE(v int) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldGTE(FieldBot, v))
}
// BotLT applies the LT predicate on the "bot" field.
func BotLT(v int) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldLT(FieldBot, v))
}
// BotLTE applies the LTE predicate on the "bot" field.
func BotLTE(v int) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldLTE(FieldBot, v))
}
// MozillaEQ applies the EQ predicate on the "mozilla" field.
func MozillaEQ(v string) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldEQ(FieldMozilla, v))

View File

@@ -106,14 +106,14 @@ func (saudc *ScaAuthUserDeviceCreate) SetBrowserVersion(s string) *ScaAuthUserDe
}
// SetMobile sets the "mobile" field.
func (saudc *ScaAuthUserDeviceCreate) SetMobile(i int) *ScaAuthUserDeviceCreate {
saudc.mutation.SetMobile(i)
func (saudc *ScaAuthUserDeviceCreate) SetMobile(b bool) *ScaAuthUserDeviceCreate {
saudc.mutation.SetMobile(b)
return saudc
}
// SetBot sets the "bot" field.
func (saudc *ScaAuthUserDeviceCreate) SetBot(i int) *ScaAuthUserDeviceCreate {
saudc.mutation.SetBot(i)
func (saudc *ScaAuthUserDeviceCreate) SetBot(b bool) *ScaAuthUserDeviceCreate {
saudc.mutation.SetBot(b)
return saudc
}
@@ -395,11 +395,11 @@ func (saudc *ScaAuthUserDeviceCreate) createSpec() (*ScaAuthUserDevice, *sqlgrap
_node.BrowserVersion = value
}
if value, ok := saudc.mutation.Mobile(); ok {
_spec.SetField(scaauthuserdevice.FieldMobile, field.TypeInt, value)
_spec.SetField(scaauthuserdevice.FieldMobile, field.TypeBool, value)
_node.Mobile = value
}
if value, ok := saudc.mutation.Bot(); ok {
_spec.SetField(scaauthuserdevice.FieldBot, field.TypeInt, value)
_spec.SetField(scaauthuserdevice.FieldBot, field.TypeBool, value)
_node.Bot = value
}
if value, ok := saudc.mutation.Mozilla(); ok {

View File

@@ -161,47 +161,33 @@ func (saudu *ScaAuthUserDeviceUpdate) SetNillableBrowserVersion(s *string) *ScaA
}
// SetMobile sets the "mobile" field.
func (saudu *ScaAuthUserDeviceUpdate) SetMobile(i int) *ScaAuthUserDeviceUpdate {
saudu.mutation.ResetMobile()
saudu.mutation.SetMobile(i)
func (saudu *ScaAuthUserDeviceUpdate) SetMobile(b bool) *ScaAuthUserDeviceUpdate {
saudu.mutation.SetMobile(b)
return saudu
}
// SetNillableMobile sets the "mobile" field if the given value is not nil.
func (saudu *ScaAuthUserDeviceUpdate) SetNillableMobile(i *int) *ScaAuthUserDeviceUpdate {
if i != nil {
saudu.SetMobile(*i)
func (saudu *ScaAuthUserDeviceUpdate) SetNillableMobile(b *bool) *ScaAuthUserDeviceUpdate {
if b != nil {
saudu.SetMobile(*b)
}
return saudu
}
// AddMobile adds i to the "mobile" field.
func (saudu *ScaAuthUserDeviceUpdate) AddMobile(i int) *ScaAuthUserDeviceUpdate {
saudu.mutation.AddMobile(i)
return saudu
}
// SetBot sets the "bot" field.
func (saudu *ScaAuthUserDeviceUpdate) SetBot(i int) *ScaAuthUserDeviceUpdate {
saudu.mutation.ResetBot()
saudu.mutation.SetBot(i)
func (saudu *ScaAuthUserDeviceUpdate) SetBot(b bool) *ScaAuthUserDeviceUpdate {
saudu.mutation.SetBot(b)
return saudu
}
// SetNillableBot sets the "bot" field if the given value is not nil.
func (saudu *ScaAuthUserDeviceUpdate) SetNillableBot(i *int) *ScaAuthUserDeviceUpdate {
if i != nil {
saudu.SetBot(*i)
func (saudu *ScaAuthUserDeviceUpdate) SetNillableBot(b *bool) *ScaAuthUserDeviceUpdate {
if b != nil {
saudu.SetBot(*b)
}
return saudu
}
// AddBot adds i to the "bot" field.
func (saudu *ScaAuthUserDeviceUpdate) AddBot(i int) *ScaAuthUserDeviceUpdate {
saudu.mutation.AddBot(i)
return saudu
}
// SetMozilla sets the "mozilla" field.
func (saudu *ScaAuthUserDeviceUpdate) SetMozilla(s string) *ScaAuthUserDeviceUpdate {
saudu.mutation.SetMozilla(s)
@@ -435,16 +421,10 @@ func (saudu *ScaAuthUserDeviceUpdate) sqlSave(ctx context.Context) (n int, err e
_spec.SetField(scaauthuserdevice.FieldBrowserVersion, field.TypeString, value)
}
if value, ok := saudu.mutation.Mobile(); ok {
_spec.SetField(scaauthuserdevice.FieldMobile, field.TypeInt, value)
}
if value, ok := saudu.mutation.AddedMobile(); ok {
_spec.AddField(scaauthuserdevice.FieldMobile, field.TypeInt, value)
_spec.SetField(scaauthuserdevice.FieldMobile, field.TypeBool, value)
}
if value, ok := saudu.mutation.Bot(); ok {
_spec.SetField(scaauthuserdevice.FieldBot, field.TypeInt, value)
}
if value, ok := saudu.mutation.AddedBot(); ok {
_spec.AddField(scaauthuserdevice.FieldBot, field.TypeInt, value)
_spec.SetField(scaauthuserdevice.FieldBot, field.TypeBool, value)
}
if value, ok := saudu.mutation.Mozilla(); ok {
_spec.SetField(scaauthuserdevice.FieldMozilla, field.TypeString, value)
@@ -639,47 +619,33 @@ func (sauduo *ScaAuthUserDeviceUpdateOne) SetNillableBrowserVersion(s *string) *
}
// SetMobile sets the "mobile" field.
func (sauduo *ScaAuthUserDeviceUpdateOne) SetMobile(i int) *ScaAuthUserDeviceUpdateOne {
sauduo.mutation.ResetMobile()
sauduo.mutation.SetMobile(i)
func (sauduo *ScaAuthUserDeviceUpdateOne) SetMobile(b bool) *ScaAuthUserDeviceUpdateOne {
sauduo.mutation.SetMobile(b)
return sauduo
}
// SetNillableMobile sets the "mobile" field if the given value is not nil.
func (sauduo *ScaAuthUserDeviceUpdateOne) SetNillableMobile(i *int) *ScaAuthUserDeviceUpdateOne {
if i != nil {
sauduo.SetMobile(*i)
func (sauduo *ScaAuthUserDeviceUpdateOne) SetNillableMobile(b *bool) *ScaAuthUserDeviceUpdateOne {
if b != nil {
sauduo.SetMobile(*b)
}
return sauduo
}
// AddMobile adds i to the "mobile" field.
func (sauduo *ScaAuthUserDeviceUpdateOne) AddMobile(i int) *ScaAuthUserDeviceUpdateOne {
sauduo.mutation.AddMobile(i)
return sauduo
}
// SetBot sets the "bot" field.
func (sauduo *ScaAuthUserDeviceUpdateOne) SetBot(i int) *ScaAuthUserDeviceUpdateOne {
sauduo.mutation.ResetBot()
sauduo.mutation.SetBot(i)
func (sauduo *ScaAuthUserDeviceUpdateOne) SetBot(b bool) *ScaAuthUserDeviceUpdateOne {
sauduo.mutation.SetBot(b)
return sauduo
}
// SetNillableBot sets the "bot" field if the given value is not nil.
func (sauduo *ScaAuthUserDeviceUpdateOne) SetNillableBot(i *int) *ScaAuthUserDeviceUpdateOne {
if i != nil {
sauduo.SetBot(*i)
func (sauduo *ScaAuthUserDeviceUpdateOne) SetNillableBot(b *bool) *ScaAuthUserDeviceUpdateOne {
if b != nil {
sauduo.SetBot(*b)
}
return sauduo
}
// AddBot adds i to the "bot" field.
func (sauduo *ScaAuthUserDeviceUpdateOne) AddBot(i int) *ScaAuthUserDeviceUpdateOne {
sauduo.mutation.AddBot(i)
return sauduo
}
// SetMozilla sets the "mozilla" field.
func (sauduo *ScaAuthUserDeviceUpdateOne) SetMozilla(s string) *ScaAuthUserDeviceUpdateOne {
sauduo.mutation.SetMozilla(s)
@@ -943,16 +909,10 @@ func (sauduo *ScaAuthUserDeviceUpdateOne) sqlSave(ctx context.Context) (_node *S
_spec.SetField(scaauthuserdevice.FieldBrowserVersion, field.TypeString, value)
}
if value, ok := sauduo.mutation.Mobile(); ok {
_spec.SetField(scaauthuserdevice.FieldMobile, field.TypeInt, value)
}
if value, ok := sauduo.mutation.AddedMobile(); ok {
_spec.AddField(scaauthuserdevice.FieldMobile, field.TypeInt, value)
_spec.SetField(scaauthuserdevice.FieldMobile, field.TypeBool, value)
}
if value, ok := sauduo.mutation.Bot(); ok {
_spec.SetField(scaauthuserdevice.FieldBot, field.TypeInt, value)
}
if value, ok := sauduo.mutation.AddedBot(); ok {
_spec.AddField(scaauthuserdevice.FieldBot, field.TypeInt, value)
_spec.SetField(scaauthuserdevice.FieldBot, field.TypeBool, value)
}
if value, ok := sauduo.mutation.Mozilla(); ok {
_spec.SetField(scaauthuserdevice.FieldMozilla, field.TypeString, value)

View File

@@ -1,3 +1,3 @@
package mysql
//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate --feature privacy,entql --target ./ent ./schema
//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate --feature privacy,entql --target ./ent ./model

View File

@@ -29,7 +29,7 @@ func NewMySQL(url string) *ent.Client {
defer client.Close()
if err = client.Schema.Create(context.Background()); err != nil {
log.Panicf("failed creating schema resources: %v", err)
log.Panicf("failed creating model resources: %v", err)
}
return client
}

View File

@@ -1,4 +1,4 @@
package schema
package model
import (
"entgo.io/ent"
@@ -9,7 +9,7 @@ import (
"entgo.io/ent/schema/field"
)
// ScaAuthPermissionRule holds the schema definition for the ScaAuthPermissionRule entity.
// ScaAuthPermissionRule holds the model definition for the ScaAuthPermissionRule entity.
type ScaAuthPermissionRule struct {
ent.Schema
}

View File

@@ -1,4 +1,4 @@
package schema
package model
import (
"entgo.io/ent"
@@ -8,10 +8,10 @@ import (
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/schema/mixin"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/model/mixin"
)
// ScaAuthRole holds the schema definition for the ScaAuthRole entity.
// ScaAuthRole holds the model definition for the ScaAuthRole entity.
type ScaAuthRole struct {
ent.Schema
}

View File

@@ -1,4 +1,4 @@
package schema
package model
import (
"entgo.io/ent"
@@ -9,10 +9,10 @@ import (
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/schema/mixin"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/model/mixin"
)
// ScaAuthUser holds the schema definition for the ScaAuthUser entity.
// ScaAuthUser holds the model definition for the ScaAuthUser entity.
type ScaAuthUser struct {
ent.Schema
}

View File

@@ -1,4 +1,4 @@
package schema
package model
import (
"entgo.io/ent"
@@ -9,10 +9,10 @@ import (
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/schema/mixin"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/model/mixin"
)
// ScaAuthUserDevice holds the schema definition for the ScaAuthUserDevice entity.
// ScaAuthUserDevice holds the model definition for the ScaAuthUserDevice entity.
type ScaAuthUserDevice struct {
ent.Schema
}
@@ -53,9 +53,9 @@ func (ScaAuthUserDevice) Fields() []ent.Field {
field.String("browser_version").
MaxLen(20).
Comment("浏览器版本"),
field.Int("mobile").
field.Bool("mobile").
Comment("是否为手机 0否1是"),
field.Int("bot").
field.Bool("bot").
Comment("是否为bot 0否1是"),
field.String("mozilla").
MaxLen(10).

View File

@@ -1,4 +1,4 @@
package schema
package model
import (
"entgo.io/ent"
@@ -9,10 +9,10 @@ import (
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/schema/mixin"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/model/mixin"
)
// ScaAuthUserSocial holds the schema definition for the ScaAuthUserSocial entity.
// ScaAuthUserSocial holds the model definition for the ScaAuthUserSocial entity.
type ScaAuthUserSocial struct {
ent.Schema
}

View File

@@ -0,0 +1,38 @@
package redis_session
import (
"context"
"encoding/gob"
"net/http"
"github.com/gorilla/sessions"
"github.com/rbcervilla/redisstore/v9"
"github.com/redis/go-redis/v9"
"github.com/zeromicro/go-zero/core/logc"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
"schisandra-album-cloud-microservices/app/core/api/repository/redis_session/types"
)
func NewRedisSession(addr string, password string) *redisstore.RedisStore {
client := redis.NewClient(&redis.Options{
Addr: addr,
Password: password,
DB: 0,
})
store, err := redisstore.NewRedisStore(context.Background(), client)
if err != nil {
logc.Error(context.Background(), err)
}
store.KeyPrefix(constant.UserSessionPrefix)
store.Options(sessions.Options{
Path: "/",
// Domain: global.CONFIG.System.Web,
MaxAge: 86400 * 7,
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteLaxMode,
})
gob.Register(types.SessionData{})
return store
}

View File

@@ -0,0 +1,7 @@
package types
// SessionData 返回数据
type SessionData struct {
RefreshToken string `json:"refresh_token"`
UID string `json:"uid"`
}

View File

@@ -0,0 +1,12 @@
package redisx
import "github.com/redis/go-redis/v9"
func NewRedis(host, password string, db int) *redis.Client {
rdb := redis.NewClient(&redis.Options{
Addr: host,
Password: password,
DB: db,
})
return rdb
}

View File

@@ -0,0 +1,6 @@
package types
type RedisToken struct {
AccessToken string `json:"access_token"`
UID string `json:"uid"`
}

Binary file not shown.

View File

@@ -0,0 +1,7 @@
[captcha]
verificationFailure = "verification failure!"
[login]
invalidAccount = "invalid account"
notFoundAccount = "account not found"
invalidPassword = "invalid password"
loginFailed = "login failed"

View File

@@ -0,0 +1,7 @@
[captcha]
verificationFailure = "验证失败!"
[login]
invalidAccount = "无效的账号!"
notFoundAccount = "未找到账号!"
invalidPassword = "密码错误!"
loginFailed = "登录失败!"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,170 @@
穴海
协警
纳米比亚
专业调查
有华龙
jq的来
电信路
党鞭
第一夫人
黄巨
荡尽天下
家元自称玉皇大帝
主席李世民
何祚庥
刘刚
不要沉默
后勤集团
食堂涨价
发国难财
浪漫邂逅
红满堂
张小洋
炸学校
子宫
叫晶晶的女孩
回派
社会黑暗
国之母
国母
国姆
东方微点
震惊全球
nowto
chengdu
徐明
六月飞雪
暴力虐待
暴力袭击
天府广场
粮荒
洗脑班
李愚蠢
中国猪
台湾猪
进化不完全的生命体
震死他们
10010
10086
10159
13423205670
13725516608
13875448369
15112886328
189
6-4tianwang
64
68170802
6a6.net
7.31
7.310
89-64cdjp
8945212
23条
259o
381929279
3P
4-Jun
AV
BJ
CBD
CCTV
CDMA
DICK
Dick
FLG
FOCUSC
FUCK
Fuck
GAMEMASTER
GCD
GameMaster
IP17908
KEY_TEXT
NMD
QQb
SM
Soccer01.com
TMD
UltraSurf
bignews
Bitch
boxun
Chinaliberal
chinamz
Chinesenewsnet
cnd
Creaders
dafa
Dajiyuan
df'd
Dfdz
die
dpp
Freechina
freedom
Freenet
fuck
fuck your mother
hongzhi
hrichina
huanet
hypermart.net
incest
jiangdongriji
l2590803027
lihongzhi
ma
making
minghui
minghuinews
nacb
na?ve
nmis
paper64
peacehall
piao
playboy
renminbao
renmingbao
rfa
safeweb
sex
shit
simple
svdc
taip
tibetalk
triangle
triangleboy
txwq.net
unixbox
ustibet
voa
voachinese
wangce
wstaiji
www
xinsheng
yuming
yy通讯录
zhengjian
zhengjianwang
zhenshanren
zhuanfalu
zhuanfalun
八九
八老
fuck
duck
shit
chicken
fowl
sex
sexy
prostitute
whore
harlot
hooker
gender

View File

@@ -0,0 +1,557 @@
腐败中国
三个呆婊
你办事我放心
社会主义灭亡
打倒中国
打倒共产党
打倒共产主义
打倒胡锦涛
打倒江泽民
打倒江主席
打倒李鹏
打倒罗干
打倒温家宝
打倒中共
打倒朱镕
抵制共产党
抵制共产主义
抵制胡锦涛
抵制江泽民
抵制江主席
抵制李鹏
抵制罗干
抵制温家宝
抵制中共
抵制朱镕基
灭亡中国
亡党亡国
粉碎四人帮
激流中国
特供
特贡
特共
zf大楼
殃视
贪污腐败
强制拆除
形式主义
政治风波
太子党
上海帮
北京帮
清华帮
红色贵族
权贵集团
河蟹社会
喝血社会
九风
9风
十七大
十7大
17da
九学
9学
四风
4风
双规
南街村
最淫官员
警匪
官匪
独夫民贼
官商勾结
城管暴力执法
强制捐款
毒豺
一党执政
一党专制
一党专政
专制政权
宪法法院
胡平
苏晓康
贺卫方
谭作人
焦国标
万润南
张志新
辛灝年
高勤荣
王炳章
高智晟
司马璐
刘晓竹
刘宾雁
魏京生
寻找林昭的灵魂
别梦成灰
谁是新中国
讨伐中宣部
异议人士
民运人士
启蒙派
选国家主席
民一主
min主
民竹
民珠
民猪
chinesedemocracy
大赦国际
国际特赦
da选
投公
公头
宪政
平反
党章
维权
昝爱宗
宪章
08宪
08xz
抿主
敏主
人拳
人木又
人quan
renquan
中国人权
中国新民党
群体事件
群体性事件
上中央
去中央
讨说法
请愿
请命
公开信
联名上书
万人大签名
万人骚动
截访
上访
shangfang
信访
访民
集合
集会
组织集体
静坐
静zuo
jing坐
示威
示wei
游行
you行
油行
游xing
youxing
官逼民反
反party
反共
抗议
亢议
抵制
低制
底制
di制
抵zhi
dizhi
boycott
血书
焚烧中国国旗
baoluan
流血冲突
出现暴动
发生暴动
引起暴动
baodong
灭共
杀毙
罢工
霸工
罢考
罢餐
霸餐
罢参
罢饭
罢吃
罢食
罢课
罢ke
霸课
ba课
罢教
罢学
罢运
网特
网评员
网络评论员
五毛党
五毛们
5毛党
戒严
jieyan
jie严
戒yan
8的平方事件
知道64
八九年
贰拾年
2o年
20和谐年
贰拾周年
六四
六河蟹四
六百度四
六和谐四
陆四
陆肆
198964
5月35
89年春夏之交
64惨案
64时期
64运动
4事件
四事件
北京风波
学潮
学chao
xuechao
学百度潮
门安天
天按门
坦克压大学生
民主女神
历史的伤口
高自联
北高联
血洗京城
四二六社论
王丹
柴玲
沈彤
封从德
王超华
王维林
吾尔开希
吾尔开西
侯德健
阎明复
方励之
蒋捷连
丁子霖
辛灏年
蒋彦永
严家其
陈一咨
中华局域网
党的喉舌
互联网审查
当局严密封锁
新闻封锁
封锁消息
爱国者同盟
关闭所有论坛
网络封锁
金盾工程
gfw
无界浏览
无界网络
自由门
何清涟
中国的陷阱
汪兆钧
记者无疆界
境外媒体
维基百科
纽约时报
bbc中文网
华盛顿邮报
世界日报
东森新闻网
东森电视
星岛日报
wikipedia
youtube
googleblogger
美国广播公司
英国金融时报
自由亚洲
自由时报
中国时报
反分裂
威胁论
左翼联盟
钓鱼岛
保钓组织
主权
弓单
火乍
木仓
石肖
核蛋
步qiang
bao炸
爆zha
baozha
zha药
zha弹
炸dan
炸yao
zhadan
zhayao
hmtd
三硝基甲苯
六氟化铀
炸药配方
弹药配方
炸弹配方
皮箱炸弹
火药配方
人体炸弹
人肉炸弹
解放军
兵力部署
军转
军事社
8341部队
第21集团军
七大军区
7大军区
北京军区
沈阳军区
济南军区
成都军区
广州军区
南京军区
兰州军区
颜色革命
规模冲突
塔利班
基地组织
恐怖分子
恐怖份子
三股势力
印尼屠华
印尼事件
蒋公纪念歌
马英九
mayingjiu
李天羽
苏贞昌
林文漪
陈水扁
陈s扁
陈随便
阿扁
a扁
告全国同胞书
台百度湾
台完
台wan
taiwan
台弯
湾台
台湾国
台湾共和国
台军
台独
台毒
台du
taidu
twdl
一中一台
打台湾
两岸战争
攻占台湾
支持台湾
进攻台湾
占领台湾
统一台湾
收复台湾
登陆台湾
解放台湾
解放tw
解决台湾
光复民国
台湾独立
台湾问题
台海问题
台海危机
台海统一
台海大战
台海战争
台海局势
入联
入耳关
中华联邦
国民党
x民党
民进党
青天白日
闹独立
duli
fenlie
日本万岁
小泽一郎
劣等民族
汉人
汉维
维汉
维吾
吾尔
热比娅
伊力哈木
疆独
东突厥斯坦解放组织
东突解放组织
蒙古分裂分子
列确
阿旺晋美
藏人
臧人
zang人
藏民
藏m
达赖
赖达
dalai
哒赖
dl喇嘛
丹增嘉措
打砸抢
西独
藏独
葬独
臧独
藏毒
藏du
zangdu
支持zd
藏暴乱
藏青会
雪山狮子旗
拉萨
啦萨
啦沙
啦撒
拉sa
lasa
la萨
西藏
藏西
藏春阁
藏獨
藏独
藏独立
藏妇会
藏青会
藏字石
xizang
xi藏
x藏
西z
tibet
希葬
希藏
硒藏
稀藏
西脏
西奘
西葬
西臧
援藏
bjork
王千源
安拉
回教
回族
回回
回民
穆斯林
穆罕穆德
穆罕默德
默罕默德
伊斯兰
圣战组织
清真
清zhen
qingzhen
真主
阿拉伯
高丽棒子
韩国狗
满洲第三帝国
满狗
鞑子
江丑闻
江嫡系
江毒
江独裁
江蛤蟆
江核心
江黑心
江胡内斗
江祸心
江家帮
江绵恒
江派和胡派
江派人马
江泉集团
江人马
江三条腿
江氏集团
江氏家族
江氏政治局
江氏政治委员
江梳头
江太上
江戏子
江系人
江系人马
江宰民
江贼
江贼民
江主席
麻果丸
麻将透
麻醉弹
麻醉狗
麻醉枪
麻醉槍
麻醉药
麻醉藥
台独
台湾版假币
台湾独立
台湾国
台湾应该独立
台湾有权独立
天灭中共
中共帮凶
中共保命
中共裁
中共党文化
中共腐败
中共的血旗
中共的罪恶
中共帝国
中共独裁
中共封锁
中共封网
中共腐败
中共黑
中共黑帮
中共解体
中共近期权力斗争
中共恐惧
中共权力斗争
中共任用
中共退党
中共洗脑
中共邪教
中共政治游戏
中共邪毒素

View File

@@ -0,0 +1,178 @@
福音会
中国教徒
统一教
观音法门
清海无上师
盘古
李洪志
志洪李
李宏志
轮功
法轮
轮法功
三去车仑
氵去车仑
发论工
法x功
法o功
法0功
法一轮一功
轮子功
车仑工力
法lun
fa轮
法lg
flg
fl功
falungong
大法弟子
大纪元
dajiyuan
明慧网
明慧周报
正见网
新唐人
伪火
退党
tuidang
退dang
超越红墙
自fen
真善忍
九评
9评
9ping
九ping
jiuping
藏字石
集体自杀
自sha
zi杀
suicide
titor
逢8必灾
逢八必灾
逢9必乱
逢九必乱
朱瑟里诺
根达亚文明
诺查丹玛斯
人类灭亡进程表
按照马雅历法
推背图
推bei图
济世灵文
诸世纪
电狗
电话定位器
电话拦截器
电话窃听
电话监
电话交友
电话追杀系统
电击枪
电鸡
电警棒
枪出售
枪的制
枪货到
枪决女犯
枪模
枪手
枪销售
枪械制
枪子弹
售步枪
售纯度
售单管
售弹簧刀
售防身
售狗子
售虎头
售火药
售假币
售健卫
售军用
售猎枪
售氯胺
售麻醉
售枪支
售热武
售三棱
售手枪
售五四
售一元硬
售子弹
售左轮
亚砷(酸)酐
亚砷酸钾
亚砷酸钠
亚硒酸
亚硒酸二钠
亚硒酸镁
亚硒酸钠
亚硒酸氢钠
亚硝酸乙酯
亚硝酰乙氧
氧化二丁基锡
氧化汞
氧化铊
氧化亚铊
氧氯化磷
原装弹
原子弹方法
原子弹清单
安眠酮
代血浆
普萘洛尔
呋塞米
西布曲明
testosterone
胰岛素样生长因子
促红细胞生成素
地西泮
尼可刹米
甲睾酮
adrenaline
erythropoietin
地奈德
莫达非尼
氯噻嗪
苯巴比妥
促性腺激素
泼尼松
麻黄草
雄烯二醇
地塞米松
tamoxifen
strychnine
androst
新型毒品
杜冷丁
兴奋剂
mdma
海洛因
海luo因
heroin
diamorphine
diacetylmorphine
鸦片
阿芙蓉
咖啡因
cocain
三唑仑
美沙酮
麻古
k粉
凯他敏
ketamine
冰毒
苯丙胺
cannabis
大麻
爱他死
氯胺酮
benzodiazepines
甲基安非他明
吗啡
安非他命

View File

@@ -0,0 +1,571 @@
打人
打人
拆迁
拆迁
纠纷
纠纷
盗窃
盗窃
安眠酮
代药物毒品类:血浆
普萘洛尔
呋塞米
西布曲明
testosterone
胰岛素样生长因子
促红细胞生成素
地西泮
尼可刹米
甲睾酮
adrenaline
erythropoietin
地奈德
莫达非尼
氯噻嗪
苯巴比妥
促性腺激素
泼尼松
麻黄草
雄烯二醇
地塞米松
tamoxifen
strychnine
androst
新型毒品
杜冷丁
兴奋剂
mdma
海洛因
海luo因
heroin
diamorphine
diacetylmorphine
鸦片
阿芙蓉
咖啡因
cocain
三唑仑
美沙酮
麻古
k粉
凯他敏
ketamine
冰毒
苯丙胺
cannabis
大麻
爱他死
氯胺酮
benzodiazepines
甲基安非他明
安非他命
吗啡
morphine
摇头丸
迷药
乖乖粉
narcotic
麻醉药
精神药品
专业代理
帮忙点一下
帮忙点下
请点击进入
详情请进入
私人侦探
私家侦探
针孔摄象
调查婚外情
信用卡提现
无抵押贷款
广告代理
原音铃声
借腹生子
找个妈妈
找个爸爸
代孕妈妈
代生孩子
代开发票
腾讯客服电话
销售热线
免费订购热线
低价出售
款到发货
回复可见
连锁加盟
加盟连锁
免费二级域名
免费使用
免费索取
蚁力神
婴儿汤
售肾
刻章办
买小车
套牌车
玛雅网
电脑传讯
视频来源
下载速度
高清在线
全集在线
在线播放
txt下载
六位qq
6位qq
位的qq
个qb
送qb
用刀横向切腹
完全自杀手册
四海帮
足球投注
地下钱庄
中国复兴党
阿波罗网
曾道人
六合彩
改卷内幕
替考试
隐形耳机
出售答案
考中答案
答an
da案
资金周转
救市
股市圈钱
崩盘
资金短缺
证监会
质押贷款
小额贷款
周小川
刘明康
尚福林
孔丹
汉芯造假
杨树宽
中印边界谈判结果
喂奶门
摸nai门
酒瓶门
脱裤门
75事件
乌鲁木齐
新疆骚乱
针刺
打针
食堂涨价
饭菜涨价
h1n1
瘟疫爆发
yangjia
y佳
yang佳
杨佳
杨j
袭警
杀警
武侯祠
川b26931
贺立旗
周正毅
px项目
骂四川
家l福
家le福
加了服
麦当劳被砸
豆腐渣
这不是天灾
龙小霞
震其国土
yuce
提前预测
地震预测
隐瞒地震
李四光预测
蟾蜍迁徙
地震来得更猛烈
八级地震毫无预报
踩踏事故
聂树斌
万里大造林
陈相贵
张丹红
尹方明
李树菲
王奉友
零八奥运艰
惨奥
奥晕
凹晕
懊运
懊孕
奥孕
奥你妈的运
反奥
628事件
weng安
wengan
翁安
瓮安事件
化工厂爆炸
讨回工资
代办发票
代办各
代办文
代办学
代办制
代辦
代表烦
代开发票
代開
代考
代理发票
代理票据
代您考
代讨债
代写毕
代写论文
代孕
代追债
考后付款
考机构
考考邓
考联盟
考前答案
考前付
考前密卷
考前预测
考试,答案
考试,作弊器
考试包过
考试保
考试答案
考试机构
考试联盟
考试枪
考试作弊
考试作弊器
考研考中
考中答案
透视功能
透视镜
透视扑
透视器
透视眼睛
透视眼镜
透视药
透视仪
打死经过
打死人
打砸办公
打砸抢
安眠酮
代血浆
普萘洛尔
呋塞米
西布曲明
testosterone
胰岛素样生长因子
促红细胞生成素
地西泮
尼可刹米
甲睾酮
adrenaline
erythropoietin
地奈德
莫达非尼
氯噻嗪
苯巴比妥
促性腺激素
泼尼松
麻黄草
雄烯二醇
地塞米松
tamoxifen
strychnine
androst
新型毒品
杜冷丁
兴奋剂
mdma
海洛因
海luo因
heroin
diamorphine
diacetylmorphine
鸦片
阿芙蓉
咖啡因
cocain
三唑仑
美沙酮
麻古
k粉
凯他敏
ketamine
冰毒
苯丙胺
cannabis
大麻
爱他死
氯胺酮
benzodiazepines
甲基安非他明
安非他命
吗啡
KC短信
KC嘉年华
短信广告
短信群发
短信群发器
小6灵通
短信商务广告
段录定
无界浏览
无界浏览器
无界
无网界
无网界浏览
无帮国
KC提示
KC网站
UP8新势力
白皮书
UP新势力
移民
易达网络卡
安魂网
罢工
罢课
纽崔莱七折
手机复制
手机铃声
网关
神通加持法
全1球通
如6意通
清仓
灵动卡
答案卫星接收机
高薪养廉
考后付款
佳静安定片
航空母舰
航空售票
号码百事通
考前发放
成本价
诚信通手机商城
高利贷
联4通
黑庄
黑手党
黑车
联通贵宾卡
联总
联总这声传单
联总之声传单
高息贷款
高干子弟
恭喜你的号码
恭喜您的号码
高干子女
各个银行全称
各种发票
高官
高官互调
高官子女
喝一送一
卡号
复制
监听王
传单
旦科
钓鱼岛
钓鱼台
当官靠后台
党校安插亲信
传九促三
客户端非法字符
刻章
大麻树脂
大麻油
大法
大法弟子
dpp大法
fa lun
falu
发抡
发抡功
洗脑
下法轮
发轮
发伦
发伦功
发仑
发沦
发纶
发论
发论功
发论公
发正念
发囵
发愣
发瞟
罚抡
罚伦
罚仑
罚沦
罚纶
罚囵
筏抡
筏轮
筏伦
筏仑
筏沦
筏纶
筏论
筏囵
伐抡
伐轮
伐伦
伐仑
伐沦
伐论
伐囵
乏抡
乏轮
乏伦
乏仑
乏沦
乏纶
乏论
乏囵
阀抡
阀伦
阀仑
阀沦
阀纶
阀论
阀囵
法 轮 功
法*功
法.轮.功
法L功
法lun功
法功
法会
法抡
法抡功
法轮
法轮大法
法轮佛法
法轮功
法伦
法仑
法沦
法纶
法论
法十轮十功
法西斯
法院
法正
法谪
法谪功
法輪
法囵
法愣
珐.輪功
珐抡
珐轮
珐伦
珐仑
珐沦
五不
五不争鸣论坛
五出三进
五套功法
邝锦文
垡抡
垡轮
垡伦
垡仑
垡沦
垡纶
垡论
垡囵
茳澤民
荭志
闳志
闵维方
氵去
氵去车仑工力
转法轮
砝抡
砝轮
砝伦
砝仑
砝沦
砝纶
真、善、忍
真理教
真善美
真善忍
砝论
砝囵
泓志
屙民
珐纶
珐论
珐囵
falun
Falundafa
fa轮
Flg
弟子
地下教会
炼功
梦网洪志
轮大
抡功
轮功
伦功
摩门教
三水法轮
三唑仑
天皇
天怒
天葬
车臣
达赖
功法
讲法
基督
基督教
护法
回教
教徒
功友
大师
达赖喇嘛
虹志
鸿志
洪传
贯通两极法
光祖
洪吟
洪哲胜
洪志
宏法
观世音
宏志
弘志
古兰经
红志
车库
经文
车仑

View File

@@ -0,0 +1,929 @@
爱女人
爱液
按摩棒
拔出来
爆草
包二奶
暴干
暴奸
暴乳
爆乳
暴淫
被插
被操
被干
逼奸
仓井空
插暴
操逼
操黑
操烂
肏你
肏死
操死
操我
厕奴
插比
插b
插逼
插进
插你
插我
插阴
潮吹
潮喷
成人dv
成人电影
成人论坛
成人小说
成人电
成人电影
成人卡通
成人聊
成人片
成人视
成人图
成人文
成人小
成人电影
成人论坛
成人色情
成人网站
成人文学
成人小说
艳情小说
成人游戏
吃精
赤裸
抽插
扌由插
抽一插
春药
大波
大力抽送
大乳
荡妇
荡女
盗撮
多人轮
发浪
放尿
肥逼
粉穴
封面女郎
风月大陆
干死你
干穴
肛交
肛门
龟头
裹本
国产av
好嫩
豪乳
黑逼
后庭
后穴
虎骑
花花公子
换妻俱乐部
黄片
几吧
鸡吧
鸡巴
鸡奸
寂寞男
寂寞女
妓女
激情
集体淫
奸情
叫床
脚交
金鳞岂是池中物
金麟岂是池中物
精液
就去日
巨屌
菊花洞
菊门
巨奶
巨乳
菊穴
开苞
口爆
口活
口交
口射
口淫
裤袜
狂操
狂插
浪逼
浪妇
浪叫
浪女
狼友
聊性
流淫
铃木麻
凌辱
漏乳
露b
乱交
乱伦
轮暴
轮操
轮奸
裸陪
买春
美逼
美少妇
美乳
美腿
美穴
美幼
秘唇
迷奸
密穴
蜜穴
蜜液
摸奶
摸胸
母奸
奈美
奶子
男奴
内射
嫩逼
嫩女
嫩穴
捏弄
女优
炮友
砲友
喷精
屁眼
品香堂
前凸后翘
强jian
强暴
强奸处女
情趣用品
情色
拳交
全裸
群交
惹火身材
人妻
人兽
日逼
日烂
肉棒
肉逼
肉唇
肉洞
肉缝
肉棍
肉茎
肉具
揉乳
肉穴
肉欲
乳爆
乳房
乳沟
乳交
乳头
三级片
骚逼
骚比
骚女
骚水
骚穴
色逼
色界
色猫
色盟
色情网站
色区
色色
色诱
色欲
色b
少年阿宾
少修正
射爽
射颜
食精
释欲
兽奸
兽交
手淫
兽欲
熟妇
熟母
熟女
爽片
爽死我了
双臀
死逼
丝袜
丝诱
松岛枫
酥痒
汤加丽
套弄
体奸
体位
舔脚
舔阴
调教
偷欢
偷拍
推油
脱内裤
文做
我就色
无码
舞女
无修正
吸精
夏川纯
相奸
小逼
校鸡
小穴
小xue
写真
性感妖娆
性感诱惑
性虎
性饥渴
性技巧
性交
性奴
性虐
性息
性欲
胸推
穴口
学生妹
穴图
亚情
颜射
阳具
杨思敏
要射了
夜勤病栋
一本道
一夜欢
一夜情
一ye情
阴部
淫虫
阴唇
淫荡
阴道
淫电影
阴阜
淫妇
淫河
阴核
阴户
淫贱
淫叫
淫教师
阴茎
阴精
淫浪
淫媚
淫糜
淫魔
淫母
淫女
淫虐
淫妻
淫情
淫色
淫声浪语
淫兽学园
淫书
淫术炼金士
淫水
淫娃
淫威
淫亵
淫样
淫液
淫照
阴b
应召
幼交
幼男
幼女
欲火
欲女
玉女心经
玉蒲团
玉乳
欲仙欲死
玉穴
援交
原味内衣
援助交际
张筱雨
招鸡
招妓
中年美妇
抓胸
自拍
自慰
作爱
18禁
99bb
a4u
a4y
adult
amateur
anal
a片
fuck
gay片
g点
g片
hardcore
h动画
h动漫
incest
porn
secom
sexinsex
sm女王
xiao77
xing伴侣
tokyohot
yin荡
贱人
装b
大sb
傻逼
傻b
煞逼
煞笔
刹笔
傻比
沙比
欠干
婊子养的
我日你
我操
我草
卧艹
卧槽
爆你菊
艹你
cao你
你他妈
真他妈
别他吗
草你吗
草你丫
操你妈
擦你妈
操你娘
操他妈
日你妈
干你妈
干你娘
娘西皮
狗操
狗草
狗杂种
狗日的
操你祖宗
操你全家
操你大爷
妈逼
你麻痹
麻痹的
妈了个逼
马勒
狗娘养
贱比
贱b
下贱
死全家
全家死光
全家不得好死
全家死绝
白痴
无耻
sb
杀b
你吗b
你妈的
婊子
贱货
人渣
混蛋
媚外
和弦
兼职
限量
铃声
性伴侣
男公关
火辣
精子
射精
诱奸
强奸
做爱
性爱
发生关系
按摩
快感
处男
猛男
少妇
屁股
下体
a片
内裤
浑圆
咪咪
发情
刺激
白嫩
粉嫩
兽性
风骚
呻吟
sm
阉割
高潮
裸露
不穿
一丝不挂
脱光
干你
干死
我干
裙中性运动
乱奸
乱伦
乱伦类
乱伦小
伦理大
伦理电影
伦理毛
伦理片
裸聊
裸聊网
裸体写真
裸舞视
裸照
美女裸体
美女写真
美女上门
美艳少妇
妹按摩
妹上门
迷幻药
迷幻藥
迷昏口
迷昏药
迷昏藥
迷魂香
迷魂药
迷魂藥
迷奸粉
迷奸药
迷情粉
迷情水
迷情药
迷药
迷藥
谜奸药
骚妇
骚货
骚浪
骚女
骚嘴
色电影
色妹妹
色情表演
色情电影
色情服务
色情图片
色情小说
色情影片
色情表演
色情电影
色情服务
色情片
色视频
色小说
性伴侣
性服务
性福情
性感少
性伙伴
性交
性交视频
性交图片
性奴
性奴集中营
性虐
阴唇
阴道
阴蒂
阴户
阴间来电
阴茎
阴茎增大
阴茎助勃
阴毛
陰唇
陰道
陰戶
淫荡
淫荡美女
淫荡视频
淫荡照片
淫乱
淫靡
淫魔
淫魔舞
淫女
淫情女
淫肉
淫騷妹
淫兽
淫兽学
淫水
淫穴
morphine
摇头丸
迷药
乖乖粉
narcotic
麻醉药
精神药品
爱女人
爱液
按摩棒
拔出来
爆草
包二奶
暴干
暴奸
暴乳
爆乳
暴淫
被操
被插
被干
逼奸
仓井空
插暴
操逼
操黑
操烂
肏你
肏死
操死
操我
厕奴
插比
插b
插逼
插进
插你
插我
插阴
潮吹
潮喷
成人电影
成人论坛
成人色情
成人网站
成人文学
成人小说
艳情小说
成人游戏
吃精
赤裸
抽插
扌由插
抽一插
春药
大波
大力抽送
大乳
荡妇
荡女
盗撮
多人轮
发浪
放尿
肥逼
粉穴
封面女郎
风月大陆
干死你
干穴
肛交
肛门
龟头
裹本
国产av
好嫩
豪乳
黑逼
后庭
后穴
虎骑
花花公子
换妻俱乐部
黄片
几吧
鸡吧
鸡巴
鸡奸
寂寞男
寂寞女
妓女
激情
集体淫
奸情
叫床
脚交
金鳞岂是池中物
金麟岂是池中物
精液
就去日
巨屌
菊花洞
菊门
巨奶
巨乳
菊穴
开苞
口爆
口活
口交
口射
口淫
裤袜
狂操
狂插
浪逼
浪妇
浪叫
浪女
狼友
聊性
流淫
铃木麻
凌辱
漏乳
露b
乱交
乱伦
轮暴
轮操
轮奸
裸陪
买春
美逼
美少妇
美乳
美腿
美穴
美幼
秘唇
迷奸
密穴
蜜穴
蜜液
摸奶
摸胸
母奸
奈美
奶子
男奴
内射
嫩逼
嫩女
嫩穴
捏弄
女优
炮友
砲友
喷精
屁眼
品香堂
前凸后翘
强jian
强暴
强奸处女
情趣用品
情色
拳交
全裸
群交
惹火身材
人妻
人兽
日逼
日烂
肉棒
肉逼
肉唇
肉洞
肉缝
肉棍
肉茎
肉具
揉乳
肉穴
肉欲
乳爆
乳房
乳沟
乳交
乳头
三级片
骚逼
骚比
骚女
骚水
骚穴
色逼
色界
色猫
色盟
色情网站
色区
色色
色诱
色欲
色b
少年阿宾
少修正
射爽
射颜
食精
释欲
兽奸
兽交
手淫
兽欲
熟妇
熟母
熟女
爽片
爽死我了
双臀
死逼
丝袜
丝诱
松岛枫
酥痒
汤加丽
套弄
体奸
体位
舔脚
舔阴
调教
偷欢
偷拍
推油
脱内裤
文做
我就色
无码
舞女
无修正
吸精
夏川纯
相奸
小逼
校鸡
小穴
小xue
写真
性感妖娆
性感诱惑
性虎
性饥渴
性技巧
性交
性奴
性虐
性息
性欲
胸推
穴口
学生妹
穴图
亚情
颜射
阳具
杨思敏
要射了
夜勤病栋
一本道
一夜欢
一夜情
一ye情
阴部
淫虫
阴唇
淫荡
阴道
淫电影
阴阜
淫妇
淫河
阴核
阴户
淫贱
淫叫
淫教师
阴茎
阴精
淫浪
淫媚
淫糜
淫魔
淫母
淫女
淫虐
淫妻
淫情
淫色
淫声浪语
淫兽学园
淫书
淫术炼金士
淫水
淫娃
淫威
淫亵
淫样
淫液
淫照
阴b
应召
幼交
幼男
幼女
欲火
欲女
玉女心经
玉蒲团
玉乳
欲仙欲死
玉穴
援交
原味内衣
援助交际
张筱雨
招鸡
招妓
中年美妇
抓胸
自拍
自慰
作爱
18禁
99bb
a4u
a4y
adult
amateur
anal
a片
fuck
gay片
g点
g片
hardcore
h动画
h动漫
incest
porn
secom
sexinsex
sm女王
xiao77
xing伴侣
tokyohot
yin荡

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,244 @@
腐败
贪污
gcd
共贪党
gongchandang
阿共
共一产一党
产党共
公产党
工产党
共c党
共x党
供产
共铲
共惨
供铲党
供铲谠
供铲裆
共残党
共残主义
共产主义的幽灵
拱铲
老共
中珙
中gong
gc党
贡挡
gong党
g产
狗产蛋
共残裆
恶党
邪党
共产专制
共产王朝
裆中央
土共
土g
共狗
g匪
共匪
仇共
共产党腐败
共产党专制
共产党的报应
共产党的末日
共产党专制
communistparty
症腐
政腐
政付
正府
政俯
政f
zhengfu
政zhi
挡中央
档中央
中国zf
中央zf
国wu院
中华帝国
gong和
大陆官方
北京政权
刘志军
张曙
刘志军
买别墅
玩女人
贪20亿
许宗衡
贪财物
李启红
贪腐财富
落马
高官名单
陈希同
贪污
玩忽职守
有期徒刑
陈良宇
受贿罪
滥用职权
有期徒刑
没收个人财产
成克杰
死刑
程维高
严重违纪
开除党籍
撤销职务
刘方仁
无期徒刑
倪献策
徇私舞弊
梁湘
以权谋私
撤职。
李嘉廷
死刑缓期
张国光
韩桂芝
宋平顺
自杀
黄瑶
双规
陈绍基
判处死刑
剥夺政治权利终身
没收个人全部财产
石兆彬
侯伍杰
王昭耀
剥夺政治权利
杜世成
沈图
叛逃美国
罗云光
起诉
张辛泰
李效时
边少斌
徐鹏航
违纪
收受股票
王乐毅
李纪周
郑光迪
田凤山。
邱晓华
郑筱萸
孙鹤龄
蓝田造假案
于幼军
留党察看
何洪达
朱志刚
杨汇泉
官僚主义
徐炳松
托乎提沙比尔
王宝森
经济犯罪
畏罪自杀。
陈水文
孟庆平
胡长清
朱川
许运鸿
丘广钟
刘知炳
丛福奎
王怀忠
巨额财产
来源不明罪
李达昌
刘长贵
王钟麓
阿曼哈吉
付晓光
自动辞
刘克田
吕德彬
刘维明
双开
刘志华
孙瑜
李堂堂
韩福才 青海
欧阳德 广东
韦泽芳 海南
铁英 北京
辛业江 海南
于飞 广东
姜殿武 河北
秦昌典 重庆
范广举 黑龙江
张凯广东
王厚宏海南
陈维席安徽
王有杰河南
王武龙江苏
米凤君吉林
宋勇辽宁
张家盟浙江
马烈孙宁夏
黄纪诚北京
常征贵州
王式惠重庆
周文吉
王庆录广西
潘广田山东
朱作勇甘肃
孙善武河南
宋晨光江西
梁春禄广西政协
鲁家善 中国交通
金德琴 中信
李大强 神华
吴文英 纺织
查克明 华能
朱小华光大
高严 国家电力
王雪冰
林孔兴
刘金宝
张恩照
陈同海
康日新
王益
张春江
洪清源
平义杰
李恩潮
孙小虹
陈忠
慕绥新
田凤岐
麦崇楷
柴王群
吴振汉
张秋阳
徐衍东
徐发 黑龙江
张宗海
丁鑫发
徐国健
李宝金
单平
段义和
荆福生
陈少勇
黄松有
皮黔生
王华元
王守业
刘连昆
孙晋美
邵松高
肖怀枢
刘广智 空军
姬胜德 总参
廖伯年 北京

File diff suppressed because it is too large Load Diff