diff --git a/.gitignore b/.gitignore index 3980640a..c6cf6d0d 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ __debug_bin /data /custom /.custom +/run.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 07ace981..b8b64118 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,19 @@ All notable changes to paopao-ce are documented in this file. MaxLogBuffer: 100 # 最大log缓存条数, 设置范围[10, 10000], 默认100 ... ``` +- Added friend tweets bar feature support in home page. [#377](https://github.com/rocboss/paopao-ce/pull/377) +- web: add custom `Friendship` feature support. To custom setup `Friendship` use below configure in `web/.env` or `web/.env.local` + ``` + # 功能特性开启 + VITE_USE_FRIENDSHIP=true + + # 模块开启 + VITE_ENABLE_FRIENDS_BAR=true + ``` + +## 0.4.2 +### Fixed +- fixed remove multi-objects no effects and occurs resource leak error when use Minio as OSS(Object Storage System).[#371](https://github.com/rocboss/paopao-ce/pull/371) [#372](https://github.com/rocboss/paopao-ce/pull/372) ## 0.4.1 ### Changed diff --git a/README.md b/README.md index 8d3be694..f1ebb53b 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Web端: 更多演示请前往[官网](https://www.paopao.info)体验(谢绝灌水) 桌面端: - +
diff --git a/auto/api/v1/core.go b/auto/api/v1/core.go index 159e938f..3489568e 100644 --- a/auto/api/v1/core.go +++ b/auto/api/v1/core.go @@ -31,7 +31,6 @@ type Core interface { SendUserWhisper(*web.SendWhisperReq) mir.Error ReadMessage(*web.ReadMessageReq) mir.Error GetMessages(*web.GetMessagesReq) (*web.GetMessagesResp, mir.Error) - GetUnreadMsgCount(*web.GetUnreadMsgCountReq) (*web.GetUnreadMsgCountResp, mir.Error) GetUserInfo(*web.UserInfoReq) (*web.UserInfoResp, mir.Error) SyncSearchIndex(*web.SyncSearchIndexReq) mir.Error @@ -229,20 +228,6 @@ func RegisterCoreServant(e *gin.Engine, s Core) { resp, err := s.GetMessages(req) s.Render(c, resp, err) }) - router.Handle("GET", "/user/msgcount/unread", func(c *gin.Context) { - select { - case <-c.Request.Context().Done(): - return - default: - } - req := new(web.GetUnreadMsgCountReq) - if err := s.Bind(c, req); err != nil { - s.Render(c, nil, err) - return - } - resp, err := s.GetUnreadMsgCount(req) - s.Render(c, resp, err) - }) router.Handle("GET", "/user/info", func(c *gin.Context) { select { case <-c.Request.Context().Done(): @@ -332,10 +317,6 @@ func (UnimplementedCoreServant) GetMessages(req *web.GetMessagesReq) (*web.GetMe return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented)) } -func (UnimplementedCoreServant) GetUnreadMsgCount(req *web.GetUnreadMsgCountReq) (*web.GetUnreadMsgCountResp, mir.Error) { - return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented)) -} - func (UnimplementedCoreServant) GetUserInfo(req *web.UserInfoReq) (*web.UserInfoResp, mir.Error) { return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented)) } diff --git a/auto/api/v1/relax.go b/auto/api/v1/relax.go new file mode 100644 index 00000000..e88fc8c7 --- /dev/null +++ b/auto/api/v1/relax.go @@ -0,0 +1,66 @@ +// Code generated by go-mir. DO NOT EDIT. +// versions: +// - mir v4.0.0 + +package v1 + +import ( + "net/http" + + "github.com/alimy/mir/v4" + "github.com/gin-gonic/gin" + "github.com/rocboss/paopao-ce/internal/model/web" +) + +type Relax interface { + _default_ + + // Chain provide handlers chain for gin + Chain() gin.HandlersChain + + GetUnreadMsgCount(*web.GetUnreadMsgCountReq) (*web.GetUnreadMsgCountResp, mir.Error) + + mustEmbedUnimplementedRelaxServant() +} + +// RegisterRelaxServant register Relax servant to gin +func RegisterRelaxServant(e *gin.Engine, s Relax) { + router := e.Group("v1") + // use chain for router + middlewares := s.Chain() + router.Use(middlewares...) + + // register routes info to router + router.Handle("GET", "/user/msgcount/unread", func(c *gin.Context) { + select { + case <-c.Request.Context().Done(): + return + default: + } + req := new(web.GetUnreadMsgCountReq) + if err := s.Bind(c, req); err != nil { + s.Render(c, nil, err) + return + } + resp, err := s.GetUnreadMsgCount(req) + if err != nil { + s.Render(c, nil, err) + return + } + var rv _render_ = resp + rv.Render(c) + }) +} + +// UnimplementedRelaxServant can be embedded to have forward compatible implementations. +type UnimplementedRelaxServant struct{} + +func (UnimplementedRelaxServant) Chain() gin.HandlersChain { + return nil +} + +func (UnimplementedRelaxServant) GetUnreadMsgCount(req *web.GetUnreadMsgCountReq) (*web.GetUnreadMsgCountResp, mir.Error) { + return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented)) +} + +func (UnimplementedRelaxServant) mustEmbedUnimplementedRelaxServant() {} diff --git a/docker-compose.yaml.backup b/docker-compose.yaml.backup index f68b88d4..bf163010 100644 --- a/docker-compose.yaml.backup +++ b/docker-compose.yaml.backup @@ -51,20 +51,20 @@ services: networks: - paopao-network - zinc: - image: bitbus/zincsearch:latest - user: zincsearch - restart: always - ports: - - 4080:4080 - volumes: - - ./custom/data/zinc/data:/data - environment: - ZINC_FIRST_ADMIN_USER: admin - ZINC_FIRST_ADMIN_PASSWORD: admin - DATA_PATH: /data - networks: - - paopao-network + # zinc: + # image: bitbus/zincsearch:latest + # user: zincsearch + # restart: always + # ports: + # - 4080:4080 + # volumes: + # - ./custom/data/zinc/data:/data + # environment: + # ZINC_FIRST_ADMIN_USER: admin + # ZINC_FIRST_ADMIN_PASSWORD: admin + # DATA_PATH: /data + # networks: + # - paopao-network meili: image: getmeili/meilisearch:v1.3 @@ -78,6 +78,14 @@ services: networks: - paopao-network + meilisearch-ui: + image: riccoxie/meilisearch-ui:latest + restart: always + ports: + - 24900:24900 + networks: + - paopao-network + # openobserve: # image: public.ecr.aws/zinclabs/openobserve:latest # restart: always @@ -102,18 +110,18 @@ services: # networks: # - paopao-network - phpmyadmin: - image: phpmyadmin:5.2 - depends_on: - - db - ports: - - 8080:80 - environment: - - PMA_HOST=db - - PMA_USER=paopao - - PMA_PASSWORD=paopao - networks: - - paopao-network + # phpmyadmin: + # image: phpmyadmin:5.2 + # depends_on: + # - db + # ports: + # - 8080:80 + # environment: + # - PMA_HOST=db + # - PMA_USER=paopao + # - PMA_PASSWORD=paopao + # networks: + # - paopao-network backend: image: bitbus/paopao-ce:nightly diff --git a/docs/proposal/.assets/000-00.jpg b/docs/proposal/.assets/000-00.jpg new file mode 100644 index 00000000..21ab3b16 Binary files /dev/null and b/docs/proposal/.assets/000-00.jpg differ diff --git a/go.mod b/go.mod index 7c086f8e..de59feed 100644 --- a/go.mod +++ b/go.mod @@ -6,39 +6,42 @@ require ( github.com/Masterminds/semver/v3 v3.2.1 github.com/afocus/captcha v0.0.0-20191010092841-4bd1f21c8868 github.com/alimy/mir/v4 v4.0.0 - github.com/alimy/tryst v0.3.0 - github.com/aliyun/aliyun-oss-go-sdk v2.2.8+incompatible + github.com/alimy/tryst v0.8.2 + github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible github.com/allegro/bigcache/v3 v3.1.0 github.com/bufbuild/connect-go v1.10.0 github.com/bytedance/sonic v1.10.0 - github.com/cockroachdb/errors v1.10.0 + github.com/cockroachdb/errors v1.11.1 github.com/disintegration/imaging v1.6.2 github.com/fatih/color v1.15.0 - github.com/getsentry/sentry-go v0.23.0 + github.com/getsentry/sentry-go v0.24.0 github.com/gin-contrib/cors v1.4.0 github.com/gin-gonic/gin v1.9.1 github.com/go-resty/resty/v2 v2.7.0 github.com/goccy/go-json v0.10.2 github.com/gofrs/uuid/v5 v5.0.0 - github.com/golang-jwt/jwt/v4 v4.5.0 + github.com/golang-jwt/jwt/v5 v5.0.0 github.com/golang-migrate/migrate/v4 v4.15.2 + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/huaweicloud/huaweicloud-sdk-go-obs v3.23.4+incompatible github.com/json-iterator/go v1.1.12 github.com/meilisearch/meilisearch-go v0.25.0 - github.com/minio/minio-go/v7 v7.0.62 - github.com/onsi/ginkgo/v2 v2.11.0 + github.com/minio/minio-go/v7 v7.0.63 + github.com/onsi/ginkgo/v2 v2.12.0 github.com/onsi/gomega v1.27.10 github.com/pyroscope-io/client v0.7.2 - github.com/redis/rueidis v1.0.15 + github.com/redis/rueidis v1.0.17 + github.com/robfig/cron/v3 v3.0.1 github.com/sirupsen/logrus v1.9.3 - github.com/smartwalle/alipay/v3 v3.2.15 + github.com/smartwalle/alipay/v3 v3.2.16 github.com/sourcegraph/conc v0.3.0 github.com/spf13/cobra v1.7.0 github.com/spf13/viper v1.16.0 - github.com/tencentyun/cos-go-sdk-v5 v0.7.42 + github.com/tencentyun/cos-go-sdk-v5 v0.7.43 github.com/yinheli/mahonia v0.0.0-20131226213531-0eef680515cc go.uber.org/automaxprocs v1.5.3 - google.golang.org/grpc v1.57.0 + golang.org/x/image v0.0.0-20210216034530-4410531fe030 + google.golang.org/grpc v1.58.0 google.golang.org/protobuf v1.31.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/resty.v1 v1.12.0 @@ -69,7 +72,7 @@ require ( github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/go-querystring v1.1.0 // indirect @@ -77,6 +80,7 @@ require ( github.com/google/uuid v1.3.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.6 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect @@ -109,8 +113,8 @@ require ( github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/rs/xid v1.5.0 // indirect - github.com/smartwalle/ncrypto v1.0.2 // indirect - github.com/smartwalle/ngx v1.0.6 // indirect + github.com/smartwalle/ncrypto v1.0.3 // indirect + github.com/smartwalle/ngx v1.0.7 // indirect github.com/smartwalle/nsign v1.0.8 // indirect github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.5.1 // indirect @@ -124,14 +128,13 @@ require ( go.uber.org/atomic v1.9.0 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/crypto v0.12.0 // indirect - golang.org/x/image v0.0.0-20210216034530-4410531fe030 // indirect - golang.org/x/mod v0.10.0 // indirect + golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.14.0 // indirect golang.org/x/sys v0.11.0 // indirect golang.org/x/text v0.12.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.9.3 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect + golang.org/x/tools v0.12.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/uint128 v1.2.0 // indirect diff --git a/go.sum b/go.sum index b33e4ccc..4f9b4638 100644 --- a/go.sum +++ b/go.sum @@ -125,10 +125,10 @@ github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:C github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY9UnI16Z+UJqRyk= github.com/alimy/mir/v4 v4.0.0 h1:MzGfmoLjjvR69jbZEmpKJO3tUuqB0RGRv1UWPbtukBg= github.com/alimy/mir/v4 v4.0.0/go.mod h1:d58dBvw2KImcVbAUANrciEV/of0arMNsI9c/5UNCMMc= -github.com/alimy/tryst v0.3.0 h1:8oeKy8iQQnO6BCs0YGJBnVyVxi6Y3w18n5Xz/uefUtk= -github.com/alimy/tryst v0.3.0/go.mod h1:K//dPeoE/nnv2Jw8C3iPE7n8mO6LVqAxVmqbopM9nAk= -github.com/aliyun/aliyun-oss-go-sdk v2.2.8+incompatible h1:6JF1bjhT0WN2srEmijfOFtVWwV91KZ6dJY1/JbdtGrI= -github.com/aliyun/aliyun-oss-go-sdk v2.2.8+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= +github.com/alimy/tryst v0.8.2 h1:azu5B58vS6m/ZeHovYGWjVvEOJN2llDIBLxuN3qtMtk= +github.com/alimy/tryst v0.8.2/go.mod h1:ua2eJbFrisHPh7z93Bgc0jNBE8Khu1SCx2p/6t3OzZI= +github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible h1:Sg/2xHwDrioHpxTN6WMiwbXTpUEinBpHsN7mG21Rc2k= +github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk= github.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= @@ -243,8 +243,8 @@ github.com/cockroachdb/cockroach-go/v2 v2.1.1/go.mod h1:7NtUnP6eK+l6k483WSYNrq3K github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= -github.com/cockroachdb/errors v1.10.0 h1:lfxS8zZz1+OjtV4MtNWgboi/W5tyLEB6VQZBXN+0VUU= -github.com/cockroachdb/errors v1.10.0/go.mod h1:lknhIsEVQ9Ss/qKDBQS/UqFSvPQjOwNq2qyKAxtHRqE= +github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= +github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= @@ -456,8 +456,8 @@ github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= -github.com/getsentry/sentry-go v0.23.0 h1:dn+QRCeJv4pPt9OjVXiMcGIBIefaTJPw/h0bZWO05nE= -github.com/getsentry/sentry-go v0.23.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= +github.com/getsentry/sentry-go v0.24.0 h1:02b7qEmJ56EHGe9KFgjArjU/vG/aywm7Efgu+iPc01Y= +github.com/getsentry/sentry-go v0.24.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/cors v1.4.0 h1:oJ6gwtUl3lqV0WEIwM/LxPF1QZ5qe2lGWdY2+bz7y0g= @@ -581,6 +581,8 @@ github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-migrate/migrate/v4 v4.15.2 h1:vU+M05vs6jWHKDdmE1Ecwj0BznygFc4QsdRe2E/L7kc= github.com/golang-migrate/migrate/v4 v4.15.2/go.mod h1:f2toGLkYqD3JH+Todi4aZ2ZdbeUNx4sIwiOK96rE9Lw= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= @@ -727,6 +729,8 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru/v2 v2.0.6 h1:3xi/Cafd1NaoEnS/yDssIiuVeDVywU0QdFGl3aQaQHM= +github.com/hashicorp/golang-lru/v2 v2.0.6/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -939,8 +943,8 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.62 h1:qNYsFZHEzl+NfH8UxW4jpmlKav1qUAgfY30YNRneVhc= -github.com/minio/minio-go/v7 v7.0.62/go.mod h1:Q6X7Qjb7WMhvG65qKf4gUgA5XaiSox74kR1uAEjxRS4= +github.com/minio/minio-go/v7 v7.0.63 h1:GbZ2oCvaUdgT5640WJOpyDhhDxvknAJU2/T3yurwcbQ= +github.com/minio/minio-go/v7 v7.0.63/go.mod h1:Q6X7Qjb7WMhvG65qKf4gUgA5XaiSox74kR1uAEjxRS4= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= @@ -1008,8 +1012,8 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108 github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= -github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= +github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI= +github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -1117,12 +1121,14 @@ github.com/pyroscope-io/client v0.7.2 h1:OX2qdUQsS8RSkn/3C8isD7f/P0YiZQlRbAlecAa github.com/pyroscope-io/client v0.7.2/go.mod h1:FEocnjn+Ngzxy6EtU9ZxXWRvQ0+pffkrBxHLnPpxwi8= github.com/pyroscope-io/godeltaprof v0.1.2 h1:MdlEmYELd5w+lvIzmZvXGNMVzW2Qc9jDMuJaPOR75g4= github.com/pyroscope-io/godeltaprof v0.1.2/go.mod h1:psMITXp90+8pFenXkKIpNhrfmI9saQnPbba27VIaiQE= -github.com/redis/rueidis v1.0.15 h1:KjTaoP4ab6lpxyCwgIEZ3/rqvKfKnbICe83tVaaItxQ= -github.com/redis/rueidis v1.0.15/go.mod h1:8B+r5wdnjwK3lTFml5VtxjzGOQAC+5UmujoD12pDrEo= +github.com/redis/rueidis v1.0.17 h1:RyjiBVnPcKxjgiUpkyqbRw/OFJV5vX2bMM/oMPdz8JE= +github.com/redis/rueidis v1.0.17/go.mod h1:8B+r5wdnjwK3lTFml5VtxjzGOQAC+5UmujoD12pDrEo= github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -1164,12 +1170,12 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartwalle/alipay/v3 v3.2.15 h1:3fvFJnINKKAOXHR/Iv20k1Z7KJ+nOh3oK214lELPqG8= -github.com/smartwalle/alipay/v3 v3.2.15/go.mod h1:niTNB609KyUYuAx9Bex/MawEjv2yPx4XOjxSAkqmGjE= -github.com/smartwalle/ncrypto v1.0.2 h1:pTAhCqtPCMhpOwFXX+EcMdR6PNzruBNoGQrN2S1GbGI= -github.com/smartwalle/ncrypto v1.0.2/go.mod h1:Dwlp6sfeNaPMnOxMNayMTacvC5JGEVln3CVdiVDgbBk= -github.com/smartwalle/ngx v1.0.6 h1:JPNqNOIj+2nxxFtrSkJO+vKJfeNUSEQueck/Wworjps= -github.com/smartwalle/ngx v1.0.6/go.mod h1:mx/nz2Pk5j+RBs7t6u6k22MPiBG/8CtOMpCnALIG8Y0= +github.com/smartwalle/alipay/v3 v3.2.16 h1:oSzcQgV+kUHH7ko7FjYowU4RIm6chuQjgXeuChUbj0M= +github.com/smartwalle/alipay/v3 v3.2.16/go.mod h1:5EC6QZNr51TjmDAJFHSEJMLLSoTtge7583W2vuNmOYc= +github.com/smartwalle/ncrypto v1.0.3 h1:fnzjoriZt2LZeD8ljEtRe2eU33Au7i8vIF4Gafz5RuI= +github.com/smartwalle/ncrypto v1.0.3/go.mod h1:Dwlp6sfeNaPMnOxMNayMTacvC5JGEVln3CVdiVDgbBk= +github.com/smartwalle/ngx v1.0.7 h1:BIQo6wmAnERehogNKUnthoxwBavTWxbR9oLFcGjWXKQ= +github.com/smartwalle/ngx v1.0.7/go.mod h1:mx/nz2Pk5j+RBs7t6u6k22MPiBG/8CtOMpCnALIG8Y0= github.com/smartwalle/nsign v1.0.8 h1:78KWtwKPrdt4Xsn+tNEBVxaTLIJBX9YRX0ZSrMUeuHo= github.com/smartwalle/nsign v1.0.8/go.mod h1:eY6I4CJlyNdVMP+t6z1H6Jpd4m5/V+8xi44ufSTxXgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -1239,8 +1245,8 @@ github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0= -github.com/tencentyun/cos-go-sdk-v5 v0.7.42 h1:Up1704BJjI5orycXKjpVpvuOInt9GC5pqY4knyE9Uds= -github.com/tencentyun/cos-go-sdk-v5 v0.7.42/go.mod h1:LUFnaqRmGk6pEHOaRmdn2dCZR2j0cSsM5xowWFPTPao= +github.com/tencentyun/cos-go-sdk-v5 v0.7.43 h1:aPCPWy85T3C3Ga3hn7va2DC4c0hAf8Dx0kpKj/uB/vw= +github.com/tencentyun/cos-go-sdk-v5 v0.7.43/go.mod h1:LUFnaqRmGk6pEHOaRmdn2dCZR2j0cSsM5xowWFPTPao= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -1436,8 +1442,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1538,7 +1544,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1774,8 +1780,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= -golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= +golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1902,8 +1908,8 @@ google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1937,8 +1943,8 @@ google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/grpc v1.58.0 h1:32JY8YpPMSR45K+c3o6b8VL73V+rR8k+DeMIr4vRH8o= +google.golang.org/grpc v1.58.0/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/internal/conf/cache.go b/internal/conf/cache.go new file mode 100644 index 00000000..ae3875b2 --- /dev/null +++ b/internal/conf/cache.go @@ -0,0 +1,30 @@ +// Copyright 2023 ROC. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package conf + +import ( + "fmt" + + "github.com/alimy/tryst/cache" +) + +const ( + _defaultKeyPoolSize = 128 +) + +// 以下包含一些在cache中会用到的池化后的key +var ( + KeyUnreadMsg cache.KeyPool[int64] +) + +func initCacheKeyPool() { + poolSize := _defaultKeyPoolSize + if poolSize < CacheSetting.KeyPoolSize { + poolSize = CacheSetting.KeyPoolSize + } + KeyUnreadMsg = cache.MustKeyPool[int64](poolSize, func(key int64) string { + return fmt.Sprintf("paopao:unreadmsg:%d", key) + }) +} diff --git a/internal/conf/redis.go b/internal/conf/cache_redis.go similarity index 92% rename from internal/conf/redis.go rename to internal/conf/cache_redis.go index 93799b82..00564a31 100644 --- a/internal/conf/redis.go +++ b/internal/conf/cache_redis.go @@ -29,6 +29,8 @@ func MustRedisClient() rueidis.Client { log.Fatalf("create a redis client failed: %s", err) } _redisClient = client + // 顺便初始化一下CacheKeyPool + initCacheKeyPool() }) return _redisClient } diff --git a/internal/conf/conf.go b/internal/conf/conf.go index 213d4f9f..bc4be72f 100644 --- a/internal/conf/conf.go +++ b/internal/conf/conf.go @@ -35,6 +35,8 @@ var ( DocsServerSetting *httpServerConf MobileServerSetting *grpcServerConf AppSetting *appConf + CacheSetting *cacheConf + EventManagerSetting *eventManagerConf CacheIndexSetting *cacheIndexConf SimpleCacheIndexSetting *simpleCacheIndexConf BigCacheIndexSetting *bigCacheIndexConf @@ -69,6 +71,8 @@ func setupSetting(suite []string, noDefault bool) error { objects := map[string]any{ "App": &AppSetting, + "Cache": &CacheSetting, + "EventManager": &EventManagerSetting, "PprofServer": &PprofServerSetting, "WebServer": &WebServerSetting, "AdminServer": &AdminServerSetting, @@ -115,6 +119,8 @@ func setupSetting(suite []string, noDefault bool) error { } } + CacheSetting.CientSideCacheExpire *= time.Second + EventManagerSetting.TickWaitTime *= time.Second JWTSetting.Expire *= time.Second SimpleCacheIndexSetting.CheckTickDuration *= time.Second SimpleCacheIndexSetting.ExpireTickDuration *= time.Second diff --git a/internal/conf/config.yaml b/internal/conf/config.yaml index a4deda16..5f6c8ceb 100644 --- a/internal/conf/config.yaml +++ b/internal/conf/config.yaml @@ -5,6 +5,16 @@ App: # APP基础设置项 DefaultContextTimeout: 60 DefaultPageSize: 10 MaxPageSize: 100 +Cache: + KeyPoolSize: 256 # 键的池大小, 设置范围[128, ++], 默认256 + CientSideCacheExpire: 60 # 客户端缓存过期时间 默认60s + UnreadMsgExpire: 60 # 未读消息过期时间,单位秒, 默认60s +EventManager: # 事件管理器的配置参数 + MinWorker: 10 # 最小后台工作者, 设置范围[5, ++], 默认10 + MaxEventBuf: 100 # 最大log缓存条数, 设置范围[10, ++], 默认100 + MaxTempEventBuf: 100 # 最大log缓存条数, 设置范围[10, ++], 默认100 + MaxTickCount: 60 # 最大的循环周期, 设置范围[60, ++], 默认60 + TickWaitTime: 1 # 一个周期的等待时间,单位:秒 默认1s Features: Default: [] WebServer: # Web服务 @@ -64,8 +74,9 @@ SmsJuhe: TplID: TplVal: "#code#=%sm#=%d" Alipay: - AppID: + AppID: "paopao-ce-app-id" InProduction: True + PrivateKey: "MIICXAIBAAKBgQCzXV/spaX9+eOjM5f12W6eDTtszU9f9rgpXG4EQwzZI3WM5+Fe+9Bn6NQQILfF1o3Z+3BEzHMMcYwxrQw/toq2o6JPchbUK7eArKc6pl/GV3uIefZdKncz5bZvCFMgiJrpy75lYKhJgotQFEfQd+ks2t0gtC007uOjmY9QDB2EVQIDAQABAoGAMruhi0UbW2gYHCxWuiJDKI9jlJXJ8sHNO126fJgehTiDYlSgKYaeXxW7DcjDUkEqpFJ7YepWTFm9prtksIzIVQFNNjstI6cvowVF2t+lWf7mIB4w0ugarVd+SXssQK830Og3kjtZ84a3BbC6uf3a/qcgoIO8Sj1VnzOJ8fEYl+0CQQDeG6JhauGDOC8oCTwbFs9QPpjwGnp7UkYAJNg7jn4uBSVeg4lwb5uj9TshLSp49geNkPcWeCythuiz1jvoTqEjAkEAzrwIBxUPT1WmcDUXAkVPaQNADDbhMZLdw5nHZEUVwmO3o1FkJky4MLjLjT977400mhsnsQCy4sAWUZs6aEyoJwJARK3U2zy6eOHhqwaYAGRgPJbuoaf+Ya3CGX9LIbdhCwfqUzxnPk40mVFWNF8L+BVTppHB5b/JSOsjf6BqK95McwJBAL+kvUhbdHrV6lmgTXkUaV3u3mO0SCPdgui9WIKSLG6sY+LpI48BlcnMtR12WVyjKL0nKS9Dd5EOAmKaJJXlYgcCQGWbWCn9KUDUqpm4o3wr5nwXzlS74XYZo65UAM7TSzHRpcovfv5uiQ0VRLImWeiSXKK2aTOBGn5eKbevRTxN07k=" RootCertFile: "custom/alipay/RootCert.crt" PublicCertFile: "custom/alipay/CertPublicKey_RSA2.crt" AppPublicCertFile: "custom/alipay/AppCertPublicKey.crt" diff --git a/internal/conf/logger_observe.go b/internal/conf/logger_observe.go index 6297fa82..dc83a83e 100644 --- a/internal/conf/logger_observe.go +++ b/internal/conf/logger_observe.go @@ -53,11 +53,11 @@ func newObserveLogHook() *observeLogHook { Secure: s.Secure, } acc := &hx.AsyncClientConf{ - MinWorker: s.MinWorker, - MaxRequestInCh: s.MaxLogBuffer, - MaxRequestInTempCh: 100, - MaxTickCount: 60, - TickWaitTime: time.Second, + MinWorker: s.MinWorker, + MaxRequestBuf: s.MaxLogBuffer, + MaxRequestTempBuf: 100, + MaxTickCount: 60, + TickWaitTime: time.Second, } return &observeLogHook{ client: obx.NewClient(obc, acc, func(req *http.Request, resp *http.Response, err error) { diff --git a/internal/conf/setting.go b/internal/conf/setting.go index a210cbb1..04fcd9c9 100644 --- a/internal/conf/setting.go +++ b/internal/conf/setting.go @@ -96,6 +96,20 @@ type appConf struct { MaxPageSize int } +type cacheConf struct { + KeyPoolSize int + CientSideCacheExpire time.Duration + UnreadMsgExpire int64 +} + +type eventManagerConf struct { + MinWorker int + MaxEventBuf int + MaxTempEventBuf int + MaxTickCount int + TickWaitTime time.Duration +} + type cacheIndexConf struct { MaxUpdateQPS int MinWorker int diff --git a/internal/core/cache.go b/internal/core/cache.go index 8d8c3b42..2a0db675 100644 --- a/internal/core/cache.go +++ b/internal/core/cache.go @@ -97,3 +97,9 @@ type RedisCache interface { SetRechargeStatus(ctx context.Context, tradeNo string) error DelRechargeStatus(ctx context.Context, tradeNo string) error } + +type WebCache interface { + GetUnreadMsgCountResp(uid int64) ([]byte, error) + PutUnreadMsgCountResp(uid int64, data []byte) error + DelUnreadMsgCountResp(uid int64) error +} diff --git a/internal/core/messages.go b/internal/core/messages.go index 519e4e49..09314204 100644 --- a/internal/core/messages.go +++ b/internal/core/messages.go @@ -14,6 +14,6 @@ type MessageService interface { GetUnreadCount(userID int64) (int64, error) GetMessageByID(id int64) (*ms.Message, error) ReadMessage(message *ms.Message) error - GetMessages(conditions *ms.ConditionsT, offset, limit int) ([]*ms.MessageFormated, error) - GetMessageCount(conditions *ms.ConditionsT) (int64, error) + GetMessages(userId int64, offset, limit int) ([]*ms.MessageFormated, error) + GetMessageCount(userId int64) (int64, error) } diff --git a/internal/dao/cache/cache.go b/internal/dao/cache/cache.go index 3a2938cb..05f6fce8 100644 --- a/internal/dao/cache/cache.go +++ b/internal/dao/cache/cache.go @@ -6,6 +6,7 @@ package cache import ( "context" + "sync" "time" "github.com/allegro/bigcache/v3" @@ -14,6 +15,10 @@ import ( "github.com/sirupsen/logrus" ) +var ( + _onceInit sync.Once +) + func NewRedisCache() core.RedisCache { return &redisCache{ c: conf.MustRedisClient(), @@ -48,6 +53,11 @@ func NewRedisCacheIndexService(ips core.IndexPostsService, ams core.Authorizatio return cacheIndex, cacheIndex } +func NewWebCache() core.WebCache { + lazyInitial() + return _webCache +} + func NewSimpleCacheIndexService(indexPosts core.IndexPostsService) (core.CacheIndexService, core.VersionInfo) { s := conf.SimpleCacheIndexSetting cacheIndex := &simpleCacheIndexServant{ @@ -88,3 +98,9 @@ func NewNoneCacheIndexService(indexPosts core.IndexPostsService) (core.CacheInde } return obj, obj } + +func lazyInitial() { + _onceInit.Do(func() { + _webCache = newWebCache() + }) +} diff --git a/internal/dao/cache/redis.go b/internal/dao/cache/redis.go index 50b581a7..cf1a2c4e 100644 --- a/internal/dao/cache/redis.go +++ b/internal/dao/cache/redis.go @@ -59,9 +59,22 @@ func (s *redisCacheTweetsCache) delTweets(keys []string) error { return s.c.Do(context.Background(), cmd).Error() } -func (s *redisCacheTweetsCache) allKeys() ([]string, error) { - cmd := s.c.B().Keys().Pattern(_cacheIndexKeyPattern).Build() - return s.c.Do(context.Background(), cmd).AsStrSlice() +func (s *redisCacheTweetsCache) allKeys() (res []string, err error) { + cursor := uint64(0) + for { + cmd := s.c.B().Scan().Cursor(cursor).Match(_cacheIndexKeyPattern).Count(50).Build() + entry, err := s.c.Do(context.Background(), cmd).AsScanEntry() + if err != nil { + return nil, err + } + res = append(res, entry.Elements...) + if entry.Cursor != 0 { + cursor = entry.Cursor + continue + } + break + } + return } func (s *redisCacheTweetsCache) Name() string { diff --git a/internal/dao/cache/web.go b/internal/dao/cache/web.go new file mode 100644 index 00000000..d911c5bc --- /dev/null +++ b/internal/dao/cache/web.go @@ -0,0 +1,66 @@ +// Copyright 2023 ROC. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package cache + +import ( + "context" + "time" + + "github.com/Masterminds/semver/v3" + "github.com/redis/rueidis" + "github.com/rocboss/paopao-ce/internal/conf" + "github.com/rocboss/paopao-ce/internal/core" + "github.com/rocboss/paopao-ce/pkg/utils" +) + +var ( + _webCache core.WebCache = (*redisWebCache)(nil) +) + +type redisWebCache struct { + cscExpire time.Duration + unreadMsgExpire int64 + c rueidis.Client +} + +func (s *redisWebCache) Name() string { + return "RedisWebCache" +} + +func (s *redisWebCache) Version() *semver.Version { + return semver.MustParse("v0.1.0") +} + +func (s *redisWebCache) GetUnreadMsgCountResp(uid int64) ([]byte, error) { + key := conf.KeyUnreadMsg.Get(uid) + res, err := rueidis.MGetCache(s.c, context.Background(), s.cscExpire, []string{key}) + if err != nil { + return nil, err + } + message := res[key] + return message.AsBytes() +} + +func (s *redisWebCache) PutUnreadMsgCountResp(uid int64, data []byte) error { + return s.c.Do(context.Background(), s.c.B().Set(). + Key(conf.KeyUnreadMsg.Get(uid)). + Value(utils.String(data)). + ExSeconds(s.unreadMsgExpire). + Build()). + Error() +} + +func (s *redisWebCache) DelUnreadMsgCountResp(uid int64) error { + return s.c.Do(context.Background(), s.c.B().Del().Key(conf.KeyUnreadMsg.Get(uid)).Build()).Error() +} + +func newWebCache() *redisWebCache { + s := conf.CacheSetting + return &redisWebCache{ + cscExpire: s.CientSideCacheExpire, + unreadMsgExpire: s.UnreadMsgExpire, + c: conf.MustRedisClient(), + } +} diff --git a/internal/dao/jinzhu/dbr/message.go b/internal/dao/jinzhu/dbr/message.go index 6c952bae..cac72cbf 100644 --- a/internal/dao/jinzhu/dbr/message.go +++ b/internal/dao/jinzhu/dbr/message.go @@ -38,6 +38,7 @@ type MessageFormated struct { SenderUserID int64 `json:"sender_user_id"` SenderUser *UserFormated `json:"sender_user"` ReceiverUserID int64 `json:"receiver_user_id"` + ReceiverUser *UserFormated `json:"receiver_user,omitempty"` Type MessageT `json:"type"` Brief string `json:"brief"` Content string `json:"content"` @@ -61,6 +62,7 @@ func (m *Message) Format() *MessageFormated { SenderUserID: m.SenderUserID, SenderUser: &UserFormated{}, ReceiverUserID: m.ReceiverUserID, + ReceiverUser: &UserFormated{}, Type: m.Type, Brief: m.Brief, Content: m.Content, @@ -114,39 +116,20 @@ func (m *Message) FetchBy(db *gorm.DB, predicates Predicates) ([]*Message, error return messages, nil } -func (c *Message) List(db *gorm.DB, conditions *ConditionsT, offset, limit int) ([]*Message, error) { - var messages []*Message - var err error +func (c *Message) List(db *gorm.DB, userId int64, offset, limit int) (res []*Message, err error) { if offset >= 0 && limit > 0 { db = db.Offset(offset).Limit(limit) } - - for k, v := range *conditions { - if k == "ORDER" { - db = db.Order(v) - } else { - db = db.Where(k, v) - } - } - - if err = db.Where("is_del = ?", 0).Find(&messages).Error; err != nil { - return nil, err - } - - return messages, nil + err = db.Where("receiver_user_id=? OR (sender_user_id=? AND type=4)", userId, userId).Order("id DESC").Find(&res).Error + return } -func (m *Message) Count(db *gorm.DB, conditions *ConditionsT) (int64, error) { - var count int64 - - for k, v := range *conditions { - if k != "ORDER" { - db = db.Where(k, v) - } - } - if err := db.Model(m).Count(&count).Error; err != nil { - return 0, err - } +func (m *Message) Count(db *gorm.DB, userId int64) (res int64, err error) { + err = db.Model(m).Where("receiver_user_id=? OR (sender_user_id=? AND type=4)", userId, userId).Count(&res).Error + return +} - return count, nil +func (m *Message) CountUnread(db *gorm.DB, userId int64) (res int64, err error) { + err = db.Model(m).Where("receiver_user_id=? AND is_read=0", userId).Count(&res).Error + return } diff --git a/internal/dao/jinzhu/messages.go b/internal/dao/jinzhu/messages.go index 00df949f..2fe8df07 100644 --- a/internal/dao/jinzhu/messages.go +++ b/internal/dao/jinzhu/messages.go @@ -30,10 +30,7 @@ func (s *messageSrv) CreateMessage(msg *ms.Message) (*ms.Message, error) { } func (s *messageSrv) GetUnreadCount(userID int64) (int64, error) { - return (&dbr.Message{}).Count(s.db, &dbr.ConditionsT{ - "receiver_user_id": userID, - "is_read": dbr.MsgStatusUnread, - }) + return (&dbr.Message{}).CountUnread(s.db, userID) } func (s *messageSrv) GetMessageByID(id int64) (*ms.Message, error) { @@ -49,21 +46,19 @@ func (s *messageSrv) ReadMessage(message *ms.Message) error { return message.Update(s.db) } -func (s *messageSrv) GetMessages(conditions *ms.ConditionsT, offset, limit int) ([]*ms.MessageFormated, error) { - messages, err := (&dbr.Message{}).List(s.db, conditions, offset, limit) +func (s *messageSrv) GetMessages(userId int64, offset, limit int) ([]*ms.MessageFormated, error) { + messages, err := (&dbr.Message{}).List(s.db, userId, offset, limit) if err != nil { return nil, err } - mfs := []*dbr.MessageFormated{} for _, message := range messages { mf := message.Format() mfs = append(mfs, mf) } - return mfs, nil } -func (s *messageSrv) GetMessageCount(conditions *ms.ConditionsT) (int64, error) { - return (&dbr.Message{}).Count(s.db, conditions) +func (s *messageSrv) GetMessageCount(userId int64) (int64, error) { + return (&dbr.Message{}).Count(s.db, userId) } diff --git a/internal/dao/storage/minio.go b/internal/dao/storage/minio.go index 3859a5b7..028e331a 100644 --- a/internal/dao/storage/minio.go +++ b/internal/dao/storage/minio.go @@ -140,6 +140,8 @@ func (s *minioServant) DeleteObjects(objectKeys []string) (err error) { Key: objectKey, } } + // 记得一定要close,否则会被卡死,退出不了函数,造成资源泄露!!! + close(objectsCh) // 宽松处理所有错误,只记录最后一次发生的错误 for result := range resCh { diff --git a/internal/events/events.go b/internal/events/events.go new file mode 100644 index 00000000..01b9c15c --- /dev/null +++ b/internal/events/events.go @@ -0,0 +1,26 @@ +// Copyright 2023 ROC. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package events + +import ( + "sync" + + "github.com/alimy/tryst/cfg" + "github.com/sirupsen/logrus" +) + +var ( + _onceInitial sync.Once +) + +func Initial() { + _onceInitial.Do(func() { + initEventManager() + if cfg.If("JobManager") { + initJobManager() + logrus.Debugln("initial JobManager") + } + }) +} diff --git a/internal/events/jobs.go b/internal/events/jobs.go new file mode 100644 index 00000000..f3cc984a --- /dev/null +++ b/internal/events/jobs.go @@ -0,0 +1,110 @@ +// Copyright 2023 ROC. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package events + +import ( + "github.com/robfig/cron/v3" +) + +var ( + _defaultJobManager JobManager = (*jobManager)(nil) +) + +type ( + EntryID = cron.EntryID +) + +// JobFn job help function that implement cron.Job interface +type JobFn func() + +func (fn JobFn) Run() { + fn() +} + +// Job job interface +type Job interface { + cron.Schedule + cron.Job +} + +type simpleJob struct { + cron.Schedule + cron.Job +} + +// JobManager job manger interface +type JobManager interface { + Start() + Stop() + Remove(id EntryID) + Schedule(Job) EntryID +} + +type jobManager struct { + m *cron.Cron +} + +func (j *jobManager) Start() { + j.m.Start() +} + +func (j *jobManager) Stop() { + j.m.Stop() +} + +// Remove an entry from being run in the future. +func (j *jobManager) Remove(id EntryID) { + j.m.Remove(id) +} + +// Schedule adds a Job to the Cron to be run on the given schedule. +// The job is wrapped with the configured Chain. +func (j *jobManager) Schedule(job Job) EntryID { + return j.m.Schedule(job, job) +} + +func initJobManager() { + _defaultJobManager = &jobManager{ + m: cron.New(), + } + StartJobManager() +} + +func StartJobManager() { + _defaultJobManager.Start() +} + +func StopJobManager() { + _defaultJobManager.Stop() +} + +// NewJob create new Job instance +func NewJob(s cron.Schedule, fn JobFn) Job { + return &simpleJob{ + Schedule: s, + Job: fn, + } +} + +// RemoveJob an entry from being run in the future. +func RemoveJob(id EntryID) { + _defaultJobManager.Remove(id) +} + +// ScheduleJob adds a Job to the Cron to be run on the given schedule. +// The job is wrapped with the configured Chain. +func ScheduleJob(job Job) EntryID { + return _defaultJobManager.Schedule(job) +} + +// Schedule adds a Job to the Cron to be run on the given schedule. +// The job is wrapped with the configured Chain. +func Schedule(s cron.Schedule, fn JobFn) EntryID { + job := &simpleJob{ + Schedule: s, + Job: fn, + } + return _defaultJobManager.Schedule(job) +} diff --git a/internal/events/pool.go b/internal/events/pool.go new file mode 100644 index 00000000..f5170591 --- /dev/null +++ b/internal/events/pool.go @@ -0,0 +1,55 @@ +// Copyright 2023 ROC. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package events + +import ( + "github.com/alimy/tryst/event" + "github.com/alimy/tryst/pool" + "github.com/rocboss/paopao-ce/internal/conf" + "github.com/sirupsen/logrus" +) + +var ( + _defaultEventManager event.EventManager +) + +func initEventManager() { + var opts []pool.Option + s := conf.EventManagerSetting + if s.MinWorker > 5 { + opts = append(opts, pool.MinWorkerOpt(s.MinWorker)) + } else { + opts = append(opts, pool.MinWorkerOpt(5)) + } + if s.MaxEventBuf > 10 { + opts = append(opts, pool.MaxRequestBufOpt(s.MaxEventBuf)) + } else { + opts = append(opts, pool.MaxRequestBufOpt(10)) + } + if s.MaxTempEventBuf > 10 { + opts = append(opts, pool.MaxRequestTempBufOpt(s.MaxTempEventBuf)) + } else { + opts = append(opts, pool.MaxRequestTempBufOpt(10)) + } + opts = append(opts, pool.MaxTickCountOpt(s.MaxTickCount), pool.TickWaitTimeOpt(s.TickWaitTime)) + _defaultEventManager = event.NewEventManager(func(req event.Event, err error) { + if err != nil { + logrus.Errorf("handle event[%s] occurs error: %s", req.Name(), err) + } + }, opts...) +} + +func StartEventManager() { + _defaultEventManager.Start() +} + +func StopEventManager() { + _defaultEventManager.Stop() +} + +// OnEvent push event to gorotine pool then handled automatic. +func OnEvent(event event.Event) { + _defaultEventManager.OnEvent(event) +} diff --git a/internal/internal.go b/internal/internal.go index 926c9f75..7bdbb94f 100644 --- a/internal/internal.go +++ b/internal/internal.go @@ -5,10 +5,13 @@ package internal import ( + "github.com/rocboss/paopao-ce/internal/events" "github.com/rocboss/paopao-ce/internal/migration" ) func Initial() { // migrate database if needed migration.Run() + // event manager system initialize + events.Initial() } diff --git a/internal/model/joint/joint.go b/internal/model/joint/joint.go index fa7e4729..79246ece 100644 --- a/internal/model/joint/joint.go +++ b/internal/model/joint/joint.go @@ -14,3 +14,9 @@ type BasePageInfo struct { func (r *BasePageInfo) SetPageInfo(page int, pageSize int) { r.Page, r.PageSize = page, pageSize } + +type JsonResp struct { + Code int `json:"code"` + Msg string `json:"msg,omitempty"` + Data any `json:"data,omitempty"` +} diff --git a/internal/model/joint/json.go b/internal/model/joint/json.go new file mode 100644 index 00000000..ad71f5de --- /dev/null +++ b/internal/model/joint/json.go @@ -0,0 +1,15 @@ +// Copyright 2023 ROC. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package joint + +import ( + stdJson "encoding/json" + + "github.com/rocboss/paopao-ce/pkg/json" +) + +func RespMarshal(data any) (stdJson.RawMessage, error) { + return json.Marshal(data) +} diff --git a/internal/model/web/core.go b/internal/model/web/core.go index 78c04bb3..4b01e9c8 100644 --- a/internal/model/web/core.go +++ b/internal/model/web/core.go @@ -40,14 +40,6 @@ type UserInfoResp struct { Followings int64 `json:"followings"` } -type GetUnreadMsgCountReq struct { - SimpleInfo `json:"-" binding:"-"` -} - -type GetUnreadMsgCountResp struct { - Count int64 `json:"count"` -} - type GetMessagesReq BasePageReq type GetMessagesResp base.PageResp diff --git a/internal/model/web/relax.go b/internal/model/web/relax.go new file mode 100644 index 00000000..e75ae315 --- /dev/null +++ b/internal/model/web/relax.go @@ -0,0 +1,34 @@ +// Copyright 2023 ROC. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package web + +import ( + "encoding/json" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/rocboss/paopao-ce/internal/model/joint" +) + +type GetUnreadMsgCountReq struct { + SimpleInfo `json:"-" binding:"-"` +} + +type GetUnreadMsgCountResp struct { + Count int64 `json:"count"` + JsonResp json.RawMessage `json:"-"` +} + +func (r *GetUnreadMsgCountResp) Render(c *gin.Context) { + if len(r.JsonResp) != 0 { + c.JSON(http.StatusOK, r.JsonResp) + } else { + c.JSON(http.StatusOK, &joint.JsonResp{ + Code: 0, + Msg: "success", + Data: r, + }) + } +} diff --git a/internal/servants/base/base.go b/internal/servants/base/base.go index e94e75af..afe857c8 100644 --- a/internal/servants/base/base.go +++ b/internal/servants/base/base.go @@ -21,9 +21,10 @@ import ( "github.com/rocboss/paopao-ce/internal/core/ms" "github.com/rocboss/paopao-ce/internal/dao" "github.com/rocboss/paopao-ce/internal/dao/cache" + "github.com/rocboss/paopao-ce/internal/events" + "github.com/rocboss/paopao-ce/internal/model/joint" "github.com/rocboss/paopao-ce/pkg/app" "github.com/rocboss/paopao-ce/pkg/xerror" - "github.com/sirupsen/logrus" ) type BaseServant struct { @@ -39,12 +40,6 @@ type DaoServant struct { Redis core.RedisCache } -type JsonResp struct { - Code int `json:"code"` - Msg string `json:"msg,omitempty"` - Data any `json:"data,omitempty"` -} - type SentryHubSetter interface { SetSentryHub(hub *sentry.Hub) } @@ -145,13 +140,13 @@ func bindAnySentry(c *gin.Context, obj any) mir.Error { func RenderAny(c *gin.Context, data any, err mir.Error) { if err == nil { - c.JSON(http.StatusOK, &JsonResp{ + c.JSON(http.StatusOK, &joint.JsonResp{ Code: 0, Msg: "success", Data: data, }) } else { - c.JSON(xerror.HttpStatusCode(err), &JsonResp{ + c.JSON(xerror.HttpStatusCode(err), &joint.JsonResp{ Code: err.StatusCode(), Msg: err.Error(), }) @@ -164,13 +159,13 @@ func (s *BaseServant) Bind(c *gin.Context, obj any) mir.Error { func (s *BaseServant) Render(c *gin.Context, data any, err mir.Error) { if err == nil { - c.JSON(http.StatusOK, &JsonResp{ + c.JSON(http.StatusOK, &joint.JsonResp{ Code: 0, Msg: "success", Data: data, }) } else { - c.JSON(xerror.HttpStatusCode(err), &JsonResp{ + c.JSON(xerror.HttpStatusCode(err), &joint.JsonResp{ Code: err.StatusCode(), Msg: err.Error(), }) @@ -203,9 +198,16 @@ func (s *DaoServant) GetTweetBy(id int64) (*ms.PostFormated, error) { return postFormated, nil } -func (s *DaoServant) PushPostsToSearch(c context.Context) { - if err := s.Redis.SetPushToSearchJob(c); err == nil { - defer s.Redis.DelPushToSearchJob(c) +func (s *DaoServant) PushAllPostToSearch() { + events.OnEvent(&pushAllPostToSearchEvent{ + fn: s.pushAllPostToSearch, + }) +} + +func (s *DaoServant) pushAllPostToSearch() error { + ctx := context.Background() + if err := s.Redis.SetPushToSearchJob(ctx); err == nil { + defer s.Redis.DelPushToSearchJob(ctx) splitNum := 1000 conditions := ms.ConditionsT{ @@ -234,11 +236,19 @@ func (s *DaoServant) PushPostsToSearch(c context.Context) { } } } else { - logrus.Errorf("redis: set JOB_PUSH_TO_SEARCH error: %s", err) + return fmt.Errorf("redis: set JOB_PUSH_TO_SEARCH error: %w", err) } + return nil } func (s *DaoServant) PushPostToSearch(post *ms.Post) { + events.OnEvent(&pushPostToSearchEvent{ + fn: s.pushPostToSearch, + post: post, + }) +} + +func (s *DaoServant) pushPostToSearch(post *ms.Post) { postFormated := post.Format() postFormated.User = &ms.UserFormated{ ID: post.UserID, @@ -247,14 +257,12 @@ func (s *DaoServant) PushPostToSearch(post *ms.Post) { for _, content := range contents { postFormated.Contents = append(postFormated.Contents, content.Format()) } - contentFormated := "" for _, content := range postFormated.Contents { if content.Type == ms.ContentTypeText || content.Type == ms.ContentTypeTitle { contentFormated = contentFormated + content.Content + "\n" } } - docs := []core.TsDocItem{{ Post: post, Content: contentFormated, diff --git a/internal/servants/base/events.go b/internal/servants/base/events.go new file mode 100644 index 00000000..c5e7d08e --- /dev/null +++ b/internal/servants/base/events.go @@ -0,0 +1,38 @@ +// Copyright 2023 ROC. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package base + +import ( + "github.com/alimy/tryst/event" + "github.com/rocboss/paopao-ce/internal/core/ms" +) + +type pushPostToSearchEvent struct { + event.UnimplementedEvent + fn func(*ms.Post) + post *ms.Post +} + +type pushAllPostToSearchEvent struct { + event.UnimplementedEvent + fn func() error +} + +func (p *pushPostToSearchEvent) Name() string { + return "servants.base.pushPostToSearchEvent" +} + +func (p *pushPostToSearchEvent) Action() (err error) { + p.fn(p.post) + return +} + +func (p *pushAllPostToSearchEvent) Name() string { + return "servants.base.pushAllPostToSearchEvent" +} + +func (p *pushAllPostToSearchEvent) Action() error { + return p.fn() +} diff --git a/internal/servants/chain/jwt.go b/internal/servants/chain/jwt.go index ae96d550..04ccd58e 100644 --- a/internal/servants/chain/jwt.go +++ b/internal/servants/chain/jwt.go @@ -5,10 +5,11 @@ package chain import ( + "errors" "strings" "github.com/gin-gonic/gin" - "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v5" "github.com/rocboss/paopao-ce/internal/conf" "github.com/rocboss/paopao-ce/pkg/app" "github.com/rocboss/paopao-ce/pkg/xerror" @@ -51,10 +52,53 @@ func JWT() gin.HandlerFunc { ecode = xerror.UnauthorizedAuthNotExist } } else { - switch err.(*jwt.ValidationError).Errors { - case jwt.ValidationErrorExpired: + if errors.Is(err, jwt.ErrTokenExpired) { ecode = xerror.UnauthorizedTokenTimeout - default: + } else { + ecode = xerror.UnauthorizedTokenError + } + } + } else { + ecode = xerror.InvalidParams + } + if ecode != xerror.Success { + response := app.NewResponse(c) + response.ToErrorResponse(ecode) + c.Abort() + return + } + c.Next() + } +} + +func JwtSurely() gin.HandlerFunc { + return func(c *gin.Context) { + var ( + token string + ecode = xerror.Success + ) + if s, exist := c.GetQuery("token"); exist { + token = s + } else { + token = c.GetHeader("Authorization") + // 验证前端传过来的token格式,不为空,开头为Bearer + if token == "" || !strings.HasPrefix(token, "Bearer ") { + response := app.NewResponse(c) + response.ToErrorResponse(xerror.UnauthorizedTokenError) + c.Abort() + return + } + // 验证通过,提取有效部分(除去Bearer) + token = token[7:] + } + if token != "" { + if claims, err := app.ParseToken(token); err == nil { + c.Set("UID", claims.UID) + c.Set("USERNAME", claims.Username) + } else { + if errors.Is(err, jwt.ErrTokenExpired) { + ecode = xerror.UnauthorizedTokenTimeout + } else { ecode = xerror.UnauthorizedTokenError } } diff --git a/internal/servants/web/core.go b/internal/servants/web/core.go index de092a60..b0c42019 100644 --- a/internal/servants/web/core.go +++ b/internal/servants/web/core.go @@ -37,6 +37,7 @@ type coreSrv struct { *base.DaoServant oss core.ObjectStorageService + wc core.WebCache } func (s *coreSrv) Chain() gin.HandlersChain { @@ -45,7 +46,7 @@ func (s *coreSrv) Chain() gin.HandlersChain { func (s *coreSrv) SyncSearchIndex(req *web.SyncSearchIndexReq) mir.Error { if req.User != nil && req.User.IsAdmin { - go s.PushPostsToSearch(context.Background()) + s.PushAllPostToSearch() } else { logrus.Warnf("sync search index need admin permision user: %#v", req.User) } @@ -82,29 +83,20 @@ func (s *coreSrv) GetUserInfo(req *web.UserInfoReq) (*web.UserInfoResp, mir.Erro return resp, nil } -func (s *coreSrv) GetUnreadMsgCount(req *web.GetUnreadMsgCountReq) (*web.GetUnreadMsgCountResp, mir.Error) { - count, err := s.Ds.GetUnreadCount(req.Uid) - if err != nil { - return nil, xerror.ServerError - } - return &web.GetUnreadMsgCountResp{ - Count: count, - }, nil -} - func (s *coreSrv) GetMessages(req *web.GetMessagesReq) (*web.GetMessagesResp, mir.Error) { - conditions := &ms.ConditionsT{ - "receiver_user_id": req.UserId, - "ORDER": "id DESC", - } - messages, err := s.Ds.GetMessages(conditions, (req.Page-1)*req.PageSize, req.PageSize) + messages, err := s.Ds.GetMessages(req.UserId, (req.Page-1)*req.PageSize, req.PageSize) for _, mf := range messages { + // TODO: 优化处理这里的user获取逻辑以及错误处理 if mf.SenderUserID > 0 { - user, err := s.Ds.GetUserByID(mf.SenderUserID) - if err == nil { + if user, err := s.Ds.GetUserByID(mf.SenderUserID); err == nil { mf.SenderUser = user.Format() } } + if mf.Type == ms.MsgTypeWhisper && mf.ReceiverUserID != req.UserId { + if user, err := s.Ds.GetUserByID(mf.ReceiverUserID); err == nil { + mf.ReceiverUser = user.Format() + } + } // 好友申请消息不需要获取其他信息 if mf.Type == ms.MsgTypeRequestingFriend { continue @@ -132,7 +124,7 @@ func (s *coreSrv) GetMessages(req *web.GetMessagesReq) (*web.GetMessagesResp, mi logrus.Errorf("Ds.GetMessages err: %v\n", err) return nil, web.ErrGetMessagesFailed } - totalRows, _ := s.Ds.GetMessageCount(conditions) + totalRows, _ := s.Ds.GetMessageCount(req.UserId) resp := base.PageRespFrom(messages, req.Page, req.PageSize, totalRows) return (*web.GetMessagesResp)(resp), nil } @@ -149,6 +141,8 @@ func (s *coreSrv) ReadMessage(req *web.ReadMessageReq) mir.Error { logrus.Errorf("Ds.ReadMessage err: %s", err) return web.ErrReadMessageFailed } + // 清除未读消息数缓存,不需要处理错误 + s.wc.DelUnreadMsgCountResp(req.Uid) return nil } @@ -177,6 +171,9 @@ func (s *coreSrv) SendUserWhisper(req *web.SendWhisperReq) mir.Error { return web.ErrSendWhisperFailed } + // 清除接收者未读消息缓存, 不需要处理错误 + s.wc.DelUnreadMsgCountResp(req.UserID) + // 写入当日(自然日)计数缓存 s.Redis.IncrCountWhisper(ctx, req.Uid) @@ -374,9 +371,10 @@ func (s *coreSrv) TweetStarStatus(req *web.TweetStarStatusReq) (*web.TweetStarSt return resp, nil } -func newCoreSrv(s *base.DaoServant, oss core.ObjectStorageService) api.Core { +func newCoreSrv(s *base.DaoServant, oss core.ObjectStorageService, wc core.WebCache) api.Core { return &coreSrv{ DaoServant: s, oss: oss, + wc: wc, } } diff --git a/internal/servants/web/events.go b/internal/servants/web/events.go new file mode 100644 index 00000000..f5b354df --- /dev/null +++ b/internal/servants/web/events.go @@ -0,0 +1,84 @@ +// Copyright 2023 ROC. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package web + +import ( + "encoding/json" + "fmt" + + "github.com/alimy/tryst/event" + "github.com/rocboss/paopao-ce/internal/core" + "github.com/rocboss/paopao-ce/internal/core/ms" + "github.com/rocboss/paopao-ce/internal/events" + "github.com/rocboss/paopao-ce/internal/model/joint" + "github.com/rocboss/paopao-ce/internal/model/web" +) + +type cacheUnreadMsgEvent struct { + event.UnimplementedEvent + ds core.DataService + wc core.WebCache + uid int64 +} + +type createMessageEvent struct { + event.UnimplementedEvent + ds core.DataService + wc core.WebCache + message *ms.Message +} + +func onCacheUnreadMsgEvent(uid int64) { + events.OnEvent(&cacheUnreadMsgEvent{ + ds: _ds, + wc: _wc, + uid: uid, + }) +} + +func onCreateMessageEvent(data *ms.Message) { + events.OnEvent(&createMessageEvent{ + ds: _ds, + wc: _wc, + message: data, + }) +} + +func (e *cacheUnreadMsgEvent) Name() string { + return "cacheUnreadMsgEvent" +} + +func (e *cacheUnreadMsgEvent) Action() error { + count, err := e.ds.GetUnreadCount(e.uid) + if err != nil { + return fmt.Errorf("cacheUnreadMsgEvent action occurs error: %w", err) + } + resp := &joint.JsonResp{ + Code: 0, + Msg: "success", + Data: &web.GetUnreadMsgCountResp{ + Count: count, + }, + } + data, err := json.Marshal(resp) + if err != nil { + return fmt.Errorf("cacheUnreadMsgEvent action marshal resp occurs error: %w", err) + } + if err = e.wc.PutUnreadMsgCountResp(e.uid, data); err != nil { + return fmt.Errorf("cacheUnreadMsgEvent action put resp data to redis cache occurs error: %w", err) + } + return nil +} + +func (e *createMessageEvent) Name() string { + return "createMessageEvent" +} + +func (e *createMessageEvent) Action() (err error) { + if _, err = e.ds.CreateMessage(e.message); err == nil { + err = e.wc.DelUnreadMsgCountResp(e.message.ReceiverUserID) + } + return +} diff --git a/internal/servants/web/priv.go b/internal/servants/web/priv.go index 590f7722..efb681b4 100644 --- a/internal/servants/web/priv.go +++ b/internal/servants/web/priv.go @@ -286,8 +286,7 @@ func (s *privSrv) CreateTweet(req *web.CreateTweetReq) (_ *web.CreateTweetResp, } // 创建消息提醒 - // TODO: 优化消息提醒处理机制 - go s.Ds.CreateMessage(&ms.Message{ + onCreateMessageEvent(&ms.Message{ SenderUserID: req.User.ID, ReceiverUserID: user.ID, Type: ms.MsgTypePost, @@ -390,7 +389,7 @@ func (s *privSrv) CreateCommentReply(req *web.CreateCommentReplyReq) (*web.Creat // 创建用户消息提醒 commentMaster, err := s.Ds.GetUserByID(comment.UserID) if err == nil && commentMaster.ID != req.Uid { - go s.Ds.CreateMessage(&ms.Message{ + onCreateMessageEvent(&ms.Message{ SenderUserID: req.Uid, ReceiverUserID: commentMaster.ID, Type: ms.MsgTypeReply, @@ -402,7 +401,7 @@ func (s *privSrv) CreateCommentReply(req *web.CreateCommentReplyReq) (*web.Creat } postMaster, err := s.Ds.GetUserByID(post.UserID) if err == nil && postMaster.ID != req.Uid && commentMaster.ID != postMaster.ID { - go s.Ds.CreateMessage(&ms.Message{ + onCreateMessageEvent(&ms.Message{ SenderUserID: req.Uid, ReceiverUserID: postMaster.ID, Type: ms.MsgTypeReply, @@ -416,7 +415,7 @@ func (s *privSrv) CreateCommentReply(req *web.CreateCommentReplyReq) (*web.Creat user, err := s.Ds.GetUserByID(atUserID) if err == nil && user.ID != req.Uid && commentMaster.ID != user.ID && postMaster.ID != user.ID { // 创建消息提醒 - go s.Ds.CreateMessage(&ms.Message{ + onCreateMessageEvent(&ms.Message{ SenderUserID: req.Uid, ReceiverUserID: user.ID, Type: ms.MsgTypeReply, @@ -522,7 +521,7 @@ func (s *privSrv) CreateComment(req *web.CreateCommentReq) (_ *web.CreateComment // 创建用户消息提醒 postMaster, err := s.Ds.GetUserByID(post.UserID) if err == nil && postMaster.ID != req.Uid { - go s.Ds.CreateMessage(&ms.Message{ + onCreateMessageEvent(&ms.Message{ SenderUserID: req.Uid, ReceiverUserID: postMaster.ID, Type: ms.MsgtypeComment, @@ -538,7 +537,7 @@ func (s *privSrv) CreateComment(req *web.CreateCommentReq) (_ *web.CreateComment } // 创建消息提醒 - go s.Ds.CreateMessage(&ms.Message{ + onCreateMessageEvent(&ms.Message{ SenderUserID: req.Uid, ReceiverUserID: user.ID, Type: ms.MsgtypeComment, diff --git a/internal/servants/web/relax.go b/internal/servants/web/relax.go new file mode 100644 index 00000000..7b6dd71e --- /dev/null +++ b/internal/servants/web/relax.go @@ -0,0 +1,52 @@ +// Copyright 2023 ROC. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package web + +import ( + "github.com/alimy/mir/v4" + "github.com/gin-gonic/gin" + "github.com/redis/rueidis" + api "github.com/rocboss/paopao-ce/auto/api/v1" + "github.com/rocboss/paopao-ce/internal/core" + "github.com/rocboss/paopao-ce/internal/model/web" + "github.com/rocboss/paopao-ce/internal/servants/base" + "github.com/rocboss/paopao-ce/internal/servants/chain" + "github.com/sirupsen/logrus" +) + +var ( + _ api.Relax = (*relaxSrv)(nil) +) + +type relaxSrv struct { + api.UnimplementedRelaxServant + *base.DaoServant + wc core.WebCache +} + +func (s *relaxSrv) Chain() gin.HandlersChain { + return gin.HandlersChain{chain.JwtSurely()} +} + +func (s *relaxSrv) GetUnreadMsgCount(req *web.GetUnreadMsgCountReq) (*web.GetUnreadMsgCountResp, mir.Error) { + if data, xerr := s.wc.GetUnreadMsgCountResp(req.Uid); xerr == nil && len(data) > 0 { + // logrus.Debugln("GetUnreadMsgCount get resp from cache") + return &web.GetUnreadMsgCountResp{ + JsonResp: data, + }, nil + } else if !rueidis.IsRedisNil(xerr) { + logrus.Warnf("GetUnreadMsgCount from cache occurs error: %s", xerr) + } + // 使用缓存机制特殊处理 + onCacheUnreadMsgEvent(req.Uid) + return &web.GetUnreadMsgCountResp{}, nil +} + +func newRelaxSrv(s *base.DaoServant, wc core.WebCache) api.Relax { + return &relaxSrv{ + DaoServant: s, + wc: wc, + } +} diff --git a/internal/servants/web/web.go b/internal/servants/web/web.go index 6b4b9836..87d13d4b 100644 --- a/internal/servants/web/web.go +++ b/internal/servants/web/web.go @@ -11,27 +11,32 @@ import ( "github.com/gin-gonic/gin" api "github.com/rocboss/paopao-ce/auto/api/v1" "github.com/rocboss/paopao-ce/internal/conf" + "github.com/rocboss/paopao-ce/internal/core" "github.com/rocboss/paopao-ce/internal/dao" + "github.com/rocboss/paopao-ce/internal/dao/cache" "github.com/rocboss/paopao-ce/internal/servants/base" ) var ( _enablePhoneVerify bool _disallowUserRegister bool + _ds core.DataService + _wc core.WebCache + _oss core.ObjectStorageService _onceInitial sync.Once ) // RouteWeb register web route func RouteWeb(e *gin.Engine) { lazyInitial() - oss := dao.ObjectStorageService() ds := base.NewDaoServant() // aways register servants api.RegisterAdminServant(e, newAdminSrv(ds)) - api.RegisterCoreServant(e, newCoreSrv(ds, oss)) + api.RegisterCoreServant(e, newCoreSrv(ds, _oss, _wc)) + api.RegisterRelaxServant(e, newRelaxSrv(ds, _wc)) api.RegisterLooseServant(e, newLooseSrv(ds)) - api.RegisterPrivServant(e, newPrivSrv(ds, oss)) - api.RegisterPubServant(e, newPubSrv(ds, oss)) + api.RegisterPrivServant(e, newPrivSrv(ds, _oss)) + api.RegisterPubServant(e, newPubSrv(ds, _oss)) api.RegisterKeyQueryServant(e, NewShareKeyServant(ds)) api.RegisterRankServant(e, NewRankServant(ds)) api.RegisterFollowshipServant(e, newFollowshipSrv(ds)) @@ -49,5 +54,8 @@ func lazyInitial() { _onceInitial.Do(func() { _enablePhoneVerify = cfg.If("Sms") _disallowUserRegister = cfg.If("Web:DisallowUserRegister") + _oss = dao.ObjectStorageService() + _ds = dao.DataService() + _wc = cache.NewWebCache() }) } diff --git a/mirc/web/v1/core.go b/mirc/web/v1/core.go index f12c7018..0ec4a68c 100644 --- a/mirc/web/v1/core.go +++ b/mirc/web/v1/core.go @@ -21,9 +21,6 @@ type Core struct { // GetUserInfo 获取当前用户信息 GetUserInfo func(Get, web.UserInfoReq) web.UserInfoResp `mir:"/user/info"` - // GetUnreadMsgCount 获取当前用户未读消息数量 - GetUnreadMsgCount func(Get, web.GetUnreadMsgCountReq) web.GetUnreadMsgCountResp `mir:"/user/msgcount/unread"` - // GetMessages 获取消息列表 GetMessages func(Get, web.GetMessagesReq) web.GetMessagesResp `mir:"/user/messages"` diff --git a/mirc/web/v1/relax.go b/mirc/web/v1/relax.go new file mode 100644 index 00000000..af4bbe83 --- /dev/null +++ b/mirc/web/v1/relax.go @@ -0,0 +1,20 @@ +package v1 + +import ( + . "github.com/alimy/mir/v4" + . "github.com/alimy/mir/v4/engine" + "github.com/rocboss/paopao-ce/internal/model/web" +) + +func init() { + Entry[Relax]() +} + +// Relax 放宽授权的服务 +type Relax struct { + Chain `mir:"-"` + Group `mir:"v1"` + + // GetUnreadMsgCount 获取当前用户未读消息数量 + GetUnreadMsgCount func(Get, web.GetUnreadMsgCountReq) web.GetUnreadMsgCountResp `mir:"/user/msgcount/unread"` +} diff --git a/pkg/app/jwt.go b/pkg/app/jwt.go index a861faa2..28c9f439 100644 --- a/pkg/app/jwt.go +++ b/pkg/app/jwt.go @@ -7,7 +7,7 @@ package app import ( "time" - "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v5" "github.com/rocboss/paopao-ce/internal/conf" "github.com/rocboss/paopao-ce/internal/core/ms" ) @@ -38,18 +38,15 @@ func GenerateToken(User *ms.User) (string, error) { return token, err } -func ParseToken(token string) (*Claims, error) { - tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (any, error) { +func ParseToken(token string) (res *Claims, err error) { + var tokenClaims *jwt.Token + tokenClaims, err = jwt.ParseWithClaims(token, &Claims{}, func(_ *jwt.Token) (any, error) { return GetJWTSecret(), nil }) - if err != nil { - return nil, err + if err == nil && tokenClaims != nil && tokenClaims.Valid { + res, _ = tokenClaims.Claims.(*Claims) + } else { + err = jwt.ErrTokenNotValidYet } - if tokenClaims != nil { - if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid { - return claims, nil - } - } - - return nil, err + return } diff --git a/pkg/http/client.go b/pkg/http/client.go index 6af18377..d77c1d14 100644 --- a/pkg/http/client.go +++ b/pkg/http/client.go @@ -8,7 +8,7 @@ import ( "net/http" "time" - "github.com/sirupsen/logrus" + gp "github.com/alimy/tryst/pool" ) var ( @@ -16,13 +16,13 @@ var ( ) const ( - _minRequestInCh = 10 - _minRequestInTmpCh = 10 + _minRequestBuf = 10 + _minRequestTempBuf = 10 _minWorker = 5 ) // ResponseFn a function used handle the response of http.Client.Do -type ResponseFn = func(req *http.Request, resp *http.Response, err error) +type ResponseFn = gp.ResponseFn[*http.Request, *http.Response] // AsyncClient asynchronous client interface type AsyncClient interface { @@ -31,90 +31,44 @@ type AsyncClient interface { // AsyncClientConf client configure used to create an AsynClient instance type AsyncClientConf struct { - MinWorker int - MaxRequestInCh int - MaxRequestInTempCh int - MaxTickCount int - TickWaitTime time.Duration -} - -type requestItem struct { - request *http.Request - fn ResponseFn + MinWorker int + MaxRequestBuf int + MaxRequestTempBuf int + MaxTickCount int + TickWaitTime time.Duration } type wormClient struct { - client *http.Client - requestCh chan *requestItem // 正式工 缓存通道 - requestTempCh chan *requestItem // 临时工 缓存通道 - maxTickCount int - tickWaitTime time.Duration + pool gp.GoroutinePool[*http.Request, *http.Response] } func (s *wormClient) Do(req *http.Request, fn ResponseFn) { - item := &requestItem{req, fn} - select { - case s.requestCh <- item: - // send request item by requestCh chan - default: - select { - case s.requestTempCh <- item: - // send request item by requestTempCh chan" - default: - go func() { - s.do(item) - // watch requestTempCh to continue do work if needed. - // cancel loop if no item had watched in s.maxCyle * s.maxWaitTime. - for count := 0; count < s.maxTickCount; count++ { - select { - case item := <-s.requestTempCh: - // reset count to continue do work - count = 0 - s.do(item) - default: - // sleeping to wait request item pass over to do work - time.Sleep(s.tickWaitTime) - } - } - }() - } - } -} - -func (s *wormClient) starDotWork() { - for item := range s.requestCh { - s.do(item) - } -} - -func (s *wormClient) do(req *requestItem) { - resp, err := s.client.Do(req.request) - req.fn(req.request, resp, err) + s.pool.Do(req, fn) } // NewAsyncClient create an AsyncClient instance func NewAsyncClient(client *http.Client, conf *AsyncClientConf) AsyncClient { - maxRequestInCh := _minRequestInCh - maxRequestInTempCh := _minRequestInTmpCh - if conf.MaxRequestInCh > _minRequestInCh { - maxRequestInCh = conf.MaxRequestInCh - } - if conf.MaxRequestInTempCh > _minRequestInTmpCh { - maxRequestInTempCh = conf.MaxRequestInTempCh + minWorker := _minWorker + maxRequestBuf := _minRequestBuf + maxRequestTempBuf := _minRequestTempBuf + if conf.MaxRequestBuf > _minRequestBuf { + maxRequestBuf = conf.MaxRequestBuf } - wc := &wormClient{ - client: client, - requestCh: make(chan *requestItem, maxRequestInCh), - requestTempCh: make(chan *requestItem, maxRequestInTempCh), + if conf.MaxRequestTempBuf > _minRequestTempBuf { + maxRequestTempBuf = conf.MaxRequestTempBuf } - numWorker := conf.MinWorker - if numWorker < _minWorker { - numWorker = _minWorker + if conf.MinWorker > _minWorker { + minWorker = conf.MinWorker } - logrus.Debugf("use %d backend worker to do the http request", numWorker) - // 启动 do work 正式工 - for ; numWorker > 0; numWorker-- { - go wc.starDotWork() + return &wormClient{ + pool: gp.NewGoroutinePool(func(req *http.Request) (*http.Response, error) { + return client.Do(req) + }, + gp.MinWorkerOpt(minWorker), + gp.MaxRequestBufOpt(maxRequestBuf), + gp.MaxRequestTempBufOpt(maxRequestTempBuf), + gp.MaxTickCountOpt(conf.MaxTickCount), + gp.TickWaitTimeOpt(conf.TickWaitTime), + ), } - return wc } diff --git a/pkg/obx/obx.go b/pkg/obx/obx.go index 7ae8277e..0a6b2c0f 100644 --- a/pkg/obx/obx.go +++ b/pkg/obx/obx.go @@ -75,6 +75,7 @@ func NewClient(conf *Config, acc *hx.AsyncClientConf, fn hx.ResponseFn) OpenObse user: conf.User, password: conf.Password, userAgent: userAgent, + respFn: fn, client: hx.NewAsyncClient(http.DefaultClient, acc), } } diff --git a/pkg/utils/str.go b/pkg/utils/str.go index a69f02ec..199ad092 100644 --- a/pkg/utils/str.go +++ b/pkg/utils/str.go @@ -7,6 +7,7 @@ package utils import ( "math/rand" "time" + "unsafe" ) type StrType int @@ -41,3 +42,10 @@ func RandStr(size int, kind StrType) []byte { } return result } + +func String(data []byte) string { + if size := len(data); size > 0 { + return unsafe.String(unsafe.SliceData(data), size) + } + return "" +} diff --git a/run.sh b/run.sh new file mode 100755 index 00000000..bf4ee85f --- /dev/null +++ b/run.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# eg.1 : sh run.sh +# eg.2, push all release: sh run.sh push +# eg.3, push all release with dev branch: sh run.sh push dev + +function push { + if [ -n "$1" ]; then + echo "git push origin $1:$1" + git push origin $1:$1 + + echo "git push alimy $1:$1" + git push alimy $1:$1 + + echo "git push bitbus $1:$1" + git push bitbus $1:$1 + else + push_all dev r/paopao-ce r/paopao-ce-plus r/paopao-ce-pro r/paopao-ce-xtra + fi +} + +function push_all { + if [ $# -eq 0 ]; then + push + else + while [ $# -gt 0 ]; do + push $1 + shift + done + fi +} + +case $1 in +"push") + shift + push_all $@ + ;; +"merge") + echo "merge command" + ;; +*) + push_all + ;; +esac \ No newline at end of file diff --git a/scripts/migration/postgres/0007_content_type_alter.up.sql b/scripts/migration/postgres/0007_content_type_alter.up.sql index 4df0470d..2315fa6c 100644 --- a/scripts/migration/postgres/0007_content_type_alter.up.sql +++ b/scripts/migration/postgres/0007_content_type_alter.up.sql @@ -1,3 +1,3 @@ -ALTER TABLE p_post_content ALTER COLUMN content SET DATA TYPE TEXT NOT NULL DEFAULT ''; -ALTER TABLE p_comment_content ALTER COLUMN content SET DATA TYPE TEXT NOT NULL DEFAULT ''; -ALTER TABLE p_comment_reply ALTER COLUMN content SET DATA TYPE TEXT NOT NULL DEFAULT ''; +ALTER TABLE p_post_content ALTER COLUMN content SET DATA TYPE TEXT, ALTER COLUMN content SET NOT NULL, ALTER COLUMN content SET DEFAULT ''; +ALTER TABLE p_comment_content ALTER COLUMN content SET DATA TYPE TEXT, ALTER COLUMN content SET NOT NULL, ALTER COLUMN content SET DEFAULT ''; +ALTER TABLE p_comment_reply ALTER COLUMN content SET DATA TYPE TEXT, ALTER COLUMN content SET NOT NULL, ALTER COLUMN content SET DEFAULT ''; diff --git a/web/.env b/web/.env index f9905592..e35b377d 100644 --- a/web/.env +++ b/web/.env @@ -1,8 +1,12 @@ VITE_HOST="" +# 功能特性开启 +VITE_USE_FRIENDSHIP=true + # 模块开启 VITE_ENABLE_ANOUNCEMENT=false VITE_ENABLE_WALLET=false +VITE_ENABLE_FRIENDS_BAR=true # 功能开启 VITE_ALLOW_TWEET_ATTACHMENT=true diff --git a/web/dist/assets/404-276daf23.js b/web/dist/assets/404-276daf23.js deleted file mode 100644 index a2689dc1..00000000 --- a/web/dist/assets/404-276daf23.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as s}from"./main-nav.vue_vue_type_style_index_0_lang-fa3b58e7.js";import{u as a}from"./vue-router-edf90322.js";import{F as i,e as c,a2 as u}from"./naive-ui-702193c2.js";import{d as l,c as d,V as t,a2 as o,o as f,e as x}from"./@vue-7e1ab0af.js";import{_ as g}from"./index-b4b0f710.js";import"./vuex-f1ee712f.js";import"./vooks-e23078ea.js";import"./evtd-b614532e.js";import"./@vicons-b98681e0.js";import"./seemly-76b7b838.js";import"./vueuc-2fc92f18.js";import"./@css-render-16be7445.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";import"./axios-4a70c6fc.js";/* empty css */const v=l({__name:"404",setup(h){const e=a(),_=()=>{e.push({path:"/"})};return(k,w)=>{const n=s,p=c,r=u,m=i;return f(),d("div",null,[t(n,{title:"404"}),t(m,{class:"main-content-wrap wrap404",bordered:""},{default:o(()=>[t(r,{status:"404",title:"404 资源不存在",description:"再看看其他的吧"},{footer:o(()=>[t(p,{onClick:_},{default:o(()=>[x("回主页")]),_:1})]),_:1})]),_:1})])}}});const M=g(v,[["__scopeId","data-v-e62daa85"]]);export{M as default}; diff --git a/web/dist/assets/404-3d8d6c85.js b/web/dist/assets/404-3d8d6c85.js new file mode 100644 index 00000000..c1ba3c82 --- /dev/null +++ b/web/dist/assets/404-3d8d6c85.js @@ -0,0 +1 @@ +import{_ as s}from"./main-nav.vue_vue_type_style_index_0_lang-5497f713.js";import{u as a}from"./vue-router-e5a2430e.js";import{F as i,e as c,a2 as u}from"./naive-ui-d8de3dda.js";import{d as l,f as d,k as t,w as o,e as f,A as x}from"./@vue-a481fc63.js";import{_ as g}from"./index-d9671de1.js";import"./vuex-44de225f.js";import"./vooks-6d99783e.js";import"./evtd-b614532e.js";import"./@vicons-7a4ef312.js";import"./seemly-76b7b838.js";import"./vueuc-39372edb.js";import"./@css-render-7124a1a5.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";import"./axios-4a70c6fc.js";/* empty css */const v=l({__name:"404",setup(h){const e=a(),_=()=>{e.push({path:"/"})};return(k,w)=>{const n=s,p=c,r=u,m=i;return f(),d("div",null,[t(n,{title:"404"}),t(m,{class:"main-content-wrap wrap404",bordered:""},{default:o(()=>[t(r,{status:"404",title:"404 资源不存在",description:"再看看其他的吧"},{footer:o(()=>[t(p,{onClick:_},{default:o(()=>[x("回主页")]),_:1})]),_:1})]),_:1})])}}});const M=g(v,[["__scopeId","data-v-e62daa85"]]);export{M as default}; diff --git a/web/dist/assets/@css-render-16be7445.js b/web/dist/assets/@css-render-7124a1a5.js similarity index 96% rename from web/dist/assets/@css-render-16be7445.js rename to web/dist/assets/@css-render-7124a1a5.js index 56289c3d..60115763 100644 --- a/web/dist/assets/@css-render-16be7445.js +++ b/web/dist/assets/@css-render-7124a1a5.js @@ -1,3 +1,3 @@ -import{i as d}from"./@vue-7e1ab0af.js";function C(i){let r=".",s="__",m="--",f;if(i){let e=i.blockPrefix;e&&(r=e),e=i.elementPrefix,e&&(s=e),e=i.modifierPrefix,e&&(m=e)}const b={install(e){f=e.c;const l=e.context;l.bem={},l.bem.b=null,l.bem.els=null}};function y(e){let l,n;return{before(t){l=t.bem.b,n=t.bem.els,t.bem.els=null},after(t){t.bem.b=l,t.bem.els=n},$({context:t,props:u}){return e=typeof e=="string"?e:e({context:t,props:u}),t.bem.b=e,`${(u==null?void 0:u.bPrefix)||r}${t.bem.b}`}}}function v(e){let l;return{before(n){l=n.bem.els},after(n){n.bem.els=l},$({context:n,props:t}){return e=typeof e=="string"?e:e({context:n,props:t}),n.bem.els=e.split(",").map(u=>u.trim()),n.bem.els.map(u=>`${(t==null?void 0:t.bPrefix)||r}${n.bem.b}${s}${u}`).join(", ")}}}function P(e){return{$({context:l,props:n}){e=typeof e=="string"?e:e({context:l,props:n});const t=e.split(",").map(o=>o.trim());function u(o){return t.map(x=>`&${(n==null?void 0:n.bPrefix)||r}${l.bem.b}${o!==void 0?`${s}${o}`:""}${m}${x}`).join(", ")}const c=l.bem.els;return c!==null?u(c[0]):u()}}}function _(e){return{$({context:l,props:n}){e=typeof e=="string"?e:e({context:l,props:n});const t=l.bem.els;return`&:not(${(n==null?void 0:n.bPrefix)||r}${l.bem.b}${t!==null&&t.length>0?`${s}${t[0]}`:""}${m}${e})`}}}return Object.assign(b,{cB:(...e)=>f(y(e[0]),e[1],e[2]),cE:(...e)=>f(v(e[0]),e[1],e[2]),cM:(...e)=>f(P(e[0]),e[1],e[2]),cNotM:(...e)=>f(_(e[0]),e[1],e[2])}),b}const $=Symbol("@css-render/vue3-ssr");function M(i,r){return``}function S(i,r){const s=d($,null);if(s===null){console.error("[css-render/vue3-ssr]: no ssr context found.");return}const{styles:m,ids:f}=s;f.has(i)||m!==null&&(f.add(i),m.push(M(i,r)))}const j=typeof document<"u";function N(){if(j)return;const i=d($,null);if(i!==null)return{adapter:S,context:i}}export{C as p,N as u}; diff --git a/web/dist/assets/@opentiny-0f942bd4.css b/web/dist/assets/@opentiny-0f942bd4.css new file mode 100644 index 00000000..d40f7ac8 --- /dev/null +++ b/web/dist/assets/@opentiny-0f942bd4.css @@ -0,0 +1 @@ +.tiny-icon-success{fill:#5cb300}.tiny-icon-error{fill:#f23030}.tiny-icon-warning-triangle{fill:#f80}.tiny-icon-prompt{fill:#1476ff}.tiny-icon-text-type{fill:#9185f0}[class*=tiny-]{-webkit-box-sizing:border-box;box-sizing:border-box}[class*=tiny-] :after,[class*=tiny-] :before{-webkit-box-sizing:border-box;box-sizing:border-box}[class*=tiny-] a{cursor:pointer;background-image:none;text-decoration:none;outline:0}[class*=tiny-] a:active,[class*=tiny-] a:focus,[class*=tiny-] a:hover{outline:0;text-decoration:none}[class*=tiny-] dd,[class*=tiny-] dl,[class*=tiny-] dt,[class*=tiny-] li,[class*=tiny-] ol,[class*=tiny-] td,[class*=tiny-] th,[class*=tiny-] ul{margin:0;padding:0}[class*=tiny-] ol,[class*=tiny-] ul{list-style:none}[class*=tiny-] audio,[class*=tiny-] canvas,[class*=tiny-] video{display:inline-block}[class*=tiny-] audio:not([controls]){display:none;height:0}[class*=tiny-] mark{background:#ff0;color:#000}[class*=tiny-] pre{white-space:pre-wrap}[class*=tiny-] sub,[class*=tiny-] sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}[class*=tiny-] sup{top:-.5em}[class*=tiny-] sub{bottom:-.25em}[class*=tiny-] fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}[class*=tiny-] legend{border:0;padding:0}[class*=tiny-] input::-ms-clear,[class*=tiny-] input::-ms-reveal{display:none}[class*=tiny-] button::-moz-focus-inner,[class*=tiny-] input::-moz-focus-inner{border:0;padding:0}[class*=tiny-] textarea{overflow:auto;vertical-align:top}[class*=tiny-] table{border-collapse:collapse;border-spacing:0}[class*=tiny-] .tiny-hide{display:none}[class*=tiny-] .popper__arrow,[class*=tiny-] .popper__arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}@media (min-width:768px){[class*=tiny-] ::-webkit-scrollbar{width:var(--ti-common-scrollbar-width);height:var(--ti-common-scrollbar-height)}[class*=tiny-] ::-webkit-scrollbar-track-piece{background:var(--ti-common-scrollbar-track-piece-bg-color)}[class*=tiny-] ::-webkit-scrollbar-thumb{background:var(--ti-common-scrollbar-thumb-bg-color);border-radius:var(--ti-common-scrollbar-thumb-border-radius)}[class*=tiny-] ::-webkit-scrollbar-thumb:hover{background:var(--ti-common-scrollbar-thumb-hover-bg-color)}[class*=tiny-] ::-webkit-scrollbar-thumb:active{background:var(--ti-common-scrollbar-thumb-active-bg-color)}[class*=tiny-] .tiny-scrollbar::-webkit-scrollbar{width:8px;height:8px}[class*=tiny-] .tiny-scrollbar::-webkit-scrollbar-track-piece{background:0 0;border:0}[class*=tiny-] .tiny-scrollbar::-webkit-scrollbar-thumb{background:#bfbfbf;border-radius:4px}[class*=tiny-] .tiny-scrollbar::-webkit-scrollbar-thumb:hover{background:#999}[class*=tiny-] .tiny-scrollbar::-webkit-scrollbar-thumb:active{background:#999}[class*=tiny-] .tiny-min-scrollbar::-webkit-scrollbar{width:4px;height:4px}[class*=tiny-] .tiny-min-scrollbar::-webkit-scrollbar-track-piece{background:0 0;border:0}[class*=tiny-] .tiny-min-scrollbar::-webkit-scrollbar-thumb{background:#bfbfbf;border-radius:2px}[class*=tiny-] .tiny-min-scrollbar::-webkit-scrollbar-thumb:hover{background:#999}[class*=tiny-] .tiny-min-scrollbar::-webkit-scrollbar-thumb:active{background:#999}}:root{--ti-base-color-white:#fff;--ti-base-color-transparent:transparent;--ti-base-color-brand-6:#5e7ce0;--ti-base-color-brand-8:#344899;--ti-base-color-brand-7:#526ecc;--ti-base-color-brand-5:#7693f5;--ti-base-color-brand-4:#96adfa;--ti-base-color-brand-3:#beccfa;--ti-base-color-brand-2:#e9edfa;--ti-base-color-brand-1:#f2f5fc;--ti-base-color-common-9:#181818;--ti-base-color-common-8:#282b33;--ti-base-color-common-7:#252b3a;--ti-base-color-common-6:#464c59;--ti-base-color-common-5:#575d6c;--ti-base-color-common-4:#5c6173;--ti-base-color-common-3:#8a8e99;--ti-base-color-common-2:#adb0b8;--ti-base-color-common-1:#dfe1e6;--ti-base-color-bg-9:#b12220;--ti-base-color-bg-8:#c7000b;--ti-base-color-bg-7:#d64a52;--ti-base-color-bg-6:#eef0f5;--ti-base-color-bg-5:#f5f5f6;--ti-base-color-bg-4:#fafafa;--ti-base-color-bg-3:#ffffff;--ti-base-color-bg-2:#ffffff;--ti-base-color-bg-1:#ffffff;--ti-base-color-error-4:#de504e;--ti-base-color-error-3:#f66f6a;--ti-base-color-error-2:#ffbcba;--ti-base-color-error-1:#ffeeed;--ti-base-color-success-4:#3ac295;--ti-base-color-success-3:#50d4ab;--ti-base-color-success-2:#acf2dc;--ti-base-color-success-1:#edfff9;--ti-base-color-warn-5:#e37d29;--ti-base-color-warn-4:#fa9841;--ti-base-color-warn-3:#fac20a;--ti-base-color-warn-2:#ffd0a6;--ti-base-color-warn-1:#fff3e8;--ti-base-color-prompt-4:var(--ti-base-color-brand-7);--ti-base-color-prompt-3:var(--ti-base-color-brand-6);--ti-base-color-prompt-2:var(--ti-base-color-brand-3);--ti-base-color-prompt-1:#ebf6ff;--ti-base-color-prompt-icon-from:#7769e8;--ti-base-color-prompt-icon-to:#58bbff;--ti-base-color-icon-info:#6cbfff;--ti-base-color-data-3:#a6dd82;--ti-base-color-data-4:#f3689a;--ti-base-color-data-5:#a97af8;--ti-common-color-transparent:var(--ti-base-color-transparent);--ti-common-color-light:#fff;--ti-common-color-dark:#000;--ti-common-color-success:var(--ti-base-color-success-3);--ti-common-color-text-success:var(--ti-base-color-success-4);--ti-common-color-success-bg:var(--ti-base-color-success-1);--ti-common-color-success-border:var(--ti-base-color-success-2);--ti-common-color-error:var(--ti-base-color-error-3);--ti-common-color-error-text:var(--ti-base-color-error-4);--ti-common-color-error-bg:var(--ti-base-color-error-1);--ti-common-color-error-border:var(--ti-base-color-error-3);--ti-common-color-error-border-secondary:var(--ti-base-color-error-2);--ti-common-color-info:var(--ti-base-color-common-7);--ti-common-color-info-text:var(--ti-base-color-common-7);--ti-common-color-info-bg:rgba(51, 51, 51, .06);--ti-common-color-info-border:#d3d4d6;--ti-common-color-warn:var(--ti-base-color-warn-4);--ti-common-color-warn-text:var(--ti-base-color-warn-5);--ti-common-color-warn-bg:var(--ti-base-color-warn-1);--ti-common-color-warn-border:var(--ti-base-color-warn-2);--ti-common-color-warn-secondary:var(--ti-base-color-warn-3);--ti-common-color-prompt:var(--ti-base-color-prompt-3);--ti-common-color-prompt-text:var(--ti-base-color-prompt-4);--ti-common-color-prompt-bg:var(--ti-base-color-prompt-1);--ti-common-color-prompt-border:var(--ti-base-color-prompt-2);--ti-common-color-prompt-icon-from:var(--ti-base-color-prompt-icon-from);--ti-common-color-prompt-icon-to:var(--ti-base-color-prompt-icon-to);--ti-common-color-primary-normal:var(--ti-base-color-brand-6);--ti-common-color-primary-hover:var(--ti-base-color-brand-5);--ti-common-color-primary-active:var(--ti-base-color-brand-5);--ti-common-color-primary-disabled:#a0cfff;--ti-common-color-primary-disabled-bgcolor:var(--ti-common-color-bg-disabled);--ti-common-color-primary-disabled-border:var(--ti-common-color-line-disabled);--ti-common-color-primary-disabled-text:var(--ti-common-color-text-disabled);--ti-common-color-primary-plain-disabled-bg-color:rgba(191, 191, 191, .1);--ti-common-color-success-normal:var(--ti-common-color-success);--ti-common-color-success-hover:var(--ti-common-color-success-border);--ti-common-color-success-active:var(--ti-common-color-success-border);--ti-common-color-success-disabled:#a6c3b9;--ti-common-color-success-disabled-bgcolor:var(--ti-common-color-bg-disabled);--ti-common-color-success-disabled-border:var(--ti-common-color-line-disabled);--ti-common-color-success-disabled-text:var(--ti-common-color-text-disabled);--ti-common-color-success-plain-disabled-bg-color:rgba(166, 195, 185, .1);--ti-common-color-warning-normal:var(--ti-common-color-warn);--ti-common-color-warning-hover:var(--ti-common-color-warn-secondary);--ti-common-color-warning-active:var(--ti-common-color-warn-secondary);--ti-common-color-warning-disabled:#d3c6a2;--ti-common-color-warning-disabled-bgcolor:var(--ti-common-color-bg-disabled);--ti-common-color-warning-disabled-border:var(--ti-common-color-line-disabled);--ti-common-color-warning-disabled-text:var(--ti-common-color-text-disabled);--ti-common-color-warning-plain-disabled-bg-color:rgba(211, 198, 162, .1);--ti-common-color-danger-normal:var(--ti-common-bg-primary);--ti-common-color-danger-hover:var(--ti-common-bg-primary-hover);--ti-common-color-danger-active:var(--ti-common-bg-primary-active);--ti-common-color-danger-disabled:#d8bab5;--ti-common-color-danger-disabled-bgcolor:var(--ti-common-color-bg-disabled);--ti-common-color-danger-disabled-border:var(--ti-common-color-line-disabled);--ti-common-color-danger-disabled-text:var(--ti-common-color-text-disabled);--ti-common-color-danger-plain-disabled-bg-color:rgba(216, 186, 181, .1);--ti-common-color-info-normal:var(--ti-base-color-common-7);--ti-common-color-info-hover:var(--ti-base-color-common-4);--ti-common-color-info-active:var(--ti-base-color-common-4);--ti-common-color-info-disabled:#bfbfbf;--ti-common-color-info-disabled-bgcolor:var(--ti-common-color-bg-disabled);--ti-common-color-info-disabled-border:var(--ti-common-color-line-disabled);--ti-common-color-info-disabled-text:var(--ti-common-color-text-disabled);--ti-common-color-info-plain-disabled-bg-color:rgba(191, 191, 191, .1);--ti-common-color-text-primary:var(--ti-base-color-common-7);--ti-common-color-text-secondary:var(--ti-base-color-common-5);--ti-common-color-text-weaken:var(--ti-base-color-common-3);--ti-common-color-text-disabled:var(--ti-base-color-common-2);--ti-common-color-text-darkbg:var(--ti-base-color-common-2);--ti-common-color-text-darkbg-disabled:var(--ti-base-color-common-5);--ti-common-color-text-link:var(--ti-base-color-brand-7);--ti-common-color-text-link-hover:var(--ti-base-color-brand-8);--ti-common-color-text-link-darkbg:var(--ti-base-color-brand-4);--ti-common-color-text-link-darkbg-hover:var(--ti-base-color-brand-3);--ti-common-color-text-highlight:var(--ti-base-color-brand-7);--ti-common-color-text-white:var(--ti-base-color-white);--ti-common-color-text-gray:var(--ti-base-color-white);--ti-common-color-text-gray-disabled:var(--ti-base-color-common-4);--ti-common-color-text-important:var(--ti-base-color-error-4);--ti-common-color-placeholder:var(--ti-base-color-common-2);--ti-common-color-selected-text-color:var(--ti-common-color-light);--ti-common-color-icon-normal:var(--ti-base-color-common-5);--ti-common-color-icon-hover:var(--ti-base-color-brand-6);--ti-common-color-icon-active:var(--ti-base-color-brand-6);--ti-common-color-icon-disabled:var(--ti-base-color-common-2);--ti-common-color-icon-white:var(--ti-base-color-white);--ti-common-color-icon-graybg-normal:var(--ti-base-color-common-2);--ti-common-color-icon-graybg-hover:var(--ti-base-color-brand-6);--ti-common-color-icon-graybg-active:var(--ti-base-color-brand-6);--ti-common-color-icon-graybg-disabled:var(--ti-base-color-common-1);--ti-common-color-icon-darkbg-normal:var(--ti-base-color-common-2);--ti-common-color-icon-darkbg-hover:var(--ti-base-color-brand-5);--ti-common-color-icon-darkbg-active:var(--ti-base-color-brand-5);--ti-common-color-icon-darkbg-disabled:var(--ti-base-color-common-5);--ti-common-color-icon-info:var(--ti-base-color-icon-info);--ti-common-color-bg-normal:var(--ti-base-color-bg-6);--ti-common-color-bg-emphasize:var(--ti-base-color-brand-6);--ti-common-color-bg-disabled:var(--ti-base-color-bg-5);--ti-common-color-bg-hover:var(--ti-base-color-brand-8);--ti-common-color-bg-gray:var(--ti-base-color-bg-4);--ti-common-color-bg-secondary:var(--ti-base-color-common-2);--ti-common-bg-primary:var(--ti-base-color-bg-8);--ti-common-bg-primary-hover:var(--ti-base-color-bg-7);--ti-common-bg-primary-active:var(--ti-base-color-bg-9);--ti-common-bg-minor:var(--ti-base-color-bg-2);--ti-common-bg-minor-hover:var(--ti-base-color-bg-1);--ti-common-bg-minor-active:var(--ti-base-color-bg-3);--ti-common-color-bg-white-normal:var(--ti-base-color-white);--ti-common-color-bg-white-emphasize:var(--ti-base-color-brand-1);--ti-common-color-bg-light-normal:var(--ti-base-color-brand-2);--ti-common-color-bg-light-emphasize:var(--ti-base-color-brand-3);--ti-common-color-bg-dark-normal:var(--ti-base-color-common-6);--ti-common-color-bg-dark-emphasize:var(--ti-base-color-common-4);--ti-common-color-bg-dark-active:var(--ti-common-color-bg-normal);--ti-common-color-bg-dark-deep:var(--ti-base-color-common-6);--ti-common-color-bg-dark-disabled:var(--ti-base-color-common-1);--ti-common-color-bg-navigation:var(--ti-base-color-common-8);--ti-common-color-bg-dark-select:var(--ti-base-color-common-9);--ti-common-color-selected-background:var(--ti-base-color-brand-6);--ti-common-color-hover-background:var(--ti-base-color-brand-1);--ti-common-color-data-1:var(--ti-base-color-success-3);--ti-common-color-data-2:var(--ti-base-color-icon-info);--ti-common-color-data-3:var(--ti-base-color-data-3);--ti-common-color-data-4:var(--ti-base-color-data-4);--ti-common-color-data-5:var(--ti-base-color-data-5);--ti-common-color-data-6:var(--ti-base-color-warn-3);--ti-common-color-data-7:var(--ti-base-color-warn-4);--ti-common-color-data-8:var(--ti-base-color-error-3);--ti-common-line-height-number:1.5;--ti-common-line-height-base:12px;--ti-common-line-height-1:14px;--ti-common-line-height-2:16px;--ti-common-line-height-3:18px;--ti-common-line-height-4:20px;--ti-common-line-height-5:24px;--ti-common-line-height-6:32px;--ti-common-line-height-7:36px;--ti-common-space-base:4px;--ti-common-space-2x:calc(var(--ti-common-space-base) * 2);--ti-common-space-3x:calc(var(--ti-common-space-base) * 3);--ti-common-space-4x:calc(var(--ti-common-space-base) * 4);--ti-common-space-5x:calc(var(--ti-common-space-base) * 5);--ti-common-space-6x:calc(var(--ti-common-space-base) * 6);--ti-common-space-8x:calc(var(--ti-common-space-base) * 8);--ti-common-space-10x:calc(var(--ti-common-space-base) * 10);--ti-common-space-0:0px;--ti-common-space-1:1px;--ti-common-space-6:6px;--ti-common-space-10:10px;--ti-common-dropdown-gap:2px;--ti-common-shadow-none:none;--ti-common-shadow-1-up:0 -1px 4px 0 rgba(0, 0, 0, .1);--ti-common-shadow-1-down:0 1px 4px 0 rgba(0, 0, 0, .1);--ti-common-shadow-1-left:-1px 0px 4px 0 rgba(0, 0, 0, .1);--ti-common-shadow-1-right:1px 0px 4px 0 rgba(0, 0, 0, .1);--ti-common-shadow-2-up:0 -2px 8px 0 rgba(0, 0, 0, .2);--ti-common-shadow-2-down:0 2px 8px 0 rgba(0, 0, 0, .2);--ti-common-shadow-2-left:-2px 0 8px 0 rgba(238, 10, 10, .2);--ti-common-shadow-2-right:2px 0 8px 0 rgba(252, 5, 5, .2);--ti-common-shadow-3-up:0 -4px 16px 0 rgba(0, 0, 0, .2);--ti-common-shadow-3-down:0 4px 16px 0 rgba(0, 0, 0, .2);--ti-common-shadow-3-left:-4px 0 16px 0 rgba(0, 0, 0, .2);--ti-common-shadow-3-right:4px 0 16px 0 rgba(0, 0, 0, .2);--ti-common-shadow-4-up:0 -8px 40px 0 rgba(0, 0, 0, .2);--ti-common-shadow-4-down:0 8px 40px 0 rgba(0, 0, 0, .2);--ti-common-shadow-4-left:-8px 0 40px 0 rgba(0, 0, 0, .2);--ti-common-shadow-4-right:8px 0 40px 0 rgba(0, 0, 0, .2);--ti-common-shadow-error:0 1px 3px 0 rgba(199, 54, 54, .25);--ti-common-shadow-warn:0 1px 3px 0 rgba(204, 100, 20, .25);--ti-common-shadow-prompt:0 1px 3px 0 rgba(70, 94, 184, .25);--ti-common-shadow-success:0 1px 3px 0 rgba(39, 176, 128, .25);--ti-common-font-family:"Helvetica","Arial","PingFangSC-Regular","Hiragino Sans GB","Microsoft YaHei","微软雅黑","Microsoft JhengHei";--ti-common-font-size-base:12px;--ti-common-font-size-1:14px;--ti-common-font-size-2:16px;--ti-common-font-size-3:18px;--ti-common-font-size-4:20px;--ti-common-font-size-5:24px;--ti-common-font-size-6:32px;--ti-common-font-size-7:36px;--ti-common-font-weight-1:100;--ti-common-font-weight-2:200;--ti-common-font-weight-3:300;--ti-common-font-weight-4:normal;--ti-common-font-weight-5:500;--ti-common-font-weight-6:600;--ti-common-font-weight-7:bold;--ti-common-font-weight-8:800;--ti-common-font-weight-9:900;--ti-common-font-weight-bold:700;--ti-common-color-line-normal:var(--ti-base-color-common-2);--ti-common-color-line-hover:var(--ti-base-color-common-5);--ti-common-color-line-active:var(--ti-base-color-brand-6);--ti-common-color-line-disabled:var(--ti-base-color-common-1);--ti-common-color-line-dividing:var(--ti-base-color-common-1);--ti-common-color-dash-line-normal:var(--ti-base-color-common-5);--ti-common-color-dash-line-hover:var(--ti-base-color-brand-7);--ti-common-color-border:var(--ti-base-color-common-2);--ti-common-color-border-hover:var(--ti-base-color-common-5);--ti-common-border-weight-normal:1px;--ti-common-border-weight-1:2px;--ti-common-border-weight-2:3px;--ti-common-border-style-dashed:dashed;--ti-common-border-style-dotted:dotted;--ti-common-border-style-solid:solid;--ti-common-border-radius-normal:2px;--ti-common-border-radius-0:0px;--ti-common-border-radius-1:4px;--ti-common-border-radius-2:8px;--ti-common-border-radius-3:50%;--ti-common-size-base:4px;--ti-common-size-2x:calc(var(--ti-common-size-base) * 2);--ti-common-size-3x:calc(var(--ti-common-size-base) * 3);--ti-common-size-4x:calc(var(--ti-common-size-base) * 4);--ti-common-size-5x:calc(var(--ti-common-size-base) * 5);--ti-common-size-6x:calc(var(--ti-common-size-base) * 6);--ti-common-size-7x:calc(var(--ti-common-size-base) * 7);--ti-common-size-8x:calc(var(--ti-common-size-base) * 8);--ti-common-size-9x:calc(var(--ti-common-size-base) * 9);--ti-common-size-10x:calc(var(--ti-common-size-base) * 10);--ti-common-size-11x:calc(var(--ti-common-size-base) * 11);--ti-common-size-12x:calc(var(--ti-common-size-base) * 12);--ti-common-size-13x:calc(var(--ti-common-size-base) * 13);--ti-common-size-14x:calc(var(--ti-common-size-base) * 14);--ti-common-size-15x:calc(var(--ti-common-size-base) * 15);--ti-common-size-16x:calc(var(--ti-common-size-base) * 16);--ti-common-size-17x:calc(var(--ti-common-size-base) * 17);--ti-common-size-18x:calc(var(--ti-common-size-base) * 18);--ti-common-size-19x:calc(var(--ti-common-size-base) * 19);--ti-common-size-20x:calc(var(--ti-common-size-base) * 20);--ti-common-size-21x:calc(var(--ti-common-size-base) * 21);--ti-common-size-22x:calc(var(--ti-common-size-base) * 22);--ti-common-size-23x:calc(var(--ti-common-size-base) * 23);--ti-common-size-24x:calc(var(--ti-common-size-base) * 24);--ti-common-size-25x:calc(var(--ti-common-size-base) * 25);--ti-common-size-26x:calc(var(--ti-common-size-base) * 26);--ti-common-size-27x:calc(var(--ti-common-size-base) * 27);--ti-common-size-28x:calc(var(--ti-common-size-base) * 28);--ti-common-size-29x:calc(var(--ti-common-size-base) * 29);--ti-common-size-30x:calc(var(--ti-common-size-base) * 30);--ti-common-size-31x:calc(var(--ti-common-size-base) * 31);--ti-common-size-32x:calc(var(--ti-common-size-base) * 32);--ti-common-size-33x:calc(var(--ti-common-size-base) * 33);--ti-common-size-34x:calc(var(--ti-common-size-base) * 34);--ti-common-size-35x:calc(var(--ti-common-size-base) * 35);--ti-common-size-36x:calc(var(--ti-common-size-base) * 36);--ti-common-size-37x:calc(var(--ti-common-size-base) * 37);--ti-common-size-38x:calc(var(--ti-common-size-base) * 38);--ti-common-size-39x:calc(var(--ti-common-size-base) * 39);--ti-common-size-40x:calc(var(--ti-common-size-base) * 40);--ti-common-size-41x:calc(var(--ti-common-size-base) * 41);--ti-common-size-42x:calc(var(--ti-common-size-base) * 42);--ti-common-size-43x:calc(var(--ti-common-size-base) * 43);--ti-common-size-44x:calc(var(--ti-common-size-base) * 44);--ti-common-size-45x:calc(var(--ti-common-size-base) * 45);--ti-common-size-46x:calc(var(--ti-common-size-base) * 46);--ti-common-size-47x:calc(var(--ti-common-size-base) * 47);--ti-common-size-48x:calc(var(--ti-common-size-base) * 48);--ti-common-size-49x:calc(var(--ti-common-size-base) * 49);--ti-common-size-50x:calc(var(--ti-common-size-base) * 50);--ti-common-size-0:0px;--ti-common-size-auto:auto;--ti-common-size-width-large:var(--ti-common-size-33x);--ti-common-size-width-medium:var(--ti-common-size-30x);--ti-common-size-width-normal:var(--ti-common-size-20x);--ti-common-size-height-large:var(--ti-common-size-12x);--ti-common-size-height-medium:var(--ti-common-size-10x);--ti-common-size-height-small:var(--ti-common-size-8x);--ti-common-size-height-normal:var(--ti-common-size-7x);--ti-common-size-height-mini:var(--ti-common-size-6x);--ti-common-scrollbar-width:4px;--ti-common-scrollbar-height:4px;--ti-common-scrollbar-track-piece-bg-color:var(--ti-base-color-bg-4);--ti-common-scrollbar-thumb-bg-color:#bfbfbf;--ti-common-scrollbar-thumb-border-radius:6px;--ti-common-scrollbar-thumb-hover-bg-color:#999999;--ti-common-scrollbar-thumb-active-bg-color:#999999}:root{--ti-errortips-box-bg-color:var(--ti-common-color-light);--ti-errortips-body-text-color:#5a5e66;--ti-errortips-body-font-size:var(--ti-common-font-size-1);--ti-errortips-body-code-font-size:100px;--ti-errortips-body-code-text-color:#9ac7ef;--ti-errortips-body-content-font-size:var(--ti-common-font-size-2);--ti-errortips-body-bottom-font-weight:var(--ti-common-font-weight-8);--ti-errortips-sso-box-bg-color:var(--ti-common-color-light);--ti-errortips-sso-body-text-color:#5a5e66;--ti-errortips-sso-body-font-size:var(--ti-common-font-size-1);--ti-errortips-not-sso-bg-color:#dcdfe4;--ti-errortips-not-sso-body-bg-color:#f4f5f9;--ti-errortips-not-sso-body-border-color:#d4d5d7;--ti-errortips-not-sso-body-title-border-color:#b6babf;--ti-errortips-not-sso-body-title-font-size:var(--ti-common-font-size-4);--ti-errortips-not-sso-body-login-font-size:var(--ti-common-font-size-2);--ti-errortips-not-sso-body-text-color:#5a5e66;--ti-errortips-not-sso-body-input-border-color:var(--ti-base-color-bg-5);--ti-errortips-not-sso-body-input-border-radius:var(--ti-common-border-radius-normal);--ti-errortips-not-sso-body-placeholder-text-color:var(--ti-common-color-placeholder);--ti-errortips-not-sso-body-input-hover-text-color:var(--ti-common-color-placeholder);--ti-errortips-not-sso-body-input-focus-text-color:var(--ti-common-color-border);--ti-errortips-not-sso-body-input-danger-border-color:var(--ti-base-color-bg-8);--ti-errortips-not-sso-body-button-text-color:var(--ti-common-color-light);--ti-errortips-not-sso-body-button-bg-color:var(--ti-base-color-brand-6);--ti-errortips-not-sso-body-button-border-radius:var(--ti-common-border-radius-normal);--ti-errortips-not-sso-body-button-hover-bg-color:var(--ti-base-color-brand-5);--ti-errortips-not-sso-body-errmessage-text-color:#f00}.tiny-popup__wrapper{z-index:2147483647!important;background:rgba(0,0,0,.5);position:fixed;top:0;right:0;bottom:0;left:0;overflow:auto;margin:0}.tiny-popup__wrapper .tiny-errortips__box{position:absolute;width:var(--ti-errortips-width);min-height:var(--ti-errortips-min-height);max-height:var(--ti-errortips-max-height);top:0;left:0;right:0;bottom:0;margin:auto;overflow:hidden;background:var(--ti-errortips-box-bg-color);border:1px solid transparent;-webkit-box-shadow:2px 2px 2px 0 rgba(0,0,0,.2);box-shadow:2px 2px 2px #0003;text-align:center;overflow-y:auto}.tiny-popup__wrapper .tiny-errortips__box .tiny-errortips__body{height:100%;text-align:initial;padding:20px;color:var(--ti-errortips-body-text-color);font-size:var(--ti-errortips-body-font-size);display:table;margin:auto}.tiny-popup__wrapper .tiny-errortips__box .tiny-errortips__body .errortips{text-align:center;display:table-cell;vertical-align:middle}.tiny-popup__wrapper .tiny-errortips__box .tiny-errortips__body .errortips .error-code{font-size:var(--ti-errortips-body-code-font-size);color:var(--ti-errortips-body-code-text-color);margin:0 auto -45px;text-shadow:0 2px 0 #fff,-2px 0 0 #fff,2px 0 0 #fff}.tiny-popup__wrapper .tiny-errortips__box .tiny-errortips__body .errortips .error-img{width:260px;height:180px;margin:0 auto;background:url(/assets/errortips-bg-e8a5d5c7.png) no-repeat}.tiny-popup__wrapper .tiny-errortips__box .tiny-errortips__body .errortips .error-content{font-size:var(--ti-errortips-body-content-font-size);margin:24px 0;font-weight:700}.tiny-popup__wrapper .tiny-errortips__box .tiny-errortips__body .errortips .error-bottom a{font-weight:var(--ti-errortips-body-bottom-font-weight);cursor:pointer}.tiny-popup__wrapper .tiny-errortips__box .tiny-errortips__body .errortips .error-bottom span{padding-right:15px}.tiny-popup__wrapper .tiny-sso__box{position:absolute;background:var(--ti-errortips-sso-box-bg-color);border:1px solid transparent;-webkit-box-shadow:2px 2px 2px 0 rgba(0,0,0,.2);box-shadow:2px 2px 2px #0003;left:50%;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.tiny-popup__wrapper .tiny-sso__box .tiny-sso__body{text-align:initial;padding:20px;color:var(--ti-errortips-sso-body-text-color);line-height:32px;font-size:var(--ti-errortips-sso-body-font-size)}.tiny-popup__wrapper .tiny-sso__box .tiny-sso__body .tiny-sso__body-iframe{width:350px;height:350px;overflow:hidden}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){.tiny-popup__wrapper .tiny-sso__box .tiny-sso__body .tiny-sso__body-iframe{height:460px}}@supports (-ms-ime-align:auto){.tiny-popup__wrapper .tiny-sso__box .tiny-sso__body .tiny-sso__body-iframe{height:460px}}.tiny-popup__wrapper.login-not-sso{background:var(--ti-errortips-not-sso-bg-color);background-size:cover}.tiny-popup__wrapper.login-not-sso .tiny-not-sso__box{width:100%;height:100%;overflow:hidden}.tiny-popup__wrapper.login-not-sso .tiny-not-sso__box .tiny-not-sso__body{width:650px;height:400px;background:var(--ti-errortips-not-sso-body-bg-color);position:absolute;left:50%;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);text-align:center;border:1px solid var(--ti-errortips-not-sso-body-border-color);-webkit-box-shadow:0 2px 4px #989a9e;box-shadow:0 2px 4px #989a9e}.tiny-popup__wrapper.login-not-sso .tiny-not-sso__box .tiny-not-sso__body .title{background:-webkit-gradient(linear,left top,left bottom,from(#ecedf1),to(#dadde2));background:linear-gradient(to bottom,#ecedf1,#dadde2);border-bottom:1px solid var(--ti-errortips-not-sso-body-title-border-color);padding:16px 20px;font-size:var(--ti-errortips-not-sso-body-title-font-size)}.tiny-popup__wrapper.login-not-sso .tiny-not-sso__box .tiny-not-sso__body .tbl-login{width:100%;border-collapse:collapse;border-spacing:0;font-size:var(--ti-errortips-not-sso-body-login-font-size);margin-top:28px}.tiny-popup__wrapper.login-not-sso .tiny-not-sso__box .tiny-not-sso__body .tbl-login .form-item{height:60px;line-height:60px}.tiny-popup__wrapper.login-not-sso .tiny-not-sso__box .tiny-not-sso__body .tbl-login .form-item td.label{width:30%;text-align:right;color:var(--ti-errortips-not-sso-body-text-color)}.tiny-popup__wrapper.login-not-sso .tiny-not-sso__box .tiny-not-sso__body .tbl-login .form-item td.cell{width:70%;text-align:left;padding-left:12px}.tiny-popup__wrapper.login-not-sso .tiny-not-sso__box .tiny-not-sso__body .tbl-login .form-item td.cell input{border:1px solid var(--ti-errortips-not-sso-body-input-border-color);border-radius:var(--ti-errortips-not-sso-body-input-border-radius);outline:0;width:75%;height:40px;line-height:40px;padding:0 8px;background:0 0;color:var(--ti-errortips-not-sso-body-text-color)}.tiny-popup__wrapper.login-not-sso .tiny-not-sso__box .tiny-not-sso__body .tbl-login .form-item td.cell input::-webkit-input-placeholder{color:var(--ti-errortips-not-sso-body-placeholder-text-color)}.tiny-popup__wrapper.login-not-sso .tiny-not-sso__box .tiny-not-sso__body .tbl-login .form-item td.cell input:hover{border-color:var(--ti-errortips-not-sso-body-placeholder-text-color)}.tiny-popup__wrapper.login-not-sso .tiny-not-sso__box .tiny-not-sso__body .tbl-login .form-item td.cell input:focus::-webkit-input-placeholder{color:var(--ti-errortips-not-sso-body-input-focus-text-color)}.tiny-popup__wrapper.login-not-sso .tiny-not-sso__box .tiny-not-sso__body .tbl-login .form-item td.cell input.text-danger{border-color:var(--ti-errortips-not-sso-body-input-danger-border-color)}.tiny-popup__wrapper.login-not-sso .tiny-not-sso__box .tiny-not-sso__body .tbl-login .form-item td.cell button{width:75%;height:40px;line-height:40px;padding:0 24px;text-align:center;color:var(--ti-errortips-not-sso-body-button-text-color);background-color:var(--ti-errortips-not-sso-body-button-bg-color);border:none;border-radius:var(--ti-errortips-not-sso-body-button-border-radius);-webkit-transition:.3s;transition:.3s;outline:0}.tiny-popup__wrapper.login-not-sso .tiny-not-sso__box .tiny-not-sso__body .tbl-login .form-item td.cell button:hover{background-color:var(--ti-errortips-not-sso-body-button-hover-bg-color)}.tiny-popup__wrapper.login-not-sso .tiny-not-sso__box .tiny-not-sso__body .tbl-login .form-item td.cell .errmessage{color:var(--ti-errortips-not-sso-body-errmessage-text-color);line-height:20px}.tiny-svg{width:1em;height:1em;vertical-align:middle;overflow:hidden;display:inline-block}.tiny-slide-bar{--ti-slider-progress-box-border-color:var(--ti-common-color-light);--ti-slider-progress-box-hover-border-color:rgba(153, 153, 153, .7);--ti-slider-progress-box-arrow-normal-text-color:#f2f2f2;--ti-slider-progress-box-arrow-hover-text-color:#808080;--ti-slider-progress-box-middleline-border-color:#ebebeb;--ti-slider-progress-box-middleline-icon-color:#ebebeb;padding:0 32px;position:relative}.tiny-slide-bar>.tiny-svg{position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);font-size:2em;cursor:pointer;fill:var(--ti-slider-progress-box-arrow-normal-text-color)}.tiny-slide-bar>.tiny-svg:hover{fill:var(--ti-slider-progress-box-arrow-hover-text-color)}.tiny-slide-bar>.tiny-svg.tiny-disabled,.tiny-slide-bar>.tiny-svg.tiny-disabled:hover{background:0 0;fill:#fff;cursor:default}.tiny-slide-bar>.icon-chevron-left{left:0}.tiny-slide-bar>.icon-chevron-right{right:0}.tiny-slide-bar li li div{margin:15px 0;font-size:var(--ti-common-font-size-base);color:#4e5e67}.tiny-slide-bar li li div:nth-child(2){border-bottom:1px solid var(--ti-slider-progress-box-middleline-border-color)}.tiny-slide-bar li li div svg{float:right;margin:-6px 0 0;background:#fff;fill:var(--ti-slider-progress-box-middleline-icon-color)}.tiny-slide-bar .tiny-slide-bar__content{width:100%;min-height:170px;position:relative;overflow:hidden}.tiny-slide-bar .tiny-slide-bar__list{position:absolute;min-height:170px;display:-webkit-box;display:-ms-flexbox;display:flex}.tiny-slide-bar .tiny-slide-bar__list>li{width:23%;padding:20px;float:left;margin-left:2%;position:relative;border:5px solid var(--ti-slider-progress-box-border-color);-webkit-box-sizing:border-box;box-sizing:border-box;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.tiny-slide-bar .tiny-slide-bar__list>li:first-child{margin-left:0}.tiny-slide-bar .tiny-slide-bar__list>li:hover{border-color:var(--ti-slider-progress-box-hover-border-color)}.tiny-slide-bar .tiny-slide-bar__list>li>.icon-chevron-down{position:absolute;top:98.8%;left:50%;margin-left:-10px;font-size:2em;width:22px;display:none!important}.tiny-slide-bar .tiny-slide-bar__list>li>.icon-chevron-down:before{content:"";position:absolute;width:20px;height:20px;border-right:5px solid var(--ti-slider-progress-box-hover-border-color);border-bottom:5px solid var(--ti-slider-progress-box-hover-border-color);-webkit-transform:rotate(45deg);transform:rotate(45deg);background:#fff;top:-5px}.tiny-slide-bar .tiny-slide-bar__list>li>ul{width:100%;list-style:none}.tiny-slide-bar .tiny-slide-bar__list>li.tiny-slide-bar__select{border-color:var(--ti-slider-progress-box-hover-border-color)}.tiny-slide-bar .tiny-slide-bar__list>li.tiny-slide-bar__select>.icon-chevron-down{display:block!important}.tiny-slide-bar .tiny-slide-bar__list>li.tiny-slide-bar__select li .tiny-icon{color:var(--ti-slider-progress-box-hover-border-color)}.tiny-slide-bar .tiny-slide-bar__list>li.tiny-slide-bar__select li:nth-child(2){border-bottom:1px solid var(--ti-slider-progress-box-hover-border-color)} diff --git a/web/dist/assets/@opentiny-d73a2d67.js b/web/dist/assets/@opentiny-d73a2d67.js new file mode 100644 index 00000000..27b72d90 --- /dev/null +++ b/web/dist/assets/@opentiny-d73a2d67.js @@ -0,0 +1,2 @@ +import{h as it}from"./vue-1e3b54ec.js";import{l as Ee}from"./xss-a5544f63.js";import{d as ur,c as pr,a as fr,h as mr,g as G,i as we,p as gr,o as hr,b as vr,n as $t,m as yr,e as H,f as ae,j as D,r as Me,k as me,l as Ce,w as Ne,q as lt,s as st,t as ct,F as br,u as wr,v as xr,x as dt,T as Sr,y as Tr}from"./@vue-a481fc63.js";const It=Object.prototype.toString,He=Object.prototype.hasOwnProperty,Mr=Object.getPrototypeOf,Et=He.toString,Cr=Et.call(Object),Nr={"[object Error]":"error","[object Object]":"object","[object RegExp]":"regExp","[object Date]":"date","[object Array]":"array","[object Function]":"function","[object String]":"string","[object Number]":"number","[object Boolean]":"boolean"},Z=e=>e==null||e==="undefined",ge=e=>Z(e)?String(e):Nr[It.call(e)]||"object",ut=e=>ge(e)==="object",$=e=>{if(!e||It.call(e)!=="[object Object]")return!1;const t=Mr(e);if(!t)return!0;const r=He.call(t,"constructor")&&t.constructor;return typeof r=="function"&&Et.call(r)===Cr},Ve=e=>typeof e=="number"&&isFinite(e),pt=e=>e-parseFloat(e)>=0,he=e=>ge(e)==="date",ft=(e,t)=>{if(typeof t=="function"){for(const r in e)if(He.call(e,r)&&t(r,e[r])===!1)break}};let q;const ke=(e,t,r)=>{if(!e||!$(e)||!t||typeof t!="string")return;t=t.split(".");let n=e;const a=t.length;if(a>1){const o=r?1:0;for(let i=o;i{if(!e||!$(e)||!t||typeof t!="string")return e;t=t.split(".");const a=e;let o=t.length,i=t[0];if(o>1){o--;let l=a,s,c;for(let d=0;d{yt[t]=rn(e,t)}),yt};nn(tn);let on=Jr;function an(e){let t=Object.create(null);return function(n){return t[n]||(t[n]=e(n))}}const ln=/\B([A-Z])/g,sn=an(e=>e.replace(ln,"-$1").toLowerCase()),bt=(e,t,r,n="0")=>{if(typeof e=="string"&&typeof n=="string"&&Ve(t)){let a=e.length-t;if(a>0)return r?e.substr(0,t):e.substr(a,t);{const o=[];for(a=Math.abs(a)/n.length;a>0;a--)o.push(n);const i=o.join("");return r?e+i:i+e}}};on.random;const Bt=[31,28,31,30,31,30,31,31,30,31,30,31],cn=new RegExp("^(\\d{4})(/|-)(((0)?[1-9])|(1[0-2]))((/|-)(((0)?[1-9])|([1-2][0-9])|(3[0-1])))?( ((0)?[0-9]|1[0-9]|20|21|22|23):([0-5]?[0-9])((:([0-5]?[0-9]))?(.([0-9]{1,6}))?)?)?$"),dn=new RegExp("^(((0)?[1-9])|(1[0-2]))(/|-)(((0)?[1-9])|([1-2][0-9])|(3[0-1]))?(/|-)?(\\d{4})( ((0)?[0-9]|1[0-9]|20|21|22|23):([0-5]?[0-9])((:([0-5]?[0-9]))?(.([0-9]{1,6}))?)?)?$"),un=new RegExp("^(\\d{4})-(((0)?[1-9])|(1[0-2]))-(((0)?[1-9])|([1-2][0-9])|(3[0-1]))T(((0)?[0-9]|1[0-9]|20|21|22|23):([0-5]?[0-9])((:([0-5]?[0-9]))?(.([0-9]{1,6}))?)?)?(Z|([+-])((0)?[0-9]|1[0-9]|20|21|22|23):?([0-5]?[0-9]))$"),j={YEAR:9999,MONTH:11,DATE:31,HOUR:23,MINUTE:59,SECOND:59,MILLISECOND:999},pn="-12:00,-11:00,-10:00,-09:30,-08:00,-07:00,-06:00,-05:00,-04:30,-04:00,-03:30,-02:00,-01:00",fn="-00:00,+00:00,+01:00,+02:00,+03:00,+03:30,+04:00,+04:30,+05:00,+05:30,+05:45,+06:00",mn="+06:30,+07:00,+08:00,+09:00,+10:00,+10:30,+11:00,+11:30,+12:00,+12:45,+13:00,+14:00",gn=[].concat(pn.split(","),fn.split(","),mn.split(",")),Ut=e=>e%400===0||e%4===0&&e%100!==0,Ze=({year:e,month:t,date:r,hours:n,minutes:a,seconds:o,milliseconds:i})=>{let l=Bt[t];if(Ut(e)&&t===1&&(l+=1),r<=l)return new Date(e,t,r,n,a,o,i)},hn=e=>{if(e.length===23){const t=Number(e[1]),r=e[3]-1,n=Number(e[9]||1),a=e[15]||0,o=e[17]||0,i=e[20]||0,l=e[22]||0;return Ze({date:n,year:t,hours:a,month:r,seconds:i,minutes:o,milliseconds:l})}},vn=e=>{if(e.length===22){const t=Number(e[12]),r=e[1]-1,n=Number(e[6]||1),a=e[14]||0,o=e[16]||0,i=e[19]||0,l=e[21]||0;return Ze({year:t,month:r,date:n,hours:a,minutes:o,seconds:i,milliseconds:l})}},yn=e=>{if(e.length!==25)return;const t=Number(e[1]),r=e[2]-1,n=Number(e[6]),a=new Date(t,r,n).getTimezoneOffset(),o=e[12]||0,i=e[14]||0,l=e[17]||0,s=e[19]||0;let c=e[20];const d=e[21],u=e[22]||0,p=e[24]||0;let f=Bt[r],g,v;if(Ut(t)&&r===1&&(f+=1),n<=f){if(c==="Z")g=o-a/60,v=i;else{if(c.includes(":")||(c=c.substr(0,3)+":"+c.substr(3)),!gn.includes(c))return;g=d==="+"?o-u-a/60:Number(o)+Number(u)-a/60,v=d==="+"?i-p:Number(i)+Number(p)}return new Date(t,r,n,g,v,l,s)}},De=[[cn,hn],[dn,vn],[un,yn]],bn=e=>{for(let t=0,r=De.length;t{const t=new Set(e);return t.w=0,t.n=0,t},hr=e=>(e.w&ke)>0,pr=e=>(e.n&ke)>0,Lo=({deps:e})=>{if(e.length)for(let t=0;t{const t=new Set(e);return t.w=0,t.n=0,t},Qr=e=>(e.w&ze)>0,zr=e=>(e.n&ze)>0,Lo=({deps:e})=>{if(e.length)for(let t=0;t