From 5ec628fccb9e4f13fc9fd73ab08968bf5652fc5e Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Mon, 3 Jul 2023 14:12:29 +0800 Subject: [PATCH 01/73] style: add import formatting Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- .github/workflows/openim-ci.yml | 72 +++++++++++++++++++ go.mod | 1 + go.sum | 2 + internal/msggateway/callback.go | 3 +- internal/msggateway/context.go | 5 +- internal/msggateway/encoder.go | 1 + internal/msggateway/hub_server.go | 1 + internal/msggateway/long_conn.go | 3 +- internal/msggateway/user_map.go | 3 +- internal/push/offlinepush/fcm/push_test.go | 3 +- internal/push/offlinepush/getui/push.go | 8 ++- .../push/offlinepush/jpush/body/platform.go | 1 + internal/push/offlinepush/jpush/push.go | 1 + internal/rpc/group/fill.go | 1 + internal/rpc/msg/msg_status.go | 1 + internal/rpc/msg/revoke.go | 3 +- internal/rpc/msg/send.go | 1 + internal/rpc/third/s3.go | 3 +- internal/rpc/user/statistics.go | 3 +- internal/rpc/user/user.go | 3 +- pkg/a2r/api2rpc.go | 1 + pkg/apiresp/gin.go | 3 +- pkg/apiresp/resp.go | 3 +- pkg/common/config/parse.go | 7 +- pkg/common/db/controller/msg.go | 3 +- pkg/common/db/controller/storage.go | 11 +-- pkg/common/db/obj/minio.go | 9 +-- pkg/common/db/ormutil/utils.go | 3 +- pkg/common/db/relation/group_model.go | 1 + pkg/common/db/relation/group_request_model.go | 1 + pkg/common/db/relation/mysql_init.go | 3 +- pkg/common/db/relation/object_hash_model.go | 1 + pkg/common/db/relation/object_info_model.go | 3 +- pkg/common/db/relation/object_put_model.go | 3 +- pkg/common/db/relation/user_model.go | 3 +- pkg/common/db/tx/mongo.go | 1 + pkg/common/db/unrelation/super_group.go | 1 + pkg/common/kafka/consumer_group.go | 1 + pkg/common/mw/rpc_server_interceptor.go | 3 +- pkg/common/prome/prometheus.go | 3 +- pkg/common/tokenverify/jwt_token.go | 3 +- pkg/errs/coderr.go | 3 +- pkg/proto/auth/auth.pb.go | 5 +- pkg/proto/conversation/conversation.pb.go | 5 +- pkg/proto/errinfo/errinfo.pb.go | 5 +- pkg/proto/friend/friend.pb.go | 5 +- pkg/proto/group/group.pb.go | 5 +- pkg/proto/msg/msg.pb.go | 5 +- pkg/proto/msggateway/msggateway.pb.go | 5 +- pkg/proto/push/push.pb.go | 5 +- pkg/proto/sdkws/sdkws.pb.go | 5 +- pkg/proto/statistics/statistics.pb.go | 3 +- pkg/proto/third/third.pb.go | 5 +- pkg/proto/user/user.pb.go | 5 +- pkg/proto/wrapperspb/wrapperspb.pb.go | 5 +- pkg/statistics/statistics.go | 3 +- pkg/utils/file.go | 3 +- pkg/utils/platform_number_id_to_name_test.go | 3 +- pkg/utils/time_format.go | 18 ++--- scripts/make-rules/golang.mk | 2 +- test/mongo/cmd/main.go | 1 + test/mongo/mongo_utils.go | 3 +- 62 files changed, 209 insertions(+), 76 deletions(-) create mode 100644 .github/workflows/openim-ci.yml diff --git a/.github/workflows/openim-ci.yml b/.github/workflows/openim-ci.yml new file mode 100644 index 000000000..7ee0545f8 --- /dev/null +++ b/.github/workflows/openim-ci.yml @@ -0,0 +1,72 @@ +name: IamCI + +on: +# main branch + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + + iamci: + name: Test with go ${{ matrix.go_version }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + environment: + name: iamci + + strategy: + matrix: + go_version: [1.18, 1.19, 1.20] + os: [ubuntu-latest, macOS-latest] + + steps: + - name: Set up Go ${{ matrix.go_version }} + uses: actions/setup-go@v2 + with: + go-version: ${{ matrix.go_version }} + id: go + + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + + - name: Run go modules Tidy + run: | + make tidy + + - name: Generate all necessary files, such as error code files + run: | + make gen + + - name: Check syntax and styling of go sources + run: | + make lint + + - name: Run unit test and get test coverage + run: | + make cover + + - name: Build source code for host platform + run: | + make build + + - name: Collect Test Coverage File + uses: actions/upload-artifact@v1.0.0 + with: + name: main-output + path: _output/coverage.out + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build docker images for host arch and push images to registry + run: | + make push \ No newline at end of file diff --git a/go.mod b/go.mod index 0a8c1fd52..88d5abb09 100644 --- a/go.mod +++ b/go.mod @@ -43,6 +43,7 @@ require ( github.com/go-sql-driver/mysql v1.6.0 github.com/go-zookeeper/zk v1.0.3 github.com/redis/go-redis/v9 v9.0.5 + gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 ) require ( diff --git a/go.sum b/go.sum index 443d6cb9d..12f5d41bb 100644 --- a/go.sum +++ b/go.sum @@ -784,6 +784,8 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= +gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/msggateway/callback.go b/internal/msggateway/callback.go index 2d30b52b4..557e65ea6 100644 --- a/internal/msggateway/callback.go +++ b/internal/msggateway/callback.go @@ -2,12 +2,13 @@ package msggateway import ( "context" + "time" + cbapi "github.com/OpenIMSDK/Open-IM-Server/pkg/callbackstruct" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/http" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" - "time" ) func url() string { diff --git a/internal/msggateway/context.go b/internal/msggateway/context.go index cd395e7e0..61f4d7970 100644 --- a/internal/msggateway/context.go +++ b/internal/msggateway/context.go @@ -1,11 +1,12 @@ package msggateway import ( - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" - "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" "net/http" "strconv" "time" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" + "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" ) type UserConnContext struct { diff --git a/internal/msggateway/encoder.go b/internal/msggateway/encoder.go index 6a4104ff4..6915766c6 100644 --- a/internal/msggateway/encoder.go +++ b/internal/msggateway/encoder.go @@ -3,6 +3,7 @@ package msggateway import ( "bytes" "encoding/gob" + "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" ) diff --git a/internal/msggateway/hub_server.go b/internal/msggateway/hub_server.go index 633b7c479..ab8f9af26 100644 --- a/internal/msggateway/hub_server.go +++ b/internal/msggateway/hub_server.go @@ -2,6 +2,7 @@ package msggateway import ( "context" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" diff --git a/internal/msggateway/long_conn.go b/internal/msggateway/long_conn.go index ed31dbda7..911e9a4a1 100644 --- a/internal/msggateway/long_conn.go +++ b/internal/msggateway/long_conn.go @@ -1,9 +1,10 @@ package msggateway import ( - "github.com/gorilla/websocket" "net/http" "time" + + "github.com/gorilla/websocket" ) type LongConn interface { diff --git a/internal/msggateway/user_map.go b/internal/msggateway/user_map.go index 63881bc1a..46bdb16a4 100644 --- a/internal/msggateway/user_map.go +++ b/internal/msggateway/user_map.go @@ -2,9 +2,10 @@ package msggateway import ( "context" + "sync" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "sync" ) type UserMap struct { diff --git a/internal/push/offlinepush/fcm/push_test.go b/internal/push/offlinepush/fcm/push_test.go index 81b54cfbb..1a7240302 100644 --- a/internal/push/offlinepush/fcm/push_test.go +++ b/internal/push/offlinepush/fcm/push_test.go @@ -2,10 +2,11 @@ package fcm import ( "context" + "testing" + "github.com/OpenIMSDK/Open-IM-Server/internal/push/offlinepush" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" "github.com/stretchr/testify/assert" - "testing" ) func Test_Push(t *testing.T) { diff --git a/internal/push/offlinepush/getui/push.go b/internal/push/offlinepush/getui/push.go index db5accbd7..54bd83673 100644 --- a/internal/push/offlinepush/getui/push.go +++ b/internal/push/offlinepush/getui/push.go @@ -1,13 +1,17 @@ package getui import ( - "github.com/go-redis/redis" "sync" + "github.com/go-redis/redis" + "context" "crypto/sha256" "encoding/hex" "errors" + "strconv" + "time" + "github.com/OpenIMSDK/Open-IM-Server/internal/push/offlinepush" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" @@ -16,8 +20,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils/splitter" - "strconv" - "time" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" ) diff --git a/internal/push/offlinepush/jpush/body/platform.go b/internal/push/offlinepush/jpush/body/platform.go index 9aa28f82c..68264fea0 100644 --- a/internal/push/offlinepush/jpush/body/platform.go +++ b/internal/push/offlinepush/jpush/body/platform.go @@ -2,6 +2,7 @@ package body import ( "errors" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" ) diff --git a/internal/push/offlinepush/jpush/push.go b/internal/push/offlinepush/jpush/push.go index 6a4ea0ceb..75fc2b27b 100644 --- a/internal/push/offlinepush/jpush/push.go +++ b/internal/push/offlinepush/jpush/push.go @@ -4,6 +4,7 @@ import ( "context" "encoding/base64" "fmt" + "github.com/OpenIMSDK/Open-IM-Server/internal/push/offlinepush" "github.com/OpenIMSDK/Open-IM-Server/internal/push/offlinepush/jpush/body" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" diff --git a/internal/rpc/group/fill.go b/internal/rpc/group/fill.go index 4302e3bf0..da82e9b5e 100644 --- a/internal/rpc/group/fill.go +++ b/internal/rpc/group/fill.go @@ -2,6 +2,7 @@ package group import ( "context" + relationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" ) diff --git a/internal/rpc/msg/msg_status.go b/internal/rpc/msg/msg_status.go index 5639ecc01..3e1f9053b 100644 --- a/internal/rpc/msg/msg_status.go +++ b/internal/rpc/msg/msg_status.go @@ -2,6 +2,7 @@ package msg import ( "context" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" pbMsg "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" diff --git a/internal/rpc/msg/revoke.go b/internal/rpc/msg/revoke.go index 746017940..c32050bfe 100644 --- a/internal/rpc/msg/revoke.go +++ b/internal/rpc/msg/revoke.go @@ -3,9 +3,10 @@ package msg import ( "context" "encoding/json" - "github.com/google/uuid" "time" + "github.com/google/uuid" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" unRelationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" diff --git a/internal/rpc/msg/send.go b/internal/rpc/msg/send.go index a7fc6533b..deaee0bed 100644 --- a/internal/rpc/msg/send.go +++ b/internal/rpc/msg/send.go @@ -2,6 +2,7 @@ package msg import ( "context" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" diff --git a/internal/rpc/third/s3.go b/internal/rpc/third/s3.go index 21471e00d..e0a3a99da 100644 --- a/internal/rpc/third/s3.go +++ b/internal/rpc/third/s3.go @@ -2,9 +2,10 @@ package third import ( "context" + "time" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/tokenverify" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/third" - "time" ) func (t *thirdServer) ApplyPut(ctx context.Context, req *third.ApplyPutReq) (*third.ApplyPutResp, error) { diff --git a/internal/rpc/user/statistics.go b/internal/rpc/user/statistics.go index 036b09687..12ec0d55f 100644 --- a/internal/rpc/user/statistics.go +++ b/internal/rpc/user/statistics.go @@ -2,9 +2,10 @@ package user import ( "context" + "time" + "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" pbuser "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/user" - "time" ) func (s *userServer) UserRegisterCount(ctx context.Context, req *pbuser.UserRegisterCountReq) (*pbuser.UserRegisterCountResp, error) { diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index 3437754dc..277bf4284 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -2,10 +2,11 @@ package user import ( "context" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "strings" "time" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/convert" diff --git a/pkg/a2r/api2rpc.go b/pkg/a2r/api2rpc.go index 50d5b2ac3..39bd70ce4 100644 --- a/pkg/a2r/api2rpc.go +++ b/pkg/a2r/api2rpc.go @@ -2,6 +2,7 @@ package a2r import ( "context" + "github.com/OpenIMSDK/Open-IM-Server/pkg/checker" "github.com/OpenIMSDK/Open-IM-Server/pkg/apiresp" diff --git a/pkg/apiresp/gin.go b/pkg/apiresp/gin.go index e17f0144a..b2bfaf4f9 100644 --- a/pkg/apiresp/gin.go +++ b/pkg/apiresp/gin.go @@ -1,8 +1,9 @@ package apiresp import ( - "github.com/gin-gonic/gin" "net/http" + + "github.com/gin-gonic/gin" ) func GinError(c *gin.Context, err error) { diff --git a/pkg/apiresp/resp.go b/pkg/apiresp/resp.go index cd215a984..29dc33996 100644 --- a/pkg/apiresp/resp.go +++ b/pkg/apiresp/resp.go @@ -1,8 +1,9 @@ package apiresp import ( - "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "reflect" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" ) type ApiResponse struct { diff --git a/pkg/common/config/parse.go b/pkg/common/config/parse.go index 8ab68b68f..0a32e81c7 100644 --- a/pkg/common/config/parse.go +++ b/pkg/common/config/parse.go @@ -3,13 +3,14 @@ package config import ( "bytes" "fmt" + "os" + "path/filepath" + "runtime" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" "gopkg.in/yaml.v3" - "os" - "path/filepath" - "runtime" ) var ( diff --git a/pkg/common/db/controller/msg.go b/pkg/common/db/controller/msg.go index e732f1f08..dd62e0e2e 100644 --- a/pkg/common/db/controller/msg.go +++ b/pkg/common/db/controller/msg.go @@ -2,9 +2,10 @@ package controller import ( "fmt" - "github.com/redis/go-redis/v9" "time" + "github.com/redis/go-redis/v9" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/convert" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" diff --git a/pkg/common/db/controller/storage.go b/pkg/common/db/controller/storage.go index 5182bb199..6531c5127 100644 --- a/pkg/common/db/controller/storage.go +++ b/pkg/common/db/controller/storage.go @@ -9,6 +9,12 @@ import ( "encoding/json" "errors" "fmt" + "io" + "net/url" + "path" + "strconv" + "time" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/obj" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" @@ -16,11 +22,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/third" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" "github.com/google/uuid" - "io" - "net/url" - "path" - "strconv" - "time" ) const ( diff --git a/pkg/common/db/obj/minio.go b/pkg/common/db/obj/minio.go index 8e28896bd..488775f27 100644 --- a/pkg/common/db/obj/minio.go +++ b/pkg/common/db/obj/minio.go @@ -4,15 +4,16 @@ import ( "context" "errors" "fmt" + "io" + "net/http" + "net/url" + "time" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" "github.com/minio/minio-go/v7/pkg/s3utils" - "io" - "net/http" - "net/url" - "time" ) func NewMinioInterface() (Interface, error) { diff --git a/pkg/common/db/ormutil/utils.go b/pkg/common/db/ormutil/utils.go index 2a916cb39..9c4e19a5b 100644 --- a/pkg/common/db/ormutil/utils.go +++ b/pkg/common/db/ormutil/utils.go @@ -2,9 +2,10 @@ package ormutil import ( "fmt" + "strings" + "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "gorm.io/gorm" - "strings" ) func GormPage[E any](db *gorm.DB, pageNumber, showNumber int32) (uint32, []*E, error) { diff --git a/pkg/common/db/relation/group_model.go b/pkg/common/db/relation/group_model.go index 39feb5d7a..dd9f59bea 100644 --- a/pkg/common/db/relation/group_model.go +++ b/pkg/common/db/relation/group_model.go @@ -2,6 +2,7 @@ package relation import ( "context" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/ormutil" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" diff --git a/pkg/common/db/relation/group_request_model.go b/pkg/common/db/relation/group_request_model.go index 679671e5d..407c2b136 100644 --- a/pkg/common/db/relation/group_request_model.go +++ b/pkg/common/db/relation/group_request_model.go @@ -2,6 +2,7 @@ package relation import ( "context" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/ormutil" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" diff --git a/pkg/common/db/relation/mysql_init.go b/pkg/common/db/relation/mysql_init.go index b63bada05..432af214f 100644 --- a/pkg/common/db/relation/mysql_init.go +++ b/pkg/common/db/relation/mysql_init.go @@ -2,13 +2,14 @@ package relation import ( "fmt" + "time" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mw/specialerror" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" mysqlDriver "github.com/go-sql-driver/mysql" "gorm.io/driver/mysql" - "time" "gorm.io/gorm" "gorm.io/gorm/logger" diff --git a/pkg/common/db/relation/object_hash_model.go b/pkg/common/db/relation/object_hash_model.go index e122d9cec..275d09a4e 100644 --- a/pkg/common/db/relation/object_hash_model.go +++ b/pkg/common/db/relation/object_hash_model.go @@ -2,6 +2,7 @@ package relation import ( "context" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" "gorm.io/gorm" diff --git a/pkg/common/db/relation/object_info_model.go b/pkg/common/db/relation/object_info_model.go index d2a751cff..f5b34755f 100644 --- a/pkg/common/db/relation/object_info_model.go +++ b/pkg/common/db/relation/object_info_model.go @@ -2,11 +2,12 @@ package relation import ( "context" + "time" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" "gorm.io/gorm" - "time" ) type ObjectInfoGorm struct { diff --git a/pkg/common/db/relation/object_put_model.go b/pkg/common/db/relation/object_put_model.go index 82f98624c..06a9883af 100644 --- a/pkg/common/db/relation/object_put_model.go +++ b/pkg/common/db/relation/object_put_model.go @@ -2,10 +2,11 @@ package relation import ( "context" + "time" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" "gorm.io/gorm" - "time" ) type ObjectPutGorm struct { diff --git a/pkg/common/db/relation/user_model.go b/pkg/common/db/relation/user_model.go index 3390f7d3b..481393ac0 100644 --- a/pkg/common/db/relation/user_model.go +++ b/pkg/common/db/relation/user_model.go @@ -2,9 +2,10 @@ package relation import ( "context" - "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "time" + "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" "gorm.io/gorm" diff --git a/pkg/common/db/tx/mongo.go b/pkg/common/db/tx/mongo.go index f0afaa237..c8c4817ac 100644 --- a/pkg/common/db/tx/mongo.go +++ b/pkg/common/db/tx/mongo.go @@ -2,6 +2,7 @@ package tx import ( "context" + "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" "go.mongodb.org/mongo-driver/mongo" ) diff --git a/pkg/common/db/unrelation/super_group.go b/pkg/common/db/unrelation/super_group.go index 23c226974..54de697e4 100644 --- a/pkg/common/db/unrelation/super_group.go +++ b/pkg/common/db/unrelation/super_group.go @@ -2,6 +2,7 @@ package unrelation import ( "context" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" "go.mongodb.org/mongo-driver/bson" diff --git a/pkg/common/kafka/consumer_group.go b/pkg/common/kafka/consumer_group.go index 318583852..065fc6277 100644 --- a/pkg/common/kafka/consumer_group.go +++ b/pkg/common/kafka/consumer_group.go @@ -8,6 +8,7 @@ package kafka import ( "context" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/Shopify/sarama" diff --git a/pkg/common/mw/rpc_server_interceptor.go b/pkg/common/mw/rpc_server_interceptor.go index 518b5f0a2..be37fc0fb 100644 --- a/pkg/common/mw/rpc_server_interceptor.go +++ b/pkg/common/mw/rpc_server_interceptor.go @@ -3,11 +3,12 @@ package mw import ( "context" "fmt" - "github.com/OpenIMSDK/Open-IM-Server/pkg/checker" "math" "runtime" "strings" + "github.com/OpenIMSDK/Open-IM-Server/pkg/checker" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" diff --git a/pkg/common/prome/prometheus.go b/pkg/common/prome/prometheus.go index a41626cd0..299f07d43 100644 --- a/pkg/common/prome/prometheus.go +++ b/pkg/common/prome/prometheus.go @@ -2,10 +2,11 @@ package prome import ( "bytes" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "net/http" "strconv" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" + "github.com/gin-gonic/gin" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" diff --git a/pkg/common/tokenverify/jwt_token.go b/pkg/common/tokenverify/jwt_token.go index 7d634af3e..582a7f68e 100644 --- a/pkg/common/tokenverify/jwt_token.go +++ b/pkg/common/tokenverify/jwt_token.go @@ -3,12 +3,13 @@ package tokenverify import ( "context" "fmt" + "time" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" "github.com/golang-jwt/jwt/v4" - "time" ) type Claims struct { diff --git a/pkg/errs/coderr.go b/pkg/errs/coderr.go index b8371bd5b..506a0ddcc 100644 --- a/pkg/errs/coderr.go +++ b/pkg/errs/coderr.go @@ -2,8 +2,9 @@ package errs import ( "fmt" - "github.com/pkg/errors" "strings" + + "github.com/pkg/errors" ) type CodeError interface { diff --git a/pkg/proto/auth/auth.pb.go b/pkg/proto/auth/auth.pb.go index c5343c677..095998cc6 100644 --- a/pkg/proto/auth/auth.pb.go +++ b/pkg/proto/auth/auth.pb.go @@ -8,13 +8,14 @@ package auth import ( context "context" + reflect "reflect" + sync "sync" + grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" ) const ( diff --git a/pkg/proto/conversation/conversation.pb.go b/pkg/proto/conversation/conversation.pb.go index cab6772e9..8c7451a3d 100644 --- a/pkg/proto/conversation/conversation.pb.go +++ b/pkg/proto/conversation/conversation.pb.go @@ -8,14 +8,15 @@ package conversation import ( context "context" + reflect "reflect" + sync "sync" + wrapperspb "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/wrapperspb" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" ) const ( diff --git a/pkg/proto/errinfo/errinfo.pb.go b/pkg/proto/errinfo/errinfo.pb.go index 4db1d6d95..ae10fe976 100644 --- a/pkg/proto/errinfo/errinfo.pb.go +++ b/pkg/proto/errinfo/errinfo.pb.go @@ -7,10 +7,11 @@ package errinfo import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( diff --git a/pkg/proto/friend/friend.pb.go b/pkg/proto/friend/friend.pb.go index fd97cefb3..515ac8128 100644 --- a/pkg/proto/friend/friend.pb.go +++ b/pkg/proto/friend/friend.pb.go @@ -8,14 +8,15 @@ package friend import ( context "context" + reflect "reflect" + sync "sync" + sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" ) const ( diff --git a/pkg/proto/group/group.pb.go b/pkg/proto/group/group.pb.go index 5febafa2f..5024365cc 100644 --- a/pkg/proto/group/group.pb.go +++ b/pkg/proto/group/group.pb.go @@ -8,6 +8,9 @@ package group import ( context "context" + reflect "reflect" + sync "sync" + sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" wrapperspb "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/wrapperspb" grpc "google.golang.org/grpc" @@ -15,8 +18,6 @@ import ( status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" ) const ( diff --git a/pkg/proto/msg/msg.pb.go b/pkg/proto/msg/msg.pb.go index e283b7573..4a35ebe4f 100644 --- a/pkg/proto/msg/msg.pb.go +++ b/pkg/proto/msg/msg.pb.go @@ -8,6 +8,9 @@ package msg import ( context "context" + reflect "reflect" + sync "sync" + sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" wrapperspb "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/wrapperspb" grpc "google.golang.org/grpc" @@ -15,8 +18,6 @@ import ( status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" ) const ( diff --git a/pkg/proto/msggateway/msggateway.pb.go b/pkg/proto/msggateway/msggateway.pb.go index 40751e999..9b247c65a 100644 --- a/pkg/proto/msggateway/msggateway.pb.go +++ b/pkg/proto/msggateway/msggateway.pb.go @@ -8,14 +8,15 @@ package msggateway import ( context "context" + reflect "reflect" + sync "sync" + sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" ) const ( diff --git a/pkg/proto/push/push.pb.go b/pkg/proto/push/push.pb.go index 223ecb998..1e6a895f1 100644 --- a/pkg/proto/push/push.pb.go +++ b/pkg/proto/push/push.pb.go @@ -8,14 +8,15 @@ package push import ( context "context" + reflect "reflect" + sync "sync" + sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" ) const ( diff --git a/pkg/proto/sdkws/sdkws.pb.go b/pkg/proto/sdkws/sdkws.pb.go index c8ed2a728..13637d3c9 100644 --- a/pkg/proto/sdkws/sdkws.pb.go +++ b/pkg/proto/sdkws/sdkws.pb.go @@ -7,11 +7,12 @@ package sdkws import ( + reflect "reflect" + sync "sync" + wrapperspb "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/wrapperspb" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" ) const ( diff --git a/pkg/proto/statistics/statistics.pb.go b/pkg/proto/statistics/statistics.pb.go index 6943736ec..092f67b52 100644 --- a/pkg/proto/statistics/statistics.pb.go +++ b/pkg/proto/statistics/statistics.pb.go @@ -7,9 +7,10 @@ package statistics import ( + reflect "reflect" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" ) const ( diff --git a/pkg/proto/third/third.pb.go b/pkg/proto/third/third.pb.go index 40268e683..a2512db38 100644 --- a/pkg/proto/third/third.pb.go +++ b/pkg/proto/third/third.pb.go @@ -8,13 +8,14 @@ package third import ( context "context" + reflect "reflect" + sync "sync" + grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" ) const ( diff --git a/pkg/proto/user/user.pb.go b/pkg/proto/user/user.pb.go index f0db26e04..b55abd968 100644 --- a/pkg/proto/user/user.pb.go +++ b/pkg/proto/user/user.pb.go @@ -8,6 +8,9 @@ package user import ( context "context" + reflect "reflect" + sync "sync" + conversation "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/conversation" sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" grpc "google.golang.org/grpc" @@ -15,8 +18,6 @@ import ( status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" ) const ( diff --git a/pkg/proto/wrapperspb/wrapperspb.pb.go b/pkg/proto/wrapperspb/wrapperspb.pb.go index 0e079bf36..2bb8b39db 100644 --- a/pkg/proto/wrapperspb/wrapperspb.pb.go +++ b/pkg/proto/wrapperspb/wrapperspb.pb.go @@ -7,10 +7,11 @@ package wrapperspb import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( diff --git a/pkg/statistics/statistics.go b/pkg/statistics/statistics.go index 700b24746..3ec3c6aa8 100644 --- a/pkg/statistics/statistics.go +++ b/pkg/statistics/statistics.go @@ -2,8 +2,9 @@ package statistics import ( "context" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "time" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" ) type Statistics struct { diff --git a/pkg/utils/file.go b/pkg/utils/file.go index d79b11003..7aa7719bd 100644 --- a/pkg/utils/file.go +++ b/pkg/utils/file.go @@ -2,13 +2,14 @@ package utils import ( "fmt" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "math/rand" "os" "path" "strconv" "strings" "time" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" ) const ( diff --git a/pkg/utils/platform_number_id_to_name_test.go b/pkg/utils/platform_number_id_to_name_test.go index c671d37cf..78569aed4 100644 --- a/pkg/utils/platform_number_id_to_name_test.go +++ b/pkg/utils/platform_number_id_to_name_test.go @@ -1,9 +1,10 @@ package utils import ( - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "testing" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" + "github.com/stretchr/testify/assert" ) diff --git a/pkg/utils/time_format.go b/pkg/utils/time_format.go index db1d3ac82..59abb90c4 100644 --- a/pkg/utils/time_format.go +++ b/pkg/utils/time_format.go @@ -16,17 +16,17 @@ const ( HalfOffset = 12 * 3600 //Half-day hourly offset ) -//Get the current timestamp by Second +// Get the current timestamp by Second func GetCurrentTimestampBySecond() int64 { return time.Now().Unix() } -//Convert timestamp to time.Time type +// Convert timestamp to time.Time type func UnixSecondToTime(second int64) time.Time { return time.Unix(second, 0) } -//Convert nano timestamp to time.Time type +// Convert nano timestamp to time.Time type func UnixNanoSecondToTime(nanoSecond int64) time.Time { return time.Unix(0, nanoSecond) } @@ -34,35 +34,35 @@ func UnixMillSecondToTime(millSecond int64) time.Time { return time.Unix(0, millSecond*1e6) } -//Get the current timestamp by Nano +// Get the current timestamp by Nano func GetCurrentTimestampByNano() int64 { return time.Now().UnixNano() } -//Get the current timestamp by Mill +// Get the current timestamp by Mill func GetCurrentTimestampByMill() int64 { return time.Now().UnixNano() / 1e6 } -//Get the timestamp at 0 o'clock of the day +// Get the timestamp at 0 o'clock of the day func GetCurDayZeroTimestamp() int64 { timeStr := time.Now().Format("2006-01-02") t, _ := time.Parse("2006-01-02", timeStr) return t.Unix() - TimeOffset } -//Get the timestamp at 12 o'clock on the day +// Get the timestamp at 12 o'clock on the day func GetCurDayHalfTimestamp() int64 { return GetCurDayZeroTimestamp() + HalfOffset } -//Get the formatted time at 0 o'clock of the day, the format is "2006-01-02_00-00-00" +// Get the formatted time at 0 o'clock of the day, the format is "2006-01-02_00-00-00" func GetCurDayZeroTimeFormat() string { return time.Unix(GetCurDayZeroTimestamp(), 0).Format("2006-01-02_15-04-05") } -//Get the formatted time at 12 o'clock of the day, the format is "2006-01-02_12-00-00" +// Get the formatted time at 12 o'clock of the day, the format is "2006-01-02_12-00-00" func GetCurDayHalfTimeFormat() string { return time.Unix(GetCurDayZeroTimestamp()+HalfOffset, 0).Format("2006-01-02_15-04-05") } diff --git a/scripts/make-rules/golang.mk b/scripts/make-rules/golang.mk index 1b70f09da..b12ad0c93 100644 --- a/scripts/make-rules/golang.mk +++ b/scripts/make-rules/golang.mk @@ -131,7 +131,7 @@ go.build.multiarch: go.build.verify $(foreach p,$(PLATFORMS),$(addprefix go.buil .PHONY: go.lint go.lint: tools.verify.golangci-lint @echo "===========> Run golangci to lint source codes" - @$(BIN_DIR)/golangci-lint run -c $(ROOT_DIR)/.golangci.yml $(ROOT_DIR)/... + @$(TOOLS_BIN)/golangci-lint run -c $(ROOT_DIR)/.golangci.yml $(ROOT_DIR)/... ## go.test: Run unit test .PHONY: go.test diff --git a/test/mongo/cmd/main.go b/test/mongo/cmd/main.go index 9b4f315f8..3e4017960 100644 --- a/test/mongo/cmd/main.go +++ b/test/mongo/cmd/main.go @@ -20,6 +20,7 @@ import ( "context" "flag" "fmt" + "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) diff --git a/test/mongo/mongo_utils.go b/test/mongo/mongo_utils.go index 50d000f99..18cdcbea3 100644 --- a/test/mongo/mongo_utils.go +++ b/test/mongo/mongo_utils.go @@ -19,10 +19,11 @@ import ( server_api_params "Open_IM/pkg/proto/sdk_ws" "context" "fmt" + "time" + "github.com/golang/protobuf/proto" "go.mongodb.org/mongo-driver/mongo" "gopkg.in/mgo.v2/bson" - "time" ) var ( From 187ee9a3751520f43b67d840cab27fbd4726c70b Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Mon, 3 Jul 2023 14:15:57 +0800 Subject: [PATCH 02/73] feat: fix openim ci Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- .github/workflows/openim-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/openim-ci.yml b/.github/workflows/openim-ci.yml index 7ee0545f8..e063c6e3b 100644 --- a/.github/workflows/openim-ci.yml +++ b/.github/workflows/openim-ci.yml @@ -1,4 +1,4 @@ -name: IamCI +name: OpenIMCI on: # main branch @@ -11,11 +11,11 @@ on: jobs: - iamci: + openimci: name: Test with go ${{ matrix.go_version }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} environment: - name: iamci + name: openimci strategy: matrix: From 166ddb1e34b09ffa5fa79725af532eb4e61e48ec Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Mon, 3 Jul 2023 16:29:22 +0800 Subject: [PATCH 03/73] style: add format Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- cmd/api/main.go | 6 +- internal/api/auth.go | 3 +- internal/api/conversation.go | 3 +- internal/api/custom_validator.go | 3 +- internal/api/msg.go | 9 +- internal/api/route.go | 11 +- internal/api/statistics.go | 3 +- internal/api/third.go | 8 +- internal/api/user.go | 3 +- internal/msggateway/callback.go | 15 +- internal/msggateway/client.go | 22 +- internal/msggateway/hub_server.go | 49 +- internal/msggateway/init.go | 9 +- internal/msggateway/message_handler.go | 16 +- internal/msggateway/n_ws_server.go | 75 ++- internal/msgtransfer/init.go | 38 +- internal/msgtransfer/modify_msg_handler.go | 26 +- .../msgtransfer/online_history_msg_handler.go | 147 +++++- .../online_msg_to_mongo_handler.go | 38 +- .../msgtransfer/persistent_msg_handler.go | 29 +- internal/push/callback.go | 14 +- internal/push/consumer_init.go | 3 +- internal/push/offlinepush/fcm/push.go | 5 +- internal/push/offlinepush/fcm/push_test.go | 3 +- internal/push/offlinepush/getui/push.go | 13 +- internal/push/offlinepush/jpush/push.go | 11 +- internal/push/push_handler.go | 5 +- internal/push/push_rpc_server.go | 19 +- internal/push/push_to_client.go | 103 +++- internal/rpc/auth/auth.go | 14 +- internal/rpc/conversation/conversaion.go | 113 ++++- internal/rpc/friend/black.go | 17 +- internal/rpc/friend/friend.go | 82 +++- internal/rpc/group/cache.go | 10 +- internal/rpc/group/callback.go | 30 +- internal/rpc/group/convert.go | 5 +- internal/rpc/group/fill.go | 19 +- internal/rpc/group/group.go | 202 ++++++-- internal/rpc/group/super_group.go | 28 +- internal/rpc/msg/as_read.go | 32 +- internal/rpc/msg/delete.go | 55 ++- internal/rpc/msg/extend_msg.go | 79 ++- internal/rpc/msg/extend_msg_callback.go | 5 +- internal/rpc/msg/msg_status.go | 10 +- internal/rpc/msg/revoke.go | 7 +- internal/rpc/msg/send.go | 18 +- internal/rpc/msg/seq.go | 5 +- internal/rpc/msg/server.go | 12 +- internal/rpc/msg/sync_msg.go | 15 +- internal/rpc/msg/utils.go | 5 +- internal/rpc/msg/verify.go | 16 +- internal/rpc/third/third.go | 21 +- internal/rpc/user/statistics.go | 5 +- internal/rpc/user/user.go | 54 ++- internal/tools/cron_task.go | 3 +- internal/tools/msg.go | 29 +- internal/tools/msg_test.go | 33 +- pkg/a2r/api2rpc.go | 5 +- pkg/apistruct/auth.go | 16 +- pkg/apistruct/conversation.go | 50 +- pkg/apistruct/friend.go | 24 +- pkg/apistruct/group.go | 72 +-- pkg/apistruct/manage.go | 38 +- pkg/apistruct/msg.go | 89 ++-- pkg/apistruct/public.go | 16 +- pkg/apistruct/third.go | 22 +- pkg/common/cmd/msg_gateway.go | 3 +- pkg/common/cmd/msg_transfer.go | 3 +- pkg/common/cmd/msg_utils.go | 3 +- pkg/common/cmd/root.go | 3 +- pkg/common/cmd/rpc.go | 10 +- pkg/common/config/config.go | 13 +- pkg/common/config/parse.go | 3 +- pkg/common/constant/constant.go | 3 +- pkg/common/convert/black.go | 6 +- pkg/common/convert/friend.go | 18 +- pkg/common/convert/group.go | 19 +- pkg/common/db/cache/black.go | 21 +- pkg/common/db/cache/conversation.go | 266 ++++++++--- pkg/common/db/cache/extend_msg_set.go | 37 +- pkg/common/db/cache/friend.go | 52 +- pkg/common/db/cache/group.go | 272 ++++++++--- pkg/common/db/cache/init_redis.go | 3 +- pkg/common/db/cache/meta_cache.go | 38 +- pkg/common/db/cache/msg.go | 129 ++++- pkg/common/db/cache/user.go | 58 ++- pkg/common/db/controller/auth.go | 9 +- pkg/common/db/controller/black.go | 17 +- pkg/common/db/controller/chatlog.go | 12 +- pkg/common/db/controller/conversation.go | 99 +++- pkg/common/db/controller/extend_msg.go | 116 ++++- pkg/common/db/controller/friend.go | 124 ++++- pkg/common/db/controller/group.go | 197 ++++++-- pkg/common/db/controller/msg.go | 452 +++++++++++++++--- pkg/common/db/controller/msg_test.go | 7 +- pkg/common/db/controller/storage.go | 23 +- pkg/common/db/controller/third.go | 8 +- pkg/common/db/controller/user.go | 11 +- pkg/common/db/obj/minio.go | 20 +- pkg/common/db/obj/obj.go | 8 +- pkg/common/db/ormutil/utils.go | 3 +- pkg/common/db/relation/black_model.go | 46 +- pkg/common/db/relation/chat_log_model.go | 15 +- pkg/common/db/relation/conversation_model.go | 184 +++++-- pkg/common/db/relation/friend_model.go | 117 ++++- .../db/relation/friend_request_model.go | 87 +++- pkg/common/db/relation/group_member_model.go | 75 ++- pkg/common/db/relation/group_model.go | 22 +- pkg/common/db/relation/group_request_model.go | 71 ++- pkg/common/db/relation/mysql_init.go | 25 +- pkg/common/db/relation/object_hash_model.go | 15 +- pkg/common/db/relation/object_info_model.go | 10 +- pkg/common/db/relation/object_put_model.go | 9 +- pkg/common/db/relation/user_model.go | 32 +- pkg/common/db/table/relation/black.go | 6 +- pkg/common/db/table/relation/chatlog.go | 28 +- pkg/common/db/table/relation/conversation.go | 45 +- pkg/common/db/table/relation/friend.go | 18 +- .../db/table/relation/friend_request.go | 12 +- pkg/common/db/table/relation/group.go | 22 +- pkg/common/db/table/relation/group_member.go | 16 +- pkg/common/db/table/relation/group_request.go | 12 +- pkg/common/db/table/relation/utils.go | 3 +- .../db/table/unrelation/extend_msg_set.go | 61 ++- pkg/common/db/table/unrelation/msg.go | 3 +- pkg/common/db/table/unrelation/super_group.go | 4 +- pkg/common/db/tx/mongo.go | 3 +- pkg/common/db/unrelation/extend_msg.go | 91 +++- pkg/common/db/unrelation/mongo.go | 10 +- pkg/common/db/unrelation/msg.go | 74 ++- pkg/common/db/unrelation/super_group.go | 61 ++- pkg/common/http/http_client.go | 35 +- pkg/common/log/sql_logger.go | 44 +- pkg/common/log/zap.go | 21 +- pkg/common/mw/gin.go | 31 +- pkg/common/mw/rpc_client_interceptor.go | 16 +- pkg/common/mw/rpc_server_interceptor.go | 35 +- pkg/common/network/ip.go | 3 +- pkg/common/tokenverify/jwt_token.go | 3 +- pkg/common/tokenverify/jwt_token_test.go | 3 +- pkg/discoveryregistry/zookeeper/discover.go | 28 +- pkg/discoveryregistry/zookeeper/register.go | 6 +- pkg/discoveryregistry/zookeeper/resolver.go | 30 +- pkg/discoveryregistry/zookeeper/zk.go | 7 +- pkg/proto/conversation/conversation.pb.go | 3 +- pkg/proto/friend/friend.pb.go | 3 +- pkg/proto/group/group.pb.go | 5 +- pkg/proto/msg/msg.pb.go | 5 +- pkg/proto/msggateway/msggateway.pb.go | 3 +- pkg/proto/push/push.pb.go | 3 +- pkg/proto/sdkws/sdkws.pb.go | 3 +- pkg/proto/user/user.pb.go | 5 +- pkg/rpcclient/auth.go | 3 +- pkg/rpcclient/conversation.go | 88 +++- pkg/rpcclient/friend.go | 13 +- pkg/rpcclient/group.go | 46 +- pkg/rpcclient/msg.go | 49 +- pkg/rpcclient/notification/conevrsation.go | 12 +- pkg/rpcclient/notification/extend_msg.go | 61 ++- pkg/rpcclient/notification/friend.go | 49 +- pkg/rpcclient/notification/group.go | 242 ++++++++-- pkg/rpcclient/push.go | 8 +- pkg/rpcclient/third.go | 3 +- pkg/rpcclient/user.go | 15 +- pkg/startrpc/start.go | 44 +- pkg/statistics/statistics.go | 16 +- pkg/utils/callback.go | 3 +- pkg/utils/utils.go | 7 +- scripts/make-rules/golang.mk | 3 +- test/mongo/cmd/main.go | 3 +- 170 files changed, 4841 insertions(+), 1304 deletions(-) diff --git a/cmd/api/main.go b/cmd/api/main.go index 6f56a7704..fb613b4da 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -52,8 +52,10 @@ func run(port int) error { fmt.Println("api start init discov client") var client discoveryregistry.SvcDiscoveryRegistry client, err = openKeeper.NewClient(config.Config.Zookeeper.ZkAddr, config.Config.Zookeeper.Schema, - openKeeper.WithFreq(time.Hour), openKeeper.WithUserNameAndPassword(config.Config.Zookeeper.Username, - config.Config.Zookeeper.Password), openKeeper.WithRoundRobin(), openKeeper.WithTimeout(10), openKeeper.WithLogger(log.NewZkLogger())) + openKeeper.WithFreq(time.Hour), openKeeper.WithUserNameAndPassword( + config.Config.Zookeeper.Username, + config.Config.Zookeeper.Password, + ), openKeeper.WithRoundRobin(), openKeeper.WithTimeout(10), openKeeper.WithLogger(log.NewZkLogger())) if err != nil { return err } diff --git a/internal/api/auth.go b/internal/api/auth.go index 3fdbc10ac..a9cf17707 100644 --- a/internal/api/auth.go +++ b/internal/api/auth.go @@ -1,11 +1,12 @@ package api import ( + "github.com/gin-gonic/gin" + "github.com/OpenIMSDK/Open-IM-Server/pkg/a2r" "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/auth" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" - "github.com/gin-gonic/gin" ) type AuthApi rpcclient.Auth diff --git a/internal/api/conversation.go b/internal/api/conversation.go index 22798b2a7..767cc8517 100644 --- a/internal/api/conversation.go +++ b/internal/api/conversation.go @@ -1,11 +1,12 @@ package api import ( + "github.com/gin-gonic/gin" + "github.com/OpenIMSDK/Open-IM-Server/pkg/a2r" "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/conversation" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" - "github.com/gin-gonic/gin" ) type ConversationApi rpcclient.Conversation diff --git a/internal/api/custom_validator.go b/internal/api/custom_validator.go index 541702677..b434c792e 100644 --- a/internal/api/custom_validator.go +++ b/internal/api/custom_validator.go @@ -1,8 +1,9 @@ package api import ( - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/go-playground/validator/v10" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" ) func RequiredIf(fl validator.FieldLevel) bool { diff --git a/internal/api/msg.go b/internal/api/msg.go index ea3e63d7e..6f0cf7033 100644 --- a/internal/api/msg.go +++ b/internal/api/msg.go @@ -1,6 +1,11 @@ package api import ( + "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" + "github.com/mitchellh/mapstructure" + "google.golang.org/protobuf/proto" + "github.com/OpenIMSDK/Open-IM-Server/pkg/a2r" "github.com/OpenIMSDK/Open-IM-Server/pkg/apiresp" "github.com/OpenIMSDK/Open-IM-Server/pkg/apistruct" @@ -12,10 +17,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "github.com/gin-gonic/gin" - "github.com/go-playground/validator/v10" - "github.com/mitchellh/mapstructure" - "google.golang.org/protobuf/proto" ) type MessageApi struct { diff --git a/internal/api/route.go b/internal/api/route.go index fb8c2d9a0..8a786dc6f 100644 --- a/internal/api/route.go +++ b/internal/api/route.go @@ -3,17 +3,18 @@ package api import ( "context" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mw" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/prome" - "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "github.com/go-playground/validator/v10" "github.com/redis/go-redis/v9" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mw" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/prome" + "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" ) func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.UniversalClient) *gin.Engine { diff --git a/internal/api/statistics.go b/internal/api/statistics.go index 9d2006212..4a85dd5ef 100644 --- a/internal/api/statistics.go +++ b/internal/api/statistics.go @@ -1,11 +1,12 @@ package api import ( + "github.com/gin-gonic/gin" + "github.com/OpenIMSDK/Open-IM-Server/pkg/a2r" "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/user" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" - "github.com/gin-gonic/gin" ) type StatisticsApi rpcclient.User diff --git a/internal/api/third.go b/internal/api/third.go index b04589503..559869f1e 100644 --- a/internal/api/third.go +++ b/internal/api/third.go @@ -5,6 +5,8 @@ import ( "net/http" "strconv" + "github.com/gin-gonic/gin" + "github.com/OpenIMSDK/Open-IM-Server/pkg/a2r" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" @@ -12,7 +14,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/third" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" - "github.com/gin-gonic/gin" ) type ThirdApi rpcclient.Third @@ -65,7 +66,10 @@ func (o *ThirdApi) GetURL(c *gin.Context) { } attachment, _ := strconv.ParseBool(c.Query("attachment")) c.Set(constant.OperationID, operationID) - resp, err := o.Client.GetUrl(mcontext.SetOperationID(c, operationID), &third.GetUrlReq{Name: name, Expires: expires, Attachment: attachment}) + resp, err := o.Client.GetUrl( + mcontext.SetOperationID(c, operationID), + &third.GetUrlReq{Name: name, Expires: expires, Attachment: attachment}, + ) if err != nil { if errs.ErrArgs.Is(err) { c.String(http.StatusBadRequest, err.Error()) diff --git a/internal/api/user.go b/internal/api/user.go index 299a33638..d3b7bae0e 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -1,6 +1,8 @@ package api import ( + "github.com/gin-gonic/gin" + "github.com/OpenIMSDK/Open-IM-Server/pkg/a2r" "github.com/OpenIMSDK/Open-IM-Server/pkg/apiresp" "github.com/OpenIMSDK/Open-IM-Server/pkg/apistruct" @@ -9,7 +11,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/user" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" - "github.com/gin-gonic/gin" ) type UserApi rpcclient.User diff --git a/internal/msggateway/callback.go b/internal/msggateway/callback.go index 557e65ea6..79bcdac97 100644 --- a/internal/msggateway/callback.go +++ b/internal/msggateway/callback.go @@ -78,7 +78,8 @@ func CallbackUserKickOff(ctx context.Context, userID string, platformID int) err return http.CallBackPostReturn(ctx, url(), req, resp, config.Config.Callback.CallbackUserOffline) } -//func callbackUserOnline(operationID, userID string, platformID int, token string, isAppBackground bool, connID string) cbApi.CommonCallbackResp { +// func callbackUserOnline(operationID, userID string, platformID int, token string, isAppBackground bool, connID +// string) cbApi.CommonCallbackResp { // callbackResp := cbApi.CommonCallbackResp{OperationID: operationID} // if !config.Config.Callback.CallbackUserOnline.Enable { // return callbackResp @@ -99,7 +100,9 @@ func CallbackUserKickOff(ctx context.Context, userID string, platformID int) err // ConnID: connID, // } // callbackUserOnlineResp := &cbApi.CallbackUserOnlineResp{CommonCallbackResp: &callbackResp} -// if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, constant.CallbackUserOnlineCommand, callbackUserOnlineReq, callbackUserOnlineResp, config.Config.Callback.CallbackUserOnline.CallbackTimeOut); err != nil { +// if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, constant.CallbackUserOnlineCommand, +// callbackUserOnlineReq, callbackUserOnlineResp, config.Config.Callback.CallbackUserOnline.CallbackTimeOut); err != nil +// { // callbackResp.ErrCode = http2.StatusInternalServerError // callbackResp.ErrMsg = err.Error() // } @@ -124,7 +127,9 @@ func CallbackUserKickOff(ctx context.Context, userID string, platformID int) err // ConnID: connID, // } // callbackUserOfflineResp := &cbApi.CallbackUserOfflineResp{CommonCallbackResp: &callbackResp} -// if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, constant.CallbackUserOfflineCommand, callbackOfflineReq, callbackUserOfflineResp, config.Config.Callback.CallbackUserOffline.CallbackTimeOut); err != nil { +// if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, constant.CallbackUserOfflineCommand, +// callbackOfflineReq, callbackUserOfflineResp, config.Config.Callback.CallbackUserOffline.CallbackTimeOut); err != nil +// { // callbackResp.ErrCode = http2.StatusInternalServerError // callbackResp.ErrMsg = err.Error() // } @@ -148,7 +153,9 @@ func CallbackUserKickOff(ctx context.Context, userID string, platformID int) err // Seq: int(time.Now().UnixNano() / 1e6), // } // callbackUserKickOffResp := &cbApi.CallbackUserKickOffResp{CommonCallbackResp: &callbackResp} -// if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, constant.CallbackUserKickOffCommand, callbackUserKickOffReq, callbackUserKickOffResp, config.Config.Callback.CallbackUserOffline.CallbackTimeOut); err != nil { +// if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, constant.CallbackUserKickOffCommand, +// callbackUserKickOffReq, callbackUserKickOffResp, config.Config.Callback.CallbackUserOffline.CallbackTimeOut); err != +// nil { // callbackResp.ErrCode = http2.StatusInternalServerError // callbackResp.ErrMsg = err.Error() // } diff --git a/internal/msggateway/client.go b/internal/msggateway/client.go index fa72297b8..ef92438e6 100644 --- a/internal/msggateway/client.go +++ b/internal/msggateway/client.go @@ -7,13 +7,14 @@ import ( "runtime/debug" "sync" + "google.golang.org/protobuf/proto" + "github.com/OpenIMSDK/Open-IM-Server/pkg/apiresp" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "google.golang.org/protobuf/proto" ) var ErrConnClosed = errors.New("conn has closed") @@ -65,7 +66,13 @@ func newClient(ctx *UserConnContext, conn LongConn, isCompress bool) *Client { ctx: ctx, } } -func (c *Client) ResetClient(ctx *UserConnContext, conn LongConn, isBackground, isCompress bool, longConnServer LongConnServer) { + +func (c *Client) ResetClient( + ctx *UserConnContext, + conn LongConn, + isBackground, isCompress bool, + longConnServer LongConnServer, +) { c.w = new(sync.Mutex) c.conn = conn c.PlatformID = utils.StringToInt(ctx.GetPlatformID()) @@ -145,7 +152,9 @@ func (c *Client) handleMessage(message []byte) error { if binaryReq.SendID != c.UserID { return utils.Wrap(errors.New("exception conn userID not same to req userID"), binaryReq.String()) } - ctx := mcontext.WithMustInfoCtx([]string{binaryReq.OperationID, binaryReq.SendID, constant.PlatformIDToName(c.PlatformID), c.ctx.GetConnID()}) + ctx := mcontext.WithMustInfoCtx( + []string{binaryReq.OperationID, binaryReq.SendID, constant.PlatformIDToName(c.PlatformID), c.ctx.GetConnID()}, + ) log.ZDebug(ctx, "gateway req message", "req", binaryReq.String()) var messageErr error var resp []byte @@ -163,7 +172,12 @@ func (c *Client) handleMessage(message []byte) error { case WsSetBackgroundStatus: resp, messageErr = c.setAppBackgroundStatus(ctx, binaryReq) default: - return fmt.Errorf("ReqIdentifier failed,sendID:%s,msgIncr:%s,reqIdentifier:%d", binaryReq.SendID, binaryReq.MsgIncr, binaryReq.ReqIdentifier) + return fmt.Errorf( + "ReqIdentifier failed,sendID:%s,msgIncr:%s,reqIdentifier:%d", + binaryReq.SendID, + binaryReq.MsgIncr, + binaryReq.ReqIdentifier, + ) } c.replyMessage(ctx, &binaryReq, messageErr, resp) return nil diff --git a/internal/msggateway/hub_server.go b/internal/msggateway/hub_server.go index ab8f9af26..6916f5309 100644 --- a/internal/msggateway/hub_server.go +++ b/internal/msggateway/hub_server.go @@ -5,6 +5,8 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" + "google.golang.org/grpc" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" @@ -15,7 +17,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msggateway" "github.com/OpenIMSDK/Open-IM-Server/pkg/startrpc" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "google.golang.org/grpc" ) func (s *Server) InitServer(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { @@ -31,7 +32,12 @@ func (s *Server) InitServer(client discoveryregistry.SvcDiscoveryRegistry, serve } func (s *Server) Start() error { - return startrpc.Start(s.rpcPort, config.Config.RpcRegisterName.OpenImMessageGatewayName, s.prometheusPort, s.InitServer) + return startrpc.Start( + s.rpcPort, + config.Config.RpcRegisterName.OpenImMessageGatewayName, + s.prometheusPort, + s.InitServer, + ) } type Server struct { @@ -46,14 +52,24 @@ func (s *Server) SetLongConnServer(LongConnServer LongConnServer) { } func NewServer(rpcPort int, longConnServer LongConnServer) *Server { - return &Server{rpcPort: rpcPort, LongConnServer: longConnServer, pushTerminal: []int{constant.IOSPlatformID, constant.AndroidPlatformID}} + return &Server{ + rpcPort: rpcPort, + LongConnServer: longConnServer, + pushTerminal: []int{constant.IOSPlatformID, constant.AndroidPlatformID}, + } } -func (s *Server) OnlinePushMsg(context context.Context, req *msggateway.OnlinePushMsgReq) (*msggateway.OnlinePushMsgResp, error) { +func (s *Server) OnlinePushMsg( + context context.Context, + req *msggateway.OnlinePushMsgReq, +) (*msggateway.OnlinePushMsgResp, error) { panic("implement me") } -func (s *Server) GetUsersOnlineStatus(ctx context.Context, req *msggateway.GetUsersOnlineStatusReq) (*msggateway.GetUsersOnlineStatusResp, error) { +func (s *Server) GetUsersOnlineStatus( + ctx context.Context, + req *msggateway.GetUsersOnlineStatusReq, +) (*msggateway.GetUsersOnlineStatusResp, error) { if !tokenverify.IsAppManagerUid(ctx) { return nil, errs.ErrNoPermission.Wrap("only app manager") } @@ -83,11 +99,17 @@ func (s *Server) GetUsersOnlineStatus(ctx context.Context, req *msggateway.GetUs return &resp, nil } -func (s *Server) OnlineBatchPushOneMsg(ctx context.Context, req *msggateway.OnlineBatchPushOneMsgReq) (*msggateway.OnlineBatchPushOneMsgResp, error) { +func (s *Server) OnlineBatchPushOneMsg( + ctx context.Context, + req *msggateway.OnlineBatchPushOneMsgReq, +) (*msggateway.OnlineBatchPushOneMsgResp, error) { panic("implement me") } -func (s *Server) SuperGroupOnlineBatchPushOneMsg(ctx context.Context, req *msggateway.OnlineBatchPushOneMsgReq) (*msggateway.OnlineBatchPushOneMsgResp, error) { +func (s *Server) SuperGroupOnlineBatchPushOneMsg( + ctx context.Context, + req *msggateway.OnlineBatchPushOneMsgReq, +) (*msggateway.OnlineBatchPushOneMsgResp, error) { var singleUserResult []*msggateway.SingleMsgToUserResults for _, v := range req.PushToUserIDs { var resp []*msggateway.SingleMsgToUserPlatform @@ -108,7 +130,8 @@ func (s *Server) SuperGroupOnlineBatchPushOneMsg(ctx context.Context, req *msgga RecvID: v, RecvPlatFormID: int32(client.PlatformID), } - if !client.IsBackground || (client.IsBackground == true && client.PlatformID != constant.IOSPlatformID) { + if !client.IsBackground || + (client.IsBackground == true && client.PlatformID != constant.IOSPlatformID) { err := client.PushMessage(ctx, req.MsgData) if err != nil { temp.ResultCode = -2 @@ -135,7 +158,10 @@ func (s *Server) SuperGroupOnlineBatchPushOneMsg(ctx context.Context, req *msgga }, nil } -func (s *Server) KickUserOffline(ctx context.Context, req *msggateway.KickUserOfflineReq) (*msggateway.KickUserOfflineResp, error) { +func (s *Server) KickUserOffline( + ctx context.Context, + req *msggateway.KickUserOfflineReq, +) (*msggateway.KickUserOfflineResp, error) { for _, v := range req.KickUserIDList { if clients, _, ok := s.LongConnServer.GetUserPlatformCons(v, int(req.PlatformID)); ok { for _, client := range clients { @@ -149,7 +175,10 @@ func (s *Server) KickUserOffline(ctx context.Context, req *msggateway.KickUserOf return &msggateway.KickUserOfflineResp{}, nil } -func (s *Server) MultiTerminalLoginCheck(ctx context.Context, req *msggateway.MultiTerminalLoginCheckReq) (*msggateway.MultiTerminalLoginCheckResp, error) { +func (s *Server) MultiTerminalLoginCheck( + ctx context.Context, + req *msggateway.MultiTerminalLoginCheckReq, +) (*msggateway.MultiTerminalLoginCheckResp, error) { //TODO implement me panic("implement me") } diff --git a/internal/msggateway/init.go b/internal/msggateway/init.go index 2e001667a..c21ac02ce 100644 --- a/internal/msggateway/init.go +++ b/internal/msggateway/init.go @@ -8,7 +8,14 @@ import ( ) func RunWsAndServer(rpcPort, wsPort, prometheusPort int) error { - fmt.Println("start rpc/msg_gateway server, port: ", rpcPort, wsPort, prometheusPort, ", OpenIM version: ", config.Version) + fmt.Println( + "start rpc/msg_gateway server, port: ", + rpcPort, + wsPort, + prometheusPort, + ", OpenIM version: ", + config.Version, + ) longServer, err := NewWsServer( WithPort(wsPort), WithMaxConnNum(int64(config.Config.LongConnSvr.WebsocketMaxConnNum)), diff --git a/internal/msggateway/message_handler.go b/internal/msggateway/message_handler.go index 5430c8175..73b22b83d 100644 --- a/internal/msggateway/message_handler.go +++ b/internal/msggateway/message_handler.go @@ -6,20 +6,21 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/push" + "github.com/go-playground/validator/v10" + "google.golang.org/protobuf/proto" + "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "github.com/go-playground/validator/v10" - "google.golang.org/protobuf/proto" ) type Req struct { ReqIdentifier int32 `json:"reqIdentifier" validate:"required"` - Token string `json:"token" ` - SendID string `json:"sendID" validate:"required"` - OperationID string `json:"operationID" validate:"required"` - MsgIncr string `json:"msgIncr" validate:"required"` + Token string `json:"token"` + SendID string `json:"sendID" validate:"required"` + OperationID string `json:"operationID" validate:"required"` + MsgIncr string `json:"msgIncr" validate:"required"` Data []byte `json:"data"` } @@ -160,7 +161,8 @@ func (g GrpcHandler) SetUserDeviceBackground(_ context.Context, data Req) ([]byt return nil, req.IsBackground, nil } -//func (g GrpcHandler) call[T any](ctx context.Context, data Req, m proto.Message, rpc func(ctx context.Context, req proto.Message)) ([]byte, error) { +// func (g GrpcHandler) call[T any](ctx context.Context, data Req, m proto.Message, rpc func(ctx context.Context, req +// proto.Message)) ([]byte, error) { // if err := proto.Unmarshal(data.Data, m); err != nil { // return nil, err // } diff --git a/internal/msggateway/n_ws_server.go b/internal/msggateway/n_ws_server.go index dcf183015..c1ec5d076 100644 --- a/internal/msggateway/n_ws_server.go +++ b/internal/msggateway/n_ws_server.go @@ -13,14 +13,16 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" - "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/redis/go-redis/v9" + "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" + + "github.com/go-playground/validator/v10" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/tokenverify" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "github.com/go-playground/validator/v10" ) type LongConnServer interface { @@ -169,7 +171,14 @@ func (ws *WsServer) registerClient(client *Client) { atomic.AddInt64(&ws.onlineUserConnNum, 1) } } - log.ZInfo(client.ctx, "user online", "online user Num", ws.onlineUserNum, "online user conn Num", ws.onlineUserConnNum) + log.ZInfo( + client.ctx, + "user online", + "online user Num", + ws.onlineUserNum, + "online user conn Num", + ws.onlineUserConnNum, + ) } func getRemoteAdders(client []*Client) string { var ret string @@ -200,18 +209,47 @@ func (ws *WsServer) multiTerminalLoginChecker(info *kickHandler) { log.ZWarn(c.ctx, "KickOnlineMessage", err) } } - m, err := ws.cache.GetTokensWithoutError(info.newClient.ctx, info.newClient.UserID, info.newClient.PlatformID) + m, err := ws.cache.GetTokensWithoutError( + info.newClient.ctx, + info.newClient.UserID, + info.newClient.PlatformID, + ) if err != nil && err != redis.Nil { - log.ZWarn(info.newClient.ctx, "get token from redis err", err, "userID", info.newClient.UserID, "platformID", info.newClient.PlatformID) + log.ZWarn( + info.newClient.ctx, + "get token from redis err", + err, + "userID", + info.newClient.UserID, + "platformID", + info.newClient.PlatformID, + ) return } if m == nil { - log.ZWarn(info.newClient.ctx, "m is nil", errors.New("m is nil"), "userID", info.newClient.UserID, "platformID", info.newClient.PlatformID) + log.ZWarn( + info.newClient.ctx, + "m is nil", + errors.New("m is nil"), + "userID", + info.newClient.UserID, + "platformID", + info.newClient.PlatformID, + ) return } - log.ZDebug(info.newClient.ctx, "get token from redis", "userID", info.newClient.UserID, "platformID", info.newClient.PlatformID, "tokenMap", m) + log.ZDebug( + info.newClient.ctx, + "get token from redis", + "userID", + info.newClient.UserID, + "platformID", + info.newClient.PlatformID, + "tokenMap", + m, + ) - for k, _ := range m { + for k := range m { if k != info.newClient.ctx.GetToken() { m[k] = constant.KickedToken } @@ -219,7 +257,15 @@ func (ws *WsServer) multiTerminalLoginChecker(info *kickHandler) { log.ZDebug(info.newClient.ctx, "set token map is ", "token map", m, "userID", info.newClient.UserID) err = ws.cache.SetTokenMapByUidPid(info.newClient.ctx, info.newClient.UserID, info.newClient.PlatformID, m) if err != nil { - log.ZWarn(info.newClient.ctx, "SetTokenMapByUidPid err", err, "userID", info.newClient.UserID, "platformID", info.newClient.PlatformID) + log.ZWarn( + info.newClient.ctx, + "SetTokenMapByUidPid err", + err, + "userID", + info.newClient.UserID, + "platformID", + info.newClient.PlatformID, + ) return } } @@ -233,7 +279,16 @@ func (ws *WsServer) unregisterClient(client *Client) { atomic.AddInt64(&ws.onlineUserNum, -1) } atomic.AddInt64(&ws.onlineUserConnNum, -1) - log.ZInfo(client.ctx, "user offline", "close reason", client.closedErr, "online user Num", ws.onlineUserNum, "online user conn Num", ws.onlineUserConnNum) + log.ZInfo( + client.ctx, + "user offline", + "close reason", + client.closedErr, + "online user Num", + ws.onlineUserNum, + "online user conn Num", + ws.onlineUserConnNum, + ) } func (ws *WsServer) wsHandler(w http.ResponseWriter, r *http.Request) { diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go index 19dda963e..5b82a7d30 100644 --- a/internal/msgtransfer/init.go +++ b/internal/msgtransfer/init.go @@ -5,6 +5,9 @@ import ( "sync" "time" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/controller" @@ -17,8 +20,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/common/prome" openKeeper "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry/zookeeper" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" ) type MsgTransfer struct { @@ -47,9 +48,18 @@ func StartTransfer(prometheusPort int) error { if err := mongo.CreateMsgIndex(); err != nil { return err } - client, err := openKeeper.NewClient(config.Config.Zookeeper.ZkAddr, config.Config.Zookeeper.Schema, - openKeeper.WithFreq(time.Hour), openKeeper.WithRoundRobin(), openKeeper.WithUserNameAndPassword(config.Config.Zookeeper.Username, - config.Config.Zookeeper.Password), openKeeper.WithTimeout(10), openKeeper.WithLogger(log.NewZkLogger())) + client, err := openKeeper.NewClient( + config.Config.Zookeeper.ZkAddr, + config.Config.Zookeeper.Schema, + openKeeper.WithFreq( + time.Hour, + ), + openKeeper.WithRoundRobin(), + openKeeper.WithUserNameAndPassword(config.Config.Zookeeper.Username, + config.Config.Zookeeper.Password), + openKeeper.WithTimeout(10), + openKeeper.WithLogger(log.NewZkLogger()), + ) if err != nil { return err } @@ -66,7 +76,13 @@ func StartTransfer(prometheusPort int) error { msgDatabase := controller.NewCommonMsgDatabase(msgDocModel, msgModel) conversationRpcClient := rpcclient.NewConversationRpcClient(client) groupRpcClient := rpcclient.NewGroupRpcClient(client) - msgTransfer := NewMsgTransfer(chatLogDatabase, extendMsgDatabase, msgDatabase, &conversationRpcClient, &groupRpcClient) + msgTransfer := NewMsgTransfer( + chatLogDatabase, + extendMsgDatabase, + msgDatabase, + &conversationRpcClient, + &groupRpcClient, + ) msgTransfer.initPrometheus() return msgTransfer.Start(prometheusPort) } @@ -74,8 +90,14 @@ func StartTransfer(prometheusPort int) error { func NewMsgTransfer(chatLogDatabase controller.ChatLogDatabase, extendMsgDatabase controller.ExtendMsgDatabase, msgDatabase controller.CommonMsgDatabase, conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient) *MsgTransfer { - return &MsgTransfer{persistentCH: NewPersistentConsumerHandler(chatLogDatabase), historyCH: NewOnlineHistoryRedisConsumerHandler(msgDatabase, conversationRpcClient, groupRpcClient), - historyMongoCH: NewOnlineHistoryMongoConsumerHandler(msgDatabase), modifyCH: NewModifyMsgConsumerHandler(extendMsgDatabase)} + return &MsgTransfer{ + persistentCH: NewPersistentConsumerHandler(chatLogDatabase), + historyCH: NewOnlineHistoryRedisConsumerHandler(msgDatabase, conversationRpcClient, groupRpcClient), + historyMongoCH: NewOnlineHistoryMongoConsumerHandler( + msgDatabase, + ), + modifyCH: NewModifyMsgConsumerHandler(extendMsgDatabase), + } } func (m *MsgTransfer) initPrometheus() { diff --git a/internal/msgtransfer/modify_msg_handler.go b/internal/msgtransfer/modify_msg_handler.go index 0b9ad2aff..e4130addb 100644 --- a/internal/msgtransfer/modify_msg_handler.go +++ b/internal/msgtransfer/modify_msg_handler.go @@ -4,6 +4,8 @@ import ( "context" "encoding/json" + "github.com/Shopify/sarama" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/controller" @@ -14,7 +16,6 @@ import ( pbMsg "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "github.com/Shopify/sarama" "google.golang.org/protobuf/proto" ) @@ -41,7 +42,18 @@ func (mmc *ModifyMsgConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSessi claim sarama.ConsumerGroupClaim) error { for msg := range claim.Messages() { ctx := mmc.modifyMsgConsumerGroup.GetContextFromMsg(msg) - log.ZDebug(ctx, "kafka get info to mysql", "ModifyMsgConsumerHandler", msg.Topic, "msgPartition", msg.Partition, "msg", string(msg.Value), "key", string(msg.Key)) + log.ZDebug( + ctx, + "kafka get info to mysql", + "ModifyMsgConsumerHandler", + msg.Topic, + "msgPartition", + msg.Partition, + "msg", + string(msg.Value), + "key", + string(msg.Key), + ) if len(msg.Value) != 0 { mmc.ModifyMsg(ctx, msg, string(msg.Key), sess) } else { @@ -52,7 +64,12 @@ func (mmc *ModifyMsgConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSessi return nil } -func (mmc *ModifyMsgConsumerHandler) ModifyMsg(ctx context.Context, cMsg *sarama.ConsumerMessage, msgKey string, _ sarama.ConsumerGroupSession) { +func (mmc *ModifyMsgConsumerHandler) ModifyMsg( + ctx context.Context, + cMsg *sarama.ConsumerMessage, + msgKey string, + _ sarama.ConsumerGroupSession, +) { msgFromMQ := pbMsg.MsgDataToModifyByMQ{} operationID := mcontext.GetOperationID(ctx) err := proto.Unmarshal(cMsg.Value, &msgFromMQ) @@ -92,7 +109,8 @@ func (mmc *ModifyMsgConsumerHandler) ModifyMsg(ctx context.Context, cMsg *sarama } if err := mmc.extendMsgDatabase.InsertExtendMsg(ctx, notification.ConversationID, notification.SessionType, &extendMsg); err != nil { - // log.ZError(ctx, "MsgFirstModify InsertExtendMsg failed", notification.ConversationID, notification.SessionType, extendMsg, err.Error()) + // log.ZError(ctx, "MsgFirstModify InsertExtendMsg failed", notification.ConversationID, + // notification.SessionType, extendMsg, err.Error()) continue } } else { diff --git a/internal/msgtransfer/online_history_msg_handler.go b/internal/msgtransfer/online_history_msg_handler.go index 7f776d5ee..ee7c3bd95 100644 --- a/internal/msgtransfer/online_history_msg_handler.go +++ b/internal/msgtransfer/online_history_msg_handler.go @@ -9,6 +9,10 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" + "github.com/Shopify/sarama" + "github.com/go-redis/redis" + "google.golang.org/protobuf/proto" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/controller" @@ -18,9 +22,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "github.com/Shopify/sarama" - "github.com/go-redis/redis" - "google.golang.org/protobuf/proto" ) const ConsumerMsgs = 3 @@ -63,7 +64,11 @@ type OnlineHistoryRedisConsumerHandler struct { groupRpcClient *rpcclient.GroupRpcClient } -func NewOnlineHistoryRedisConsumerHandler(database controller.CommonMsgDatabase, conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient) *OnlineHistoryRedisConsumerHandler { +func NewOnlineHistoryRedisConsumerHandler( + database controller.CommonMsgDatabase, + conversationRpcClient *rpcclient.ConversationRpcClient, + groupRpcClient *rpcclient.GroupRpcClient, +) *OnlineHistoryRedisConsumerHandler { var och OnlineHistoryRedisConsumerHandler och.msgDatabase = database och.msgDistributionCh = make(chan Cmd2Value) //no buffer channel @@ -77,7 +82,8 @@ func NewOnlineHistoryRedisConsumerHandler(database controller.CommonMsgDatabase, och.historyConsumerGroup = kafka.NewMConsumerGroup(&kafka.MConsumerGroupConfig{KafkaVersion: sarama.V2_0_0_0, OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false}, []string{config.Config.Kafka.LatestMsgToRedis.Topic}, config.Config.Kafka.Addr, config.Config.Kafka.ConsumerGroupID.MsgToRedis) - //statistics.NewStatistics(&och.singleMsgSuccessCount, config.Config.ModuleName.MsgTransferName, fmt.Sprintf("%d second singleMsgCount insert to mongo", constant.StatisticsTimeInterval), constant.StatisticsTimeInterval) + // statistics.NewStatistics(&och.singleMsgSuccessCount, config.Config.ModuleName.MsgTransferName, fmt.Sprintf("%d + // second singleMsgCount insert to mongo", constant.StatisticsTimeInterval), constant.StatisticsTimeInterval) return &och } @@ -90,16 +96,53 @@ func (och *OnlineHistoryRedisConsumerHandler) Run(channelID int) { msgChannelValue := cmd.Value.(MsgChannelValue) ctxMsgList := msgChannelValue.ctxMsgList ctx := msgChannelValue.ctx - log.ZDebug(ctx, "msg arrived channel", "channel id", channelID, "msgList length", len(ctxMsgList), "uniqueKey", msgChannelValue.uniqueKey) - storageMsgList, notStorageMsgList, storageNotificationList, notStorageNotificationList, modifyMsgList := och.getPushStorageMsgList(ctxMsgList) - log.ZDebug(ctx, "msg lens", "storageMsgList", len(storageMsgList), "notStorageMsgList", len(notStorageMsgList), - "storageNotificationList", len(storageNotificationList), "notStorageNotificationList", len(notStorageNotificationList), "modifyMsgList", len(modifyMsgList)) + log.ZDebug( + ctx, + "msg arrived channel", + "channel id", + channelID, + "msgList length", + len(ctxMsgList), + "uniqueKey", + msgChannelValue.uniqueKey, + ) + storageMsgList, notStorageMsgList, storageNotificationList, notStorageNotificationList, modifyMsgList := och.getPushStorageMsgList( + ctxMsgList, + ) + log.ZDebug( + ctx, + "msg lens", + "storageMsgList", + len(storageMsgList), + "notStorageMsgList", + len(notStorageMsgList), + "storageNotificationList", + len(storageNotificationList), + "notStorageNotificationList", + len(notStorageNotificationList), + "modifyMsgList", + len(modifyMsgList), + ) conversationIDMsg := utils.GetChatConversationIDByMsg(ctxMsgList[0].message) conversationIDNotification := utils.GetNotificationConversationID(ctxMsgList[0].message) och.handleMsg(ctx, msgChannelValue.uniqueKey, conversationIDMsg, storageMsgList, notStorageMsgList) - och.handleNotification(ctx, msgChannelValue.uniqueKey, conversationIDNotification, storageNotificationList, notStorageNotificationList) + och.handleNotification( + ctx, + msgChannelValue.uniqueKey, + conversationIDNotification, + storageNotificationList, + notStorageNotificationList, + ) if err := och.msgDatabase.MsgToModifyMQ(ctx, msgChannelValue.uniqueKey, conversationIDNotification, modifyMsgList); err != nil { - log.ZError(ctx, "msg to modify mq error", err, "uniqueKey", msgChannelValue.uniqueKey, "modifyMsgList", modifyMsgList) + log.ZError( + ctx, + "msg to modify mq error", + err, + "uniqueKey", + msgChannelValue.uniqueKey, + "modifyMsgList", + modifyMsgList, + ) } } } @@ -107,7 +150,9 @@ func (och *OnlineHistoryRedisConsumerHandler) Run(channelID int) { } // 获取消息/通知 存储的消息列表, 不存储并且推送的消息列表, -func (och *OnlineHistoryRedisConsumerHandler) getPushStorageMsgList(totalMsgs []*ContextMsg) (storageMsgList, notStorageMsgList, storageNotificatoinList, notStorageNotificationList, modifyMsgList []*sdkws.MsgData) { +func (och *OnlineHistoryRedisConsumerHandler) getPushStorageMsgList( + totalMsgs []*ContextMsg, +) (storageMsgList, notStorageMsgList, storageNotificatoinList, notStorageNotificationList, modifyMsgList []*sdkws.MsgData) { isStorage := func(msg *sdkws.MsgData) bool { options2 := utils.Options(msg.Options) if options2.IsHistory() { @@ -130,11 +175,17 @@ func (och *OnlineHistoryRedisConsumerHandler) getPushStorageMsgList(totalMsgs [] msg.Options = utils.NewMsgOptions() } if options.IsOfflinePush() { - v.message.Options = utils.WithOptions(utils.Options(v.message.Options), utils.WithOfflinePush(false)) + v.message.Options = utils.WithOptions( + utils.Options(v.message.Options), + utils.WithOfflinePush(false), + ) msg.Options = utils.WithOptions(utils.Options(msg.Options), utils.WithOfflinePush(true)) } if options.IsUnreadCount() { - v.message.Options = utils.WithOptions(utils.Options(v.message.Options), utils.WithUnreadCount(false)) + v.message.Options = utils.WithOptions( + utils.Options(v.message.Options), + utils.WithUnreadCount(false), + ) msg.Options = utils.WithOptions(utils.Options(msg.Options), utils.WithUnreadCount(true)) } storageMsgList = append(storageMsgList, msg) @@ -151,19 +202,32 @@ func (och *OnlineHistoryRedisConsumerHandler) getPushStorageMsgList(totalMsgs [] notStorageMsgList = append(notStorageMsgList, v.message) } } - if v.message.ContentType == constant.ReactionMessageModifier || v.message.ContentType == constant.ReactionMessageDeleter { + if v.message.ContentType == constant.ReactionMessageModifier || + v.message.ContentType == constant.ReactionMessageDeleter { modifyMsgList = append(modifyMsgList, v.message) } } return } -func (och *OnlineHistoryRedisConsumerHandler) handleNotification(ctx context.Context, key, conversationID string, storageList, notStorageList []*sdkws.MsgData) { +func (och *OnlineHistoryRedisConsumerHandler) handleNotification( + ctx context.Context, + key, conversationID string, + storageList, notStorageList []*sdkws.MsgData, +) { och.toPushTopic(ctx, key, conversationID, notStorageList) if len(storageList) > 0 { lastSeq, _, err := och.msgDatabase.BatchInsertChat2Cache(ctx, conversationID, storageList) if err != nil { - log.ZError(ctx, "notification batch insert to redis error", err, "conversationID", conversationID, "storageList", storageList) + log.ZError( + ctx, + "notification batch insert to redis error", + err, + "conversationID", + conversationID, + "storageList", + storageList, + ) return } log.ZDebug(ctx, "success to next topic", "conversationID", conversationID) @@ -172,13 +236,21 @@ func (och *OnlineHistoryRedisConsumerHandler) handleNotification(ctx context.Con } } -func (och *OnlineHistoryRedisConsumerHandler) toPushTopic(ctx context.Context, key, conversationID string, msgs []*sdkws.MsgData) { +func (och *OnlineHistoryRedisConsumerHandler) toPushTopic( + ctx context.Context, + key, conversationID string, + msgs []*sdkws.MsgData, +) { for _, v := range msgs { och.msgDatabase.MsgToPushMQ(ctx, key, conversationID, v) } } -func (och *OnlineHistoryRedisConsumerHandler) handleMsg(ctx context.Context, key, conversationID string, storageList, notStorageList []*sdkws.MsgData) { +func (och *OnlineHistoryRedisConsumerHandler) handleMsg( + ctx context.Context, + key, conversationID string, + storageList, notStorageList []*sdkws.MsgData, +) { och.toPushTopic(ctx, key, conversationID, notStorageList) if len(storageList) > 0 { lastSeq, isNewConversation, err := och.msgDatabase.BatchInsertChat2Cache(ctx, conversationID, storageList) @@ -240,11 +312,26 @@ func (och *OnlineHistoryRedisConsumerHandler) MessagesDistributionHandle() { for i, header := range consumerMessages[i].Headers { arr = append(arr, strconv.Itoa(i), string(header.Key), string(header.Value)) } - log.ZInfo(ctx, "consumer.kafka.GetContextWithMQHeader", "len", len(consumerMessages[i].Headers), "header", strings.Join(arr, ", ")) + log.ZInfo( + ctx, + "consumer.kafka.GetContextWithMQHeader", + "len", + len(consumerMessages[i].Headers), + "header", + strings.Join(arr, ", "), + ) ctxMsg.ctx = kafka.GetContextWithMQHeader(consumerMessages[i].Headers) ctxMsg.message = msgFromMQ - log.ZDebug(ctx, "single msg come to distribution center", "message", msgFromMQ, "key", string(consumerMessages[i].Key)) - //aggregationMsgs[string(consumerMessages[i].Key)] = append(aggregationMsgs[string(consumerMessages[i].Key)], ctxMsg) + log.ZDebug( + ctx, + "single msg come to distribution center", + "message", + msgFromMQ, + "key", + string(consumerMessages[i].Key), + ) + // aggregationMsgs[string(consumerMessages[i].Key)] = + // append(aggregationMsgs[string(consumerMessages[i].Key)], ctxMsg) if oldM, ok := aggregationMsgs[string(consumerMessages[i].Key)]; ok { oldM = append(oldM, ctxMsg) aggregationMsgs[string(consumerMessages[i].Key)] = oldM @@ -260,7 +347,16 @@ func (och *OnlineHistoryRedisConsumerHandler) MessagesDistributionHandle() { hashCode := utils.GetHashCode(uniqueKey) channelID := hashCode % ChannelNum newCtx := withAggregationCtx(ctx, v) - log.ZDebug(newCtx, "generate channelID", "hashCode", hashCode, "channelID", channelID, "uniqueKey", uniqueKey) + log.ZDebug( + newCtx, + "generate channelID", + "hashCode", + hashCode, + "channelID", + channelID, + "uniqueKey", + uniqueKey, + ) och.chArrays[channelID] <- Cmd2Value{Cmd: SourceMessages, Value: MsgChannelValue{uniqueKey: uniqueKey, ctxMsgList: v, ctx: newCtx}} } } @@ -288,7 +384,10 @@ func (och *OnlineHistoryRedisConsumerHandler) Cleanup(_ sarama.ConsumerGroupSess return nil } -func (och *OnlineHistoryRedisConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error { // a instance in the consumer group +func (och *OnlineHistoryRedisConsumerHandler) ConsumeClaim( + sess sarama.ConsumerGroupSession, + claim sarama.ConsumerGroupClaim, +) error { // a instance in the consumer group for { if sess == nil { log.ZWarn(context.Background(), "sess == nil, waiting", nil) diff --git a/internal/msgtransfer/online_msg_to_mongo_handler.go b/internal/msgtransfer/online_msg_to_mongo_handler.go index 77c5e9a6b..7a4799fac 100644 --- a/internal/msgtransfer/online_msg_to_mongo_handler.go +++ b/internal/msgtransfer/online_msg_to_mongo_handler.go @@ -3,13 +3,14 @@ package msgtransfer import ( "context" + "github.com/Shopify/sarama" + "google.golang.org/protobuf/proto" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/controller" kfk "github.com/OpenIMSDK/Open-IM-Server/pkg/common/kafka" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" pbMsg "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" - "github.com/Shopify/sarama" - "google.golang.org/protobuf/proto" ) type OnlineHistoryMongoConsumerHandler struct { @@ -27,7 +28,12 @@ func NewOnlineHistoryMongoConsumerHandler(database controller.CommonMsgDatabase) return mc } -func (mc *OnlineHistoryMongoConsumerHandler) handleChatWs2Mongo(ctx context.Context, cMsg *sarama.ConsumerMessage, key string, session sarama.ConsumerGroupSession) { +func (mc *OnlineHistoryMongoConsumerHandler) handleChatWs2Mongo( + ctx context.Context, + cMsg *sarama.ConsumerMessage, + key string, + session sarama.ConsumerGroupSession, +) { msg := cMsg.Value msgFromMQ := pbMsg.MsgDataToMongoByMQ{} err := proto.Unmarshal(msg, &msgFromMQ) @@ -42,7 +48,15 @@ func (mc *OnlineHistoryMongoConsumerHandler) handleChatWs2Mongo(ctx context.Cont log.ZInfo(ctx, "mongo consumer recv msg", "msgs", msgFromMQ.MsgData) err = mc.msgDatabase.BatchInsertChat2DB(ctx, msgFromMQ.ConversationID, msgFromMQ.MsgData, msgFromMQ.LastSeq) if err != nil { - log.ZError(ctx, "single data insert to mongo err", err, "msg", msgFromMQ.MsgData, "conversationID", msgFromMQ.ConversationID) + log.ZError( + ctx, + "single data insert to mongo err", + err, + "msg", + msgFromMQ.MsgData, + "conversationID", + msgFromMQ.ConversationID, + ) } var seqs []int64 for _, msg := range msgFromMQ.MsgData { @@ -50,14 +64,26 @@ func (mc *OnlineHistoryMongoConsumerHandler) handleChatWs2Mongo(ctx context.Cont } err = mc.msgDatabase.DeleteMessagesFromCache(ctx, msgFromMQ.ConversationID, seqs) if err != nil { - log.ZError(ctx, "remove cache msg from redis err", err, "msg", msgFromMQ.MsgData, "conversationID", msgFromMQ.ConversationID) + log.ZError( + ctx, + "remove cache msg from redis err", + err, + "msg", + msgFromMQ.MsgData, + "conversationID", + msgFromMQ.ConversationID, + ) } mc.msgDatabase.DelUserDeleteMsgsList(ctx, msgFromMQ.ConversationID, seqs) } func (OnlineHistoryMongoConsumerHandler) Setup(_ sarama.ConsumerGroupSession) error { return nil } func (OnlineHistoryMongoConsumerHandler) Cleanup(_ sarama.ConsumerGroupSession) error { return nil } -func (mc *OnlineHistoryMongoConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error { // a instance in the consumer group + +func (mc *OnlineHistoryMongoConsumerHandler) ConsumeClaim( + sess sarama.ConsumerGroupSession, + claim sarama.ConsumerGroupClaim, +) error { // a instance in the consumer group log.ZDebug(context.Background(), "online new session msg come", "highWaterMarkOffset", claim.HighWaterMarkOffset(), "topic", claim.Topic(), "partition", claim.Partition()) for msg := range claim.Messages() { diff --git a/internal/msgtransfer/persistent_msg_handler.go b/internal/msgtransfer/persistent_msg_handler.go index 026f5f5a0..30b739f7e 100644 --- a/internal/msgtransfer/persistent_msg_handler.go +++ b/internal/msgtransfer/persistent_msg_handler.go @@ -3,8 +3,7 @@ ** copyright('tuoyun,www.tuoyun.net'). ** author("fg,Gordon@tuoyun.net"). ** time(2021/5/11 15:37). - */ -package msgtransfer + */package msgtransfer import ( "context" @@ -35,7 +34,12 @@ func NewPersistentConsumerHandler(database controller.ChatLogDatabase) *Persiste } } -func (pc *PersistentConsumerHandler) handleChatWs2Mysql(ctx context.Context, cMsg *sarama.ConsumerMessage, msgKey string, _ sarama.ConsumerGroupSession) { +func (pc *PersistentConsumerHandler) handleChatWs2Mysql( + ctx context.Context, + cMsg *sarama.ConsumerMessage, + msgKey string, + _ sarama.ConsumerGroupSession, +) { msg := cMsg.Value var tag bool msgFromMQ := pbMsg.MsgDataToMQ{} @@ -73,10 +77,25 @@ func (pc *PersistentConsumerHandler) handleChatWs2Mysql(ctx context.Context, cMs } func (PersistentConsumerHandler) Setup(_ sarama.ConsumerGroupSession) error { return nil } func (PersistentConsumerHandler) Cleanup(_ sarama.ConsumerGroupSession) error { return nil } -func (pc *PersistentConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error { + +func (pc *PersistentConsumerHandler) ConsumeClaim( + sess sarama.ConsumerGroupSession, + claim sarama.ConsumerGroupClaim, +) error { for msg := range claim.Messages() { ctx := pc.persistentConsumerGroup.GetContextFromMsg(msg) - log.ZDebug(ctx, "kafka get info to mysql", "msgTopic", msg.Topic, "msgPartition", msg.Partition, "msg", string(msg.Value), "key", string(msg.Key)) + log.ZDebug( + ctx, + "kafka get info to mysql", + "msgTopic", + msg.Topic, + "msgPartition", + msg.Partition, + "msg", + string(msg.Value), + "key", + string(msg.Key), + ) if len(msg.Value) != 0 { pc.handleChatWs2Mysql(ctx, msg, string(msg.Key), sess) } else { diff --git a/internal/push/callback.go b/internal/push/callback.go index db9f36c48..854d27fc2 100644 --- a/internal/push/callback.go +++ b/internal/push/callback.go @@ -17,7 +17,12 @@ func url() string { return config.Config.Callback.CallbackUrl } -func callbackOfflinePush(ctx context.Context, userIDs []string, msg *sdkws.MsgData, offlinePushUserIDs *[]string) error { +func callbackOfflinePush( + ctx context.Context, + userIDs []string, + msg *sdkws.MsgData, + offlinePushUserIDs *[]string, +) error { if !config.Config.Callback.CallbackOfflinePush.Enable { return nil } @@ -82,7 +87,12 @@ func callbackOnlinePush(ctx context.Context, userIDs []string, msg *sdkws.MsgDat return http.CallBackPostReturn(ctx, url(), req, resp, config.Config.Callback.CallbackOnlinePush) } -func callbackBeforeSuperGroupOnlinePush(ctx context.Context, groupID string, msg *sdkws.MsgData, pushToUserIDs *[]string) error { +func callbackBeforeSuperGroupOnlinePush( + ctx context.Context, + groupID string, + msg *sdkws.MsgData, + pushToUserIDs *[]string, +) error { if !config.Config.Callback.CallbackBeforeSuperGroupOnlinePush.Enable { return nil } diff --git a/internal/push/consumer_init.go b/internal/push/consumer_init.go index 283cf1d1b..a76de754f 100644 --- a/internal/push/consumer_init.go +++ b/internal/push/consumer_init.go @@ -21,6 +21,7 @@ func (c *Consumer) initPrometheus() { } func (c *Consumer) Start() { - //statistics.NewStatistics(&c.successCount, config.Config.ModuleName.PushName, fmt.Sprintf("%d second push to msg_gateway count", constant.StatisticsTimeInterval), constant.StatisticsTimeInterval) + // statistics.NewStatistics(&c.successCount, config.Config.ModuleName.PushName, fmt.Sprintf("%d second push to + // msg_gateway count", constant.StatisticsTimeInterval), constant.StatisticsTimeInterval) go c.pushCh.pushConsumerGroup.RegisterHandleAndConsumer(&c.pushCh) } diff --git a/internal/push/offlinepush/fcm/push.go b/internal/push/offlinepush/fcm/push.go index 1d8478f31..63e945d98 100644 --- a/internal/push/offlinepush/fcm/push.go +++ b/internal/push/offlinepush/fcm/push.go @@ -6,12 +6,13 @@ import ( firebase "firebase.google.com/go" "firebase.google.com/go/messaging" + "github.com/redis/go-redis/v9" + "google.golang.org/api/option" + "github.com/OpenIMSDK/Open-IM-Server/internal/push/offlinepush" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" - "github.com/redis/go-redis/v9" - "google.golang.org/api/option" ) const SinglePushCountLimit = 400 diff --git a/internal/push/offlinepush/fcm/push_test.go b/internal/push/offlinepush/fcm/push_test.go index 1a7240302..5cef4ef2b 100644 --- a/internal/push/offlinepush/fcm/push_test.go +++ b/internal/push/offlinepush/fcm/push_test.go @@ -4,9 +4,10 @@ import ( "context" "testing" + "github.com/stretchr/testify/assert" + "github.com/OpenIMSDK/Open-IM-Server/internal/push/offlinepush" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" - "github.com/stretchr/testify/assert" ) func Test_Push(t *testing.T) { diff --git a/internal/push/offlinepush/getui/push.go b/internal/push/offlinepush/getui/push.go index 54bd83673..17278a4ff 100644 --- a/internal/push/offlinepush/getui/push.go +++ b/internal/push/offlinepush/getui/push.go @@ -99,7 +99,9 @@ func (g *Client) Push(ctx context.Context, userIDs []string, title, content stri func (g *Client) Auth(ctx context.Context, timeStamp int64) (token string, expireTime int64, err error) { h := sha256.New() - h.Write([]byte(config.Config.Push.GeTui.AppKey + strconv.Itoa(int(timeStamp)) + config.Config.Push.GeTui.MasterSecret)) + h.Write( + []byte(config.Config.Push.GeTui.AppKey + strconv.Itoa(int(timeStamp)) + config.Config.Push.GeTui.MasterSecret), + ) sign := hex.EncodeToString(h.Sum(nil)) reqAuth := AuthReq{ Sign: sign, @@ -150,7 +152,14 @@ func (g *Client) request(ctx context.Context, url string, input interface{}, tok return g.postReturn(ctx, config.Config.Push.GeTui.PushUrl+url, header, input, resp, 3) } -func (g *Client) postReturn(ctx context.Context, url string, header map[string]string, input interface{}, output RespI, timeout int) error { +func (g *Client) postReturn( + ctx context.Context, + url string, + header map[string]string, + input interface{}, + output RespI, + timeout int, +) error { err := http2.PostReturn(ctx, url, header, input, output, timeout) if err != nil { return err diff --git a/internal/push/offlinepush/jpush/push.go b/internal/push/offlinepush/jpush/push.go index 75fc2b27b..e22e97df4 100644 --- a/internal/push/offlinepush/jpush/push.go +++ b/internal/push/offlinepush/jpush/push.go @@ -60,5 +60,14 @@ func (j *JPush) Push(ctx context.Context, userIDs []string, title, content strin } func (j *JPush) request(ctx context.Context, po body.PushObj, resp interface{}, timeout int) error { - return http2.PostReturn(ctx, config.Config.Push.Jpns.PushUrl, map[string]string{"Authorization": j.getAuthorization(config.Config.Push.Jpns.AppKey, config.Config.Push.Jpns.MasterSecret)}, po, resp, timeout) + return http2.PostReturn( + ctx, + config.Config.Push.Jpns.PushUrl, + map[string]string{ + "Authorization": j.getAuthorization(config.Config.Push.Jpns.AppKey, config.Config.Push.Jpns.MasterSecret), + }, + po, + resp, + timeout, + ) } diff --git a/internal/push/push_handler.go b/internal/push/push_handler.go index ac128a1c7..2d0694088 100644 --- a/internal/push/push_handler.go +++ b/internal/push/push_handler.go @@ -3,6 +3,9 @@ package push import ( "context" + "github.com/Shopify/sarama" + "google.golang.org/protobuf/proto" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" kfk "github.com/OpenIMSDK/Open-IM-Server/pkg/common/kafka" @@ -10,8 +13,6 @@ import ( pbChat "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" pbPush "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/push" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "github.com/Shopify/sarama" - "google.golang.org/protobuf/proto" ) type ConsumerHandler struct { diff --git a/internal/push/push_rpc_server.go b/internal/push/push_rpc_server.go index ca45a58fa..08edc738e 100644 --- a/internal/push/push_rpc_server.go +++ b/internal/push/push_rpc_server.go @@ -4,6 +4,8 @@ import ( "context" "sync" + "google.golang.org/grpc" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/controller" @@ -12,7 +14,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" pbPush "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/push" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" - "google.golang.org/grpc" ) type pushServer struct { @@ -30,7 +31,16 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e groupRpcClient := rpcclient.NewGroupRpcClient(client) conversationRpcClient := rpcclient.NewConversationRpcClient(client) msgRpcClient := rpcclient.NewMessageRpcClient(client) - pusher := NewPusher(client, offlinePusher, database, localcache.NewGroupLocalCache(&groupRpcClient), localcache.NewConversationLocalCache(&conversationRpcClient), &conversationRpcClient, &groupRpcClient, &msgRpcClient) + pusher := NewPusher( + client, + offlinePusher, + database, + localcache.NewGroupLocalCache(&groupRpcClient), + localcache.NewConversationLocalCache(&conversationRpcClient), + &conversationRpcClient, + &groupRpcClient, + &msgRpcClient, + ) var wg sync.WaitGroup wg.Add(2) go func() { @@ -66,7 +76,10 @@ func (r *pushServer) PushMsg(ctx context.Context, pbData *pbPush.PushMsgReq) (re return &pbPush.PushMsgResp{}, nil } -func (r *pushServer) DelUserPushToken(ctx context.Context, req *pbPush.DelUserPushTokenReq) (resp *pbPush.DelUserPushTokenResp, err error) { +func (r *pushServer) DelUserPushToken( + ctx context.Context, + req *pbPush.DelUserPushTokenReq, +) (resp *pbPush.DelUserPushTokenResp, err error) { if err = r.pusher.database.DelFcmToken(ctx, req.UserID, int(req.PlatformID)); err != nil { return nil, err } diff --git a/internal/push/push_to_client.go b/internal/push/push_to_client.go index 7f08ee0aa..3cc91d20f 100644 --- a/internal/push/push_to_client.go +++ b/internal/push/push_to_client.go @@ -38,9 +38,16 @@ type Pusher struct { var errNoOfflinePusher = errors.New("no offlinePusher is configured") -func NewPusher(discov discoveryregistry.SvcDiscoveryRegistry, offlinePusher offlinepush.OfflinePusher, database controller.PushDatabase, - groupLocalCache *localcache.GroupLocalCache, conversationLocalCache *localcache.ConversationLocalCache, - conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient, msgRpcClient *rpcclient.MessageRpcClient) *Pusher { +func NewPusher( + discov discoveryregistry.SvcDiscoveryRegistry, + offlinePusher offlinepush.OfflinePusher, + database controller.PushDatabase, + groupLocalCache *localcache.GroupLocalCache, + conversationLocalCache *localcache.ConversationLocalCache, + conversationRpcClient *rpcclient.ConversationRpcClient, + groupRpcClient *rpcclient.GroupRpcClient, + msgRpcClient *rpcclient.MessageRpcClient, +) *Pusher { return &Pusher{ discov: discov, database: database, @@ -87,7 +94,18 @@ func (p *Pusher) Push2User(ctx context.Context, userIDs []string, msg *sdkws.Msg return err } isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush) - log.ZDebug(ctx, "push_result", "ws push result", wsResults, "sendData", msg, "isOfflinePush", isOfflinePush, "push_to_userID", userIDs) + log.ZDebug( + ctx, + "push_result", + "ws push result", + wsResults, + "sendData", + msg, + "isOfflinePush", + isOfflinePush, + "push_to_userID", + userIDs, + ) p.successCount++ for _, userID := range userIDs { if isOfflinePush && userID != msg.SendID { @@ -138,7 +156,15 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws } defer func(groupID string, userIDs []string) { if err := p.DeleteMemberAndSetConversationSeq(ctx, groupID, userIDs); err != nil { - log.ZError(ctx, "MemberQuitNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", userIDs) + log.ZError( + ctx, + "MemberQuitNotification DeleteMemberAndSetConversationSeq", + err, + "groupID", + groupID, + "userIDs", + userIDs, + ) } }(groupID, []string{tips.QuitUser.UserID}) pushToUserIDs = append(pushToUserIDs, tips.QuitUser.UserID) @@ -147,10 +173,21 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws if p.UnmarshalNotificationElem(msg.Content, &tips) != nil { return err } - kickedUsers := utils.Slice(tips.KickedUserList, func(e *sdkws.GroupMemberFullInfo) string { return e.UserID }) + kickedUsers := utils.Slice( + tips.KickedUserList, + func(e *sdkws.GroupMemberFullInfo) string { return e.UserID }, + ) defer func(groupID string, userIDs []string) { if err := p.DeleteMemberAndSetConversationSeq(ctx, groupID, userIDs); err != nil { - log.ZError(ctx, "MemberKickedNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", userIDs) + log.ZError( + ctx, + "MemberKickedNotification DeleteMemberAndSetConversationSeq", + err, + "groupID", + groupID, + "userIDs", + userIDs, + ) } }(groupID, kickedUsers) pushToUserIDs = append(pushToUserIDs, kickedUsers...) @@ -160,7 +197,16 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws if p.UnmarshalNotificationElem(msg.Content, &tips) != nil { return err } - log.ZInfo(ctx, "GroupDismissedNotificationInfo****", "groupID", groupID, "num", len(pushToUserIDs), "list", pushToUserIDs) + log.ZInfo( + ctx, + "GroupDismissedNotificationInfo****", + "groupID", + groupID, + "num", + len(pushToUserIDs), + "list", + pushToUserIDs, + ) if len(config.Config.Manager.UserID) > 0 { ctx = mcontext.WithOpUserIDContext(ctx, config.Config.Manager.UserID[0]) } @@ -224,9 +270,23 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg) return err } - _, err := p.GetConnsAndOnlinePush(ctx, msg, utils.IntersectString(needOfflinePushUserIDs, WebAndPcBackgroundUserIDs)) + _, err := p.GetConnsAndOnlinePush( + ctx, + msg, + utils.IntersectString(needOfflinePushUserIDs, WebAndPcBackgroundUserIDs), + ) if err != nil { - log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg, "userIDs", utils.IntersectString(needOfflinePushUserIDs, WebAndPcBackgroundUserIDs)) + log.ZError( + ctx, + "offlinePushMsg failed", + err, + "groupID", + groupID, + "msg", + msg, + "userIDs", + utils.IntersectString(needOfflinePushUserIDs, WebAndPcBackgroundUserIDs), + ) return err } } @@ -234,7 +294,11 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws return nil } -func (p *Pusher) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) { +func (p *Pusher) GetConnsAndOnlinePush( + ctx context.Context, + msg *sdkws.MsgData, + pushToUserIDs []string, +) (wsResults []*msggateway.SingleMsgToUserResults, err error) { conns, err := p.discov.GetConns(ctx, config.Config.RpcRegisterName.OpenImMessageGatewayName) log.ZDebug(ctx, "get gateway conn", "conn length", len(conns)) if err != nil { @@ -243,7 +307,10 @@ func (p *Pusher) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, //Online push message for _, v := range conns { msgClient := msggateway.NewMsgGatewayClient(v) - reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: pushToUserIDs}) + reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg( + ctx, + &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: pushToUserIDs}, + ) p.discov.CloseConn(v) if err != nil { continue @@ -257,7 +324,12 @@ func (p *Pusher) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, return wsResults, nil } -func (p *Pusher) offlinePushMsg(ctx context.Context, conversationID string, msg *sdkws.MsgData, offlinePushUserIDs []string) error { +func (p *Pusher) offlinePushMsg( + ctx context.Context, + conversationID string, + msg *sdkws.MsgData, + offlinePushUserIDs []string, +) error { title, content, opts, err := p.getOfflinePushInfos(conversationID, msg) if err != nil { return err @@ -291,7 +363,10 @@ func (p *Pusher) GetOfflinePushOpts(msg *sdkws.MsgData) (opts *offlinepush.Opts, return opts, nil } -func (p *Pusher) getOfflinePushInfos(conversationID string, msg *sdkws.MsgData) (title, content string, opts *offlinepush.Opts, err error) { +func (p *Pusher) getOfflinePushInfos( + conversationID string, + msg *sdkws.MsgData, +) (title, content string, opts *offlinepush.Opts, err error) { if p.offlinePusher == nil { err = errNoOfflinePusher return diff --git a/internal/rpc/auth/auth.go b/internal/rpc/auth/auth.go index 378bbfcb3..2e0b7fe53 100644 --- a/internal/rpc/auth/auth.go +++ b/internal/rpc/auth/auth.go @@ -3,6 +3,8 @@ package auth import ( "context" + "google.golang.org/grpc" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" @@ -15,7 +17,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msggateway" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "google.golang.org/grpc" ) type authServer struct { @@ -33,7 +34,11 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e pbAuth.RegisterAuthServer(server, &authServer{ userRpcClient: &userRpcClient, RegisterCenter: client, - authDatabase: controller.NewAuthDatabase(cache.NewMsgCacheModel(rdb), config.Config.Secret, config.Config.TokenPolicy.Expire), + authDatabase: controller.NewAuthDatabase( + cache.NewMsgCacheModel(rdb), + config.Config.Secret, + config.Config.TokenPolicy.Expire, + ), }) return nil } @@ -80,7 +85,10 @@ func (s *authServer) parseToken(ctx context.Context, tokensString string) (claim return nil, errs.ErrTokenNotExist.Wrap() } -func (s *authServer) ParseToken(ctx context.Context, req *pbAuth.ParseTokenReq) (resp *pbAuth.ParseTokenResp, err error) { +func (s *authServer) ParseToken( + ctx context.Context, + req *pbAuth.ParseTokenReq, +) (resp *pbAuth.ParseTokenResp, err error) { resp = &pbAuth.ParseTokenResp{} claims, err := s.parseToken(ctx, req.Token) if err != nil { diff --git a/internal/rpc/conversation/conversaion.go b/internal/rpc/conversation/conversaion.go index c5a454d4d..11859136f 100644 --- a/internal/rpc/conversation/conversaion.go +++ b/internal/rpc/conversation/conversaion.go @@ -3,6 +3,8 @@ package conversation import ( "context" + "google.golang.org/grpc" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/convert" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" @@ -17,7 +19,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient/notification" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "google.golang.org/grpc" ) type conversationServer struct { @@ -44,12 +45,19 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e pbConversation.RegisterConversationServer(server, &conversationServer{ conversationNotificationSender: notification.NewConversationNotificationSender(&msgRpcClient), groupRpcClient: &groupRpcClient, - conversationDatabase: controller.NewConversationDatabase(conversationDB, cache.NewConversationRedis(rdb, cache.GetDefaultOpt(), conversationDB), tx.NewGorm(db)), + conversationDatabase: controller.NewConversationDatabase( + conversationDB, + cache.NewConversationRedis(rdb, cache.GetDefaultOpt(), conversationDB), + tx.NewGorm(db), + ), }) return nil } -func (c *conversationServer) GetConversation(ctx context.Context, req *pbConversation.GetConversationReq) (*pbConversation.GetConversationResp, error) { +func (c *conversationServer) GetConversation( + ctx context.Context, + req *pbConversation.GetConversationReq, +) (*pbConversation.GetConversationResp, error) { conversations, err := c.conversationDatabase.FindConversations(ctx, req.OwnerUserID, []string{req.ConversationID}) if err != nil { return nil, err @@ -62,7 +70,10 @@ func (c *conversationServer) GetConversation(ctx context.Context, req *pbConvers return resp, nil } -func (c *conversationServer) GetAllConversations(ctx context.Context, req *pbConversation.GetAllConversationsReq) (*pbConversation.GetAllConversationsResp, error) { +func (c *conversationServer) GetAllConversations( + ctx context.Context, + req *pbConversation.GetAllConversationsReq, +) (*pbConversation.GetAllConversationsResp, error) { conversations, err := c.conversationDatabase.GetUserAllConversation(ctx, req.OwnerUserID) if err != nil { return nil, err @@ -72,7 +83,10 @@ func (c *conversationServer) GetAllConversations(ctx context.Context, req *pbCon return resp, nil } -func (c *conversationServer) GetConversations(ctx context.Context, req *pbConversation.GetConversationsReq) (*pbConversation.GetConversationsResp, error) { +func (c *conversationServer) GetConversations( + ctx context.Context, + req *pbConversation.GetConversationsReq, +) (*pbConversation.GetConversationsResp, error) { conversations, err := c.conversationDatabase.FindConversations(ctx, req.OwnerUserID, req.ConversationIDs) if err != nil { return nil, err @@ -82,7 +96,10 @@ func (c *conversationServer) GetConversations(ctx context.Context, req *pbConver return resp, nil } -func (c *conversationServer) BatchSetConversations(ctx context.Context, req *pbConversation.BatchSetConversationsReq) (*pbConversation.BatchSetConversationsResp, error) { +func (c *conversationServer) BatchSetConversations( + ctx context.Context, + req *pbConversation.BatchSetConversationsReq, +) (*pbConversation.BatchSetConversationsResp, error) { conversations := convert.ConversationsPb2DB(req.Conversations) err := c.conversationDatabase.SetUserConversations(ctx, req.OwnerUserID, conversations) if err != nil { @@ -92,12 +109,19 @@ func (c *conversationServer) BatchSetConversations(ctx context.Context, req *pbC return &pbConversation.BatchSetConversationsResp{}, nil } -func (c *conversationServer) SetConversation(ctx context.Context, req *pbConversation.SetConversationReq) (*pbConversation.SetConversationResp, error) { +func (c *conversationServer) SetConversation( + ctx context.Context, + req *pbConversation.SetConversationReq, +) (*pbConversation.SetConversationResp, error) { var conversation tableRelation.ConversationModel if err := utils.CopyStructFields(&conversation, req.Conversation); err != nil { return nil, err } - err := c.conversationDatabase.SetUserConversations(ctx, req.Conversation.OwnerUserID, []*tableRelation.ConversationModel{&conversation}) + err := c.conversationDatabase.SetUserConversations( + ctx, + req.Conversation.OwnerUserID, + []*tableRelation.ConversationModel{&conversation}, + ) if err != nil { return nil, err } @@ -106,7 +130,10 @@ func (c *conversationServer) SetConversation(ctx context.Context, req *pbConvers return resp, nil } -func (c *conversationServer) SetRecvMsgOpt(ctx context.Context, req *pbConversation.SetRecvMsgOptReq) (*pbConversation.SetRecvMsgOptResp, error) { +func (c *conversationServer) SetRecvMsgOpt( + ctx context.Context, + req *pbConversation.SetRecvMsgOptReq, +) (*pbConversation.SetRecvMsgOptResp, error) { if err := c.conversationDatabase.SetUsersConversationFiledTx(ctx, []string{req.OwnerUserID}, &tableRelation.ConversationModel{OwnerUserID: req.OwnerUserID, ConversationID: req.ConversationID, RecvMsgOpt: req.RecvMsgOpt}, map[string]interface{}{"recv_msg_opt": req.RecvMsgOpt}); err != nil { return nil, err } @@ -115,7 +142,10 @@ func (c *conversationServer) SetRecvMsgOpt(ctx context.Context, req *pbConversat } // deprecated -func (c *conversationServer) ModifyConversationField(ctx context.Context, req *pbConversation.ModifyConversationFieldReq) (*pbConversation.ModifyConversationFieldResp, error) { +func (c *conversationServer) ModifyConversationField( + ctx context.Context, + req *pbConversation.ModifyConversationFieldReq, +) (*pbConversation.ModifyConversationFieldResp, error) { resp := &pbConversation.ModifyConversationFieldResp{} var err error if req.Conversation.ConversationType == constant.GroupChatType { @@ -129,11 +159,19 @@ func (c *conversationServer) ModifyConversationField(ctx context.Context, req *p } conversation := convert.ConversationPb2DB(req.Conversation) if req.FieldType == constant.FieldIsPrivateChat { - err := c.conversationDatabase.SyncPeerUserPrivateConversationTx(ctx, []*tableRelation.ConversationModel{conversation}) + err := c.conversationDatabase.SyncPeerUserPrivateConversationTx( + ctx, + []*tableRelation.ConversationModel{conversation}, + ) if err != nil { return nil, err } - c.conversationNotificationSender.ConversationSetPrivateNotification(ctx, req.Conversation.OwnerUserID, req.Conversation.UserID, req.Conversation.IsPrivateChat) + c.conversationNotificationSender.ConversationSetPrivateNotification( + ctx, + req.Conversation.OwnerUserID, + req.Conversation.UserID, + req.Conversation.IsPrivateChat, + ) return resp, nil } filedMap := make(map[string]interface{}) @@ -161,7 +199,10 @@ func (c *conversationServer) ModifyConversationField(ctx context.Context, req *p return resp, nil } -func (c *conversationServer) SetConversations(ctx context.Context, req *pbConversation.SetConversationsReq) (*pbConversation.SetConversationsResp, error) { +func (c *conversationServer) SetConversations( + ctx context.Context, + req *pbConversation.SetConversationsReq, +) (*pbConversation.SetConversationsResp, error) { if req.Conversation == nil { return nil, errs.ErrArgs.Wrap("conversation must not be nil") } @@ -207,7 +248,12 @@ func (c *conversationServer) SetConversations(ctx context.Context, req *pbConver return nil, err } for _, ownerUserID := range req.UserIDs { - c.conversationNotificationSender.ConversationSetPrivateNotification(ctx, ownerUserID, req.Conversation.UserID, req.Conversation.IsPrivateChat.Value) + c.conversationNotificationSender.ConversationSetPrivateNotification( + ctx, + ownerUserID, + req.Conversation.UserID, + req.Conversation.IsPrivateChat.Value, + ) } } if req.Conversation.BurnDuration != nil { @@ -224,7 +270,10 @@ func (c *conversationServer) SetConversations(ctx context.Context, req *pbConver } // 获取超级大群开启免打扰的用户ID -func (c *conversationServer) GetRecvMsgNotNotifyUserIDs(ctx context.Context, req *pbConversation.GetRecvMsgNotNotifyUserIDsReq) (*pbConversation.GetRecvMsgNotNotifyUserIDsResp, error) { +func (c *conversationServer) GetRecvMsgNotNotifyUserIDs( + ctx context.Context, + req *pbConversation.GetRecvMsgNotNotifyUserIDsReq, +) (*pbConversation.GetRecvMsgNotNotifyUserIDsResp, error) { userIDs, err := c.conversationDatabase.FindRecvMsgNotNotifyUserIDs(ctx, req.GroupID) if err != nil { return nil, err @@ -233,7 +282,10 @@ func (c *conversationServer) GetRecvMsgNotNotifyUserIDs(ctx context.Context, req } // create conversation without notification for msg redis transfer -func (c *conversationServer) CreateSingleChatConversations(ctx context.Context, req *pbConversation.CreateSingleChatConversationsReq) (*pbConversation.CreateSingleChatConversationsResp, error) { +func (c *conversationServer) CreateSingleChatConversations( + ctx context.Context, + req *pbConversation.CreateSingleChatConversationsReq, +) (*pbConversation.CreateSingleChatConversationsResp, error) { var conversation tableRelation.ConversationModel conversation.ConversationID = utils.GetConversationIDBySessionType(constant.SingleChatType, req.RecvID, req.SendID) conversation.ConversationType = constant.SingleChatType @@ -254,7 +306,10 @@ func (c *conversationServer) CreateSingleChatConversations(ctx context.Context, return &pbConversation.CreateSingleChatConversationsResp{}, nil } -func (c *conversationServer) CreateGroupChatConversations(ctx context.Context, req *pbConversation.CreateGroupChatConversationsReq) (*pbConversation.CreateGroupChatConversationsResp, error) { +func (c *conversationServer) CreateGroupChatConversations( + ctx context.Context, + req *pbConversation.CreateGroupChatConversationsReq, +) (*pbConversation.CreateGroupChatConversationsResp, error) { err := c.conversationDatabase.CreateGroupChatConversation(ctx, req.GroupID, req.UserIDs) if err != nil { return nil, err @@ -262,7 +317,10 @@ func (c *conversationServer) CreateGroupChatConversations(ctx context.Context, r return &pbConversation.CreateGroupChatConversationsResp{}, nil } -func (c *conversationServer) SetConversationMaxSeq(ctx context.Context, req *pbConversation.SetConversationMaxSeqReq) (*pbConversation.SetConversationMaxSeqResp, error) { +func (c *conversationServer) SetConversationMaxSeq( + ctx context.Context, + req *pbConversation.SetConversationMaxSeqReq, +) (*pbConversation.SetConversationMaxSeqResp, error) { if err := c.conversationDatabase.UpdateUsersConversationFiled(ctx, req.OwnerUserID, req.ConversationID, map[string]interface{}{"max_seq": req.MaxSeq}); err != nil { return nil, err @@ -270,7 +328,10 @@ func (c *conversationServer) SetConversationMaxSeq(ctx context.Context, req *pbC return &pbConversation.SetConversationMaxSeqResp{}, nil } -func (c *conversationServer) GetConversationIDs(ctx context.Context, req *pbConversation.GetConversationIDsReq) (*pbConversation.GetConversationIDsResp, error) { +func (c *conversationServer) GetConversationIDs( + ctx context.Context, + req *pbConversation.GetConversationIDsReq, +) (*pbConversation.GetConversationIDsResp, error) { conversationIDs, err := c.conversationDatabase.GetConversationIDs(ctx, req.UserID) if err != nil { return nil, err @@ -278,7 +339,10 @@ func (c *conversationServer) GetConversationIDs(ctx context.Context, req *pbConv return &pbConversation.GetConversationIDsResp{ConversationIDs: conversationIDs}, nil } -func (c *conversationServer) GetUserConversationIDsHash(ctx context.Context, req *pbConversation.GetUserConversationIDsHashReq) (*pbConversation.GetUserConversationIDsHashResp, error) { +func (c *conversationServer) GetUserConversationIDsHash( + ctx context.Context, + req *pbConversation.GetUserConversationIDsHashReq, +) (*pbConversation.GetUserConversationIDsHashResp, error) { hash, err := c.conversationDatabase.GetUserConversationIDsHash(ctx, req.OwnerUserID) if err != nil { return nil, err @@ -286,10 +350,15 @@ func (c *conversationServer) GetUserConversationIDsHash(ctx context.Context, req return &pbConversation.GetUserConversationIDsHashResp{Hash: hash}, nil } -func (c *conversationServer) GetConversationsByConversationID(ctx context.Context, req *pbConversation.GetConversationsByConversationIDReq) (*pbConversation.GetConversationsByConversationIDResp, error) { +func (c *conversationServer) GetConversationsByConversationID( + ctx context.Context, + req *pbConversation.GetConversationsByConversationIDReq, +) (*pbConversation.GetConversationsByConversationIDResp, error) { conversations, err := c.conversationDatabase.GetConversationsByConversationID(ctx, req.ConversationIDs) if err != nil { return nil, err } - return &pbConversation.GetConversationsByConversationIDResp{Conversations: convert.ConversationsDB2Pb(conversations)}, nil + return &pbConversation.GetConversationsByConversationIDResp{ + Conversations: convert.ConversationsDB2Pb(conversations), + }, nil } diff --git a/internal/rpc/friend/black.go b/internal/rpc/friend/black.go index 692e83439..784e32d20 100644 --- a/internal/rpc/friend/black.go +++ b/internal/rpc/friend/black.go @@ -11,7 +11,10 @@ import ( pbFriend "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/friend" ) -func (s *friendServer) GetPaginationBlacks(ctx context.Context, req *pbFriend.GetPaginationBlacksReq) (resp *pbFriend.GetPaginationBlacksResp, err error) { +func (s *friendServer) GetPaginationBlacks( + ctx context.Context, + req *pbFriend.GetPaginationBlacksReq, +) (resp *pbFriend.GetPaginationBlacksResp, err error) { if err := s.userRpcClient.Access(ctx, req.UserID); err != nil { return nil, err } @@ -44,7 +47,10 @@ func (s *friendServer) IsBlack(ctx context.Context, req *pbFriend.IsBlackReq) (* return resp, nil } -func (s *friendServer) RemoveBlack(ctx context.Context, req *pbFriend.RemoveBlackReq) (*pbFriend.RemoveBlackResp, error) { +func (s *friendServer) RemoveBlack( + ctx context.Context, + req *pbFriend.RemoveBlackReq, +) (*pbFriend.RemoveBlackResp, error) { if err := s.userRpcClient.Access(ctx, req.OwnerUserID); err != nil { return nil, err } @@ -63,7 +69,12 @@ func (s *friendServer) AddBlack(ctx context.Context, req *pbFriend.AddBlackReq) if err != nil { return nil, err } - black := relation.BlackModel{OwnerUserID: req.OwnerUserID, BlockUserID: req.BlackUserID, OperatorUserID: mcontext.GetOpUserID(ctx), CreateTime: time.Now()} + black := relation.BlackModel{ + OwnerUserID: req.OwnerUserID, + BlockUserID: req.BlackUserID, + OperatorUserID: mcontext.GetOpUserID(ctx), + CreateTime: time.Now(), + } if err := s.blackDatabase.Create(ctx, []*relation.BlackModel{&black}); err != nil { return nil, err } diff --git a/internal/rpc/friend/friend.go b/internal/rpc/friend/friend.go index 7c3723583..da99fd34c 100644 --- a/internal/rpc/friend/friend.go +++ b/internal/rpc/friend/friend.go @@ -7,6 +7,8 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" + "google.golang.org/grpc" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/controller" @@ -19,7 +21,6 @@ import ( pbfriend "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/friend" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient/notification" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "google.golang.org/grpc" ) type friendServer struct { @@ -46,10 +47,21 @@ func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { friendDB := relation.NewFriendGorm(db) userRpcClient := rpcclient.NewUserRpcClient(client) msgRpcClient := rpcclient.NewMessageRpcClient(client) - notificationSender := notification.NewFriendNotificationSender(&msgRpcClient, notification.WithRpcFunc(userRpcClient.GetUsersInfo)) + notificationSender := notification.NewFriendNotificationSender( + &msgRpcClient, + notification.WithRpcFunc(userRpcClient.GetUsersInfo), + ) pbfriend.RegisterFriendServer(server, &friendServer{ - friendDatabase: controller.NewFriendDatabase(friendDB, relation.NewFriendRequestGorm(db), cache.NewFriendCacheRedis(rdb, friendDB, cache.GetDefaultOpt()), tx.NewGorm(db)), - blackDatabase: controller.NewBlackDatabase(blackDB, cache.NewBlackCacheRedis(rdb, blackDB, cache.GetDefaultOpt())), + friendDatabase: controller.NewFriendDatabase( + friendDB, + relation.NewFriendRequestGorm(db), + cache.NewFriendCacheRedis(rdb, friendDB, cache.GetDefaultOpt()), + tx.NewGorm(db), + ), + blackDatabase: controller.NewBlackDatabase( + blackDB, + cache.NewBlackCacheRedis(rdb, blackDB, cache.GetDefaultOpt()), + ), userRpcClient: &userRpcClient, notificationSender: notificationSender, RegisterCenter: client, @@ -58,7 +70,10 @@ func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { } // ok -func (s *friendServer) ApplyToAddFriend(ctx context.Context, req *pbfriend.ApplyToAddFriendReq) (resp *pbfriend.ApplyToAddFriendResp, err error) { +func (s *friendServer) ApplyToAddFriend( + ctx context.Context, + req *pbfriend.ApplyToAddFriendReq, +) (resp *pbfriend.ApplyToAddFriendResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") resp = &pbfriend.ApplyToAddFriendResp{} if err := tokenverify.CheckAccessV3(ctx, req.FromUserID); err != nil { @@ -88,7 +103,10 @@ func (s *friendServer) ApplyToAddFriend(ctx context.Context, req *pbfriend.Apply } // ok -func (s *friendServer) ImportFriends(ctx context.Context, req *pbfriend.ImportFriendReq) (resp *pbfriend.ImportFriendResp, err error) { +func (s *friendServer) ImportFriends( + ctx context.Context, + req *pbfriend.ImportFriendReq, +) (resp *pbfriend.ImportFriendResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") if err := tokenverify.CheckAdmin(ctx); err != nil { return nil, err @@ -111,14 +129,22 @@ func (s *friendServer) ImportFriends(ctx context.Context, req *pbfriend.ImportFr } // ok -func (s *friendServer) RespondFriendApply(ctx context.Context, req *pbfriend.RespondFriendApplyReq) (resp *pbfriend.RespondFriendApplyResp, err error) { +func (s *friendServer) RespondFriendApply( + ctx context.Context, + req *pbfriend.RespondFriendApplyReq, +) (resp *pbfriend.RespondFriendApplyResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") resp = &pbfriend.RespondFriendApplyResp{} if err := tokenverify.CheckAccessV3(ctx, req.ToUserID); err != nil { return nil, err } - friendRequest := tablerelation.FriendRequestModel{FromUserID: req.FromUserID, ToUserID: req.ToUserID, HandleMsg: req.HandleMsg, HandleResult: req.HandleResult} + friendRequest := tablerelation.FriendRequestModel{ + FromUserID: req.FromUserID, + ToUserID: req.ToUserID, + HandleMsg: req.HandleMsg, + HandleResult: req.HandleResult, + } if req.HandleResult == constant.FriendResponseAgree { err := s.friendDatabase.AgreeFriendRequest(ctx, &friendRequest) if err != nil { @@ -139,7 +165,10 @@ func (s *friendServer) RespondFriendApply(ctx context.Context, req *pbfriend.Res } // ok -func (s *friendServer) DeleteFriend(ctx context.Context, req *pbfriend.DeleteFriendReq) (resp *pbfriend.DeleteFriendResp, err error) { +func (s *friendServer) DeleteFriend( + ctx context.Context, + req *pbfriend.DeleteFriendReq, +) (resp *pbfriend.DeleteFriendResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") resp = &pbfriend.DeleteFriendResp{} if err := s.userRpcClient.Access(ctx, req.OwnerUserID); err != nil { @@ -157,7 +186,10 @@ func (s *friendServer) DeleteFriend(ctx context.Context, req *pbfriend.DeleteFri } // ok -func (s *friendServer) SetFriendRemark(ctx context.Context, req *pbfriend.SetFriendRemarkReq) (resp *pbfriend.SetFriendRemarkResp, err error) { +func (s *friendServer) SetFriendRemark( + ctx context.Context, + req *pbfriend.SetFriendRemarkReq, +) (resp *pbfriend.SetFriendRemarkResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") resp = &pbfriend.SetFriendRemarkResp{} if err := s.userRpcClient.Access(ctx, req.OwnerUserID); err != nil { @@ -175,7 +207,10 @@ func (s *friendServer) SetFriendRemark(ctx context.Context, req *pbfriend.SetFri } // ok -func (s *friendServer) GetDesignatedFriends(ctx context.Context, req *pbfriend.GetDesignatedFriendsReq) (resp *pbfriend.GetDesignatedFriendsResp, err error) { +func (s *friendServer) GetDesignatedFriends( + ctx context.Context, + req *pbfriend.GetDesignatedFriendsReq, +) (resp *pbfriend.GetDesignatedFriendsResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") resp = &pbfriend.GetDesignatedFriendsResp{} if utils.Duplicate(req.FriendUserIDs) { @@ -192,7 +227,10 @@ func (s *friendServer) GetDesignatedFriends(ctx context.Context, req *pbfriend.G } // ok 获取接收到的好友申请(即别人主动申请的) -func (s *friendServer) GetPaginationFriendsApplyTo(ctx context.Context, req *pbfriend.GetPaginationFriendsApplyToReq) (resp *pbfriend.GetPaginationFriendsApplyToResp, err error) { +func (s *friendServer) GetPaginationFriendsApplyTo( + ctx context.Context, + req *pbfriend.GetPaginationFriendsApplyToReq, +) (resp *pbfriend.GetPaginationFriendsApplyToResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") resp = &pbfriend.GetPaginationFriendsApplyToResp{} if err := s.userRpcClient.Access(ctx, req.UserID); err != nil { @@ -212,7 +250,10 @@ func (s *friendServer) GetPaginationFriendsApplyTo(ctx context.Context, req *pbf } // ok 获取主动发出去的好友申请列表 -func (s *friendServer) GetPaginationFriendsApplyFrom(ctx context.Context, req *pbfriend.GetPaginationFriendsApplyFromReq) (resp *pbfriend.GetPaginationFriendsApplyFromResp, err error) { +func (s *friendServer) GetPaginationFriendsApplyFrom( + ctx context.Context, + req *pbfriend.GetPaginationFriendsApplyFromReq, +) (resp *pbfriend.GetPaginationFriendsApplyFromResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") resp = &pbfriend.GetPaginationFriendsApplyFromResp{} if err := s.userRpcClient.Access(ctx, req.UserID); err != nil { @@ -232,7 +273,10 @@ func (s *friendServer) GetPaginationFriendsApplyFrom(ctx context.Context, req *p } // ok -func (s *friendServer) IsFriend(ctx context.Context, req *pbfriend.IsFriendReq) (resp *pbfriend.IsFriendResp, err error) { +func (s *friendServer) IsFriend( + ctx context.Context, + req *pbfriend.IsFriendReq, +) (resp *pbfriend.IsFriendResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") resp = &pbfriend.IsFriendResp{} resp.InUser1Friends, resp.InUser2Friends, err = s.friendDatabase.CheckIn(ctx, req.UserID1, req.UserID2) @@ -243,7 +287,10 @@ func (s *friendServer) IsFriend(ctx context.Context, req *pbfriend.IsFriendReq) } // ok -func (s *friendServer) GetPaginationFriends(ctx context.Context, req *pbfriend.GetPaginationFriendsReq) (resp *pbfriend.GetPaginationFriendsResp, err error) { +func (s *friendServer) GetPaginationFriends( + ctx context.Context, + req *pbfriend.GetPaginationFriendsReq, +) (resp *pbfriend.GetPaginationFriendsResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") if err := s.userRpcClient.Access(ctx, req.UserID); err != nil { return nil, err @@ -262,7 +309,10 @@ func (s *friendServer) GetPaginationFriends(ctx context.Context, req *pbfriend.G return resp, nil } -func (s *friendServer) GetFriendIDs(ctx context.Context, req *pbfriend.GetFriendIDsReq) (resp *pbfriend.GetFriendIDsResp, err error) { +func (s *friendServer) GetFriendIDs( + ctx context.Context, + req *pbfriend.GetFriendIDsReq, +) (resp *pbfriend.GetFriendIDsResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") if err := s.userRpcClient.Access(ctx, req.UserID); err != nil { return nil, err diff --git a/internal/rpc/group/cache.go b/internal/rpc/group/cache.go index 0bd3316be..0a13a45e5 100644 --- a/internal/rpc/group/cache.go +++ b/internal/rpc/group/cache.go @@ -7,7 +7,10 @@ import ( pbGroup "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/group" ) -func (s *groupServer) GetGroupInfoCache(ctx context.Context, req *pbGroup.GetGroupInfoCacheReq) (resp *pbGroup.GetGroupInfoCacheResp, err error) { +func (s *groupServer) GetGroupInfoCache( + ctx context.Context, + req *pbGroup.GetGroupInfoCacheReq, +) (resp *pbGroup.GetGroupInfoCacheResp, err error) { group, err := s.GroupDatabase.TakeGroup(ctx, req.GroupID) if err != nil { return nil, err @@ -16,7 +19,10 @@ func (s *groupServer) GetGroupInfoCache(ctx context.Context, req *pbGroup.GetGro return resp, nil } -func (s *groupServer) GetGroupMemberCache(ctx context.Context, req *pbGroup.GetGroupMemberCacheReq) (resp *pbGroup.GetGroupMemberCacheResp, err error) { +func (s *groupServer) GetGroupMemberCache( + ctx context.Context, + req *pbGroup.GetGroupMemberCacheReq, +) (resp *pbGroup.GetGroupMemberCacheResp, err error) { members, err := s.GroupDatabase.TakeGroupMember(ctx, req.GroupID, req.GroupMemberID) if err != nil { return nil, err diff --git a/internal/rpc/group/callback.go b/internal/rpc/group/callback.go index aa311e4e5..f61413f30 100644 --- a/internal/rpc/group/callback.go +++ b/internal/rpc/group/callback.go @@ -43,7 +43,13 @@ func CallbackBeforeCreateGroup(ctx context.Context, req *group.CreateGroupReq) ( }) } resp := &callbackstruct.CallbackBeforeCreateGroupResp{} - err = http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackBeforeCreateGroup) + err = http.CallBackPostReturn( + ctx, + config.Config.Callback.CallbackUrl, + cbReq, + resp, + config.Config.Callback.CallbackBeforeCreateGroup, + ) if err != nil { if err == errs.ErrCallbackContinue { return nil @@ -65,7 +71,11 @@ func CallbackBeforeCreateGroup(ctx context.Context, req *group.CreateGroupReq) ( return nil } -func CallbackBeforeMemberJoinGroup(ctx context.Context, groupMember *relation.GroupMemberModel, groupEx string) (err error) { +func CallbackBeforeMemberJoinGroup( + ctx context.Context, + groupMember *relation.GroupMemberModel, + groupEx string, +) (err error) { if !config.Config.Callback.CallbackBeforeMemberJoinGroup.Enable { return nil } @@ -78,7 +88,13 @@ func CallbackBeforeMemberJoinGroup(ctx context.Context, groupMember *relation.Gr GroupEx: groupEx, } resp := &callbackstruct.CallbackBeforeMemberJoinGroupResp{} - err = http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, callbackReq, resp, config.Config.Callback.CallbackBeforeMemberJoinGroup) + err = http.CallBackPostReturn( + ctx, + config.Config.Callback.CallbackUrl, + callbackReq, + resp, + config.Config.Callback.CallbackBeforeMemberJoinGroup, + ) if err != nil { if err == errs.ErrCallbackContinue { return nil @@ -118,7 +134,13 @@ func CallbackBeforeSetGroupMemberInfo(ctx context.Context, req *group.SetGroupMe callbackReq.Ex = &req.Ex.Value } resp := &callbackstruct.CallbackBeforeSetGroupMemberInfoResp{} - err = http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, callbackReq, resp, config.Config.Callback.CallbackBeforeSetGroupMemberInfo) + err = http.CallBackPostReturn( + ctx, + config.Config.Callback.CallbackUrl, + callbackReq, + resp, + config.Config.Callback.CallbackBeforeSetGroupMemberInfo, + ) if err != nil { if err == errs.ErrCallbackContinue { return nil diff --git a/internal/rpc/group/convert.go b/internal/rpc/group/convert.go index 849a8b72a..48e70357b 100644 --- a/internal/rpc/group/convert.go +++ b/internal/rpc/group/convert.go @@ -27,7 +27,10 @@ func (s *groupServer) groupDB2PB(group *relation.GroupModel, ownerUserID string, } } -func (s *groupServer) groupMemberDB2PB(member *relation.GroupMemberModel, appMangerLevel int32) *sdkws.GroupMemberFullInfo { +func (s *groupServer) groupMemberDB2PB( + member *relation.GroupMemberModel, + appMangerLevel int32, +) *sdkws.GroupMemberFullInfo { return &sdkws.GroupMemberFullInfo{ GroupID: member.GroupID, UserID: member.UserID, diff --git a/internal/rpc/group/fill.go b/internal/rpc/group/fill.go index da82e9b5e..64092eeab 100644 --- a/internal/rpc/group/fill.go +++ b/internal/rpc/group/fill.go @@ -7,7 +7,12 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" ) -func (s *groupServer) FindGroupMember(ctx context.Context, groupIDs []string, userIDs []string, roleLevels []int32) ([]*relationTb.GroupMemberModel, error) { +func (s *groupServer) FindGroupMember( + ctx context.Context, + groupIDs []string, + userIDs []string, + roleLevels []int32, +) ([]*relationTb.GroupMemberModel, error) { members, err := s.GroupDatabase.FindGroupMember(ctx, groupIDs, userIDs, roleLevels) if err != nil { return nil, err @@ -39,7 +44,11 @@ func (s *groupServer) FindGroupMember(ctx context.Context, groupIDs []string, us return members, nil } -func (s *groupServer) TakeGroupMember(ctx context.Context, groupID string, userID string) (*relationTb.GroupMemberModel, error) { +func (s *groupServer) TakeGroupMember( + ctx context.Context, + groupID string, + userID string, +) (*relationTb.GroupMemberModel, error) { member, err := s.GroupDatabase.TakeGroupMember(ctx, groupID, userID) if err != nil { return nil, err @@ -79,7 +88,11 @@ func (s *groupServer) TakeGroupOwner(ctx context.Context, groupID string) (*rela return owner, nil } -func (s *groupServer) PageGetGroupMember(ctx context.Context, groupID string, pageNumber, showNumber int32) (uint32, []*relationTb.GroupMemberModel, error) { +func (s *groupServer) PageGetGroupMember( + ctx context.Context, + groupID string, + pageNumber, showNumber int32, +) (uint32, []*relationTb.GroupMemberModel, error) { total, members, err := s.GroupDatabase.PageGetGroupMember(ctx, groupID, pageNumber, showNumber) if err != nil { return 0, nil, err diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index 0d242201d..56bc6cfad 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -15,6 +15,8 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mw/specialerror" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" + "google.golang.org/grpc" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/controller" @@ -29,7 +31,6 @@ import ( pbGroup "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/group" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "google.golang.org/grpc" ) func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { @@ -55,13 +56,17 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e pbGroup.RegisterGroupServer(server, &groupServer{ GroupDatabase: database, User: userRpcClient, - Notification: notification.NewGroupNotificationSender(database, &msgRpcClient, func(ctx context.Context, userIDs []string) ([]notification.CommonUser, error) { - users, err := userRpcClient.GetUsersInfo(ctx, userIDs) - if err != nil { - return nil, err - } - return utils.Slice(users, func(e *sdkws.UserInfo) notification.CommonUser { return e }), nil - }), + Notification: notification.NewGroupNotificationSender( + database, + &msgRpcClient, + func(ctx context.Context, userIDs []string) ([]notification.CommonUser, error) { + users, err := userRpcClient.GetUsersInfo(ctx, userIDs) + if err != nil { + return nil, err + } + return utils.Slice(users, func(e *sdkws.UserInfo) notification.CommonUser { return e }), nil + }, + ), conversationRpcClient: conversationRpcClient, msgRpcClient: msgRpcClient, }) @@ -118,7 +123,16 @@ func (s *groupServer) GenGroupID(ctx context.Context, groupID *string) error { } } for i := 0; i < 10; i++ { - id := utils.Md5(strings.Join([]string{mcontext.GetOperationID(ctx), strconv.FormatInt(time.Now().UnixNano(), 10), strconv.Itoa(rand.Int())}, ",;,")) + id := utils.Md5( + strings.Join( + []string{ + mcontext.GetOperationID(ctx), + strconv.FormatInt(time.Now().UnixNano(), 10), + strconv.Itoa(rand.Int()), + }, + ",;,", + ), + ) bi := big.NewInt(0) bi.SetString(id[0:8], 16) id = bi.String() @@ -232,7 +246,10 @@ func (s *groupServer) CreateGroup(ctx context.Context, req *pbGroup.CreateGroupR return resp, nil } -func (s *groupServer) GetJoinedGroupList(ctx context.Context, req *pbGroup.GetJoinedGroupListReq) (*pbGroup.GetJoinedGroupListResp, error) { +func (s *groupServer) GetJoinedGroupList( + ctx context.Context, + req *pbGroup.GetJoinedGroupListReq, +) (*pbGroup.GetJoinedGroupListResp, error) { resp := &pbGroup.GetJoinedGroupListResp{} if err := tokenverify.CheckAccessV3(ctx, req.FromUserID); err != nil { return nil, err @@ -242,7 +259,8 @@ func (s *groupServer) GetJoinedGroupList(ctx context.Context, req *pbGroup.GetJo pageNumber = req.Pagination.PageNumber showNumber = req.Pagination.ShowNumber } - //total, members, err := s.GroupDatabase.PageGroupMember(ctx, nil, []string{req.FromUserID}, nil, pageNumber, showNumber) + // total, members, err := s.GroupDatabase.PageGroupMember(ctx, nil, []string{req.FromUserID}, nil, pageNumber, + // showNumber) total, members, err := s.GroupDatabase.PageGetJoinGroup(ctx, req.FromUserID, pageNumber, showNumber) if err != nil { return nil, err @@ -281,7 +299,10 @@ func (s *groupServer) GetJoinedGroupList(ctx context.Context, req *pbGroup.GetJo return resp, nil } -func (s *groupServer) InviteUserToGroup(ctx context.Context, req *pbGroup.InviteUserToGroupReq) (*pbGroup.InviteUserToGroupResp, error) { +func (s *groupServer) InviteUserToGroup( + ctx context.Context, + req *pbGroup.InviteUserToGroupReq, +) (*pbGroup.InviteUserToGroupResp, error) { resp := &pbGroup.InviteUserToGroupResp{} if len(req.InvitedUserIDs) == 0 { return nil, errs.ErrArgs.Wrap("user empty") @@ -382,7 +403,10 @@ func (s *groupServer) InviteUserToGroup(ctx context.Context, req *pbGroup.Invite return resp, nil } -func (s *groupServer) GetGroupAllMember(ctx context.Context, req *pbGroup.GetGroupAllMemberReq) (*pbGroup.GetGroupAllMemberResp, error) { +func (s *groupServer) GetGroupAllMember( + ctx context.Context, + req *pbGroup.GetGroupAllMemberReq, +) (*pbGroup.GetGroupAllMemberResp, error) { resp := &pbGroup.GetGroupAllMemberResp{} group, err := s.GroupDatabase.TakeGroup(ctx, req.GroupID) if err != nil { @@ -410,7 +434,10 @@ func (s *groupServer) GetGroupAllMember(ctx context.Context, req *pbGroup.GetGro return resp, nil } -func (s *groupServer) GetGroupMemberList(ctx context.Context, req *pbGroup.GetGroupMemberListReq) (*pbGroup.GetGroupMemberListResp, error) { +func (s *groupServer) GetGroupMemberList( + ctx context.Context, + req *pbGroup.GetGroupMemberListReq, +) (*pbGroup.GetGroupMemberListResp, error) { resp := &pbGroup.GetGroupMemberListResp{} total, members, err := s.PageGetGroupMember(ctx, req.GroupID, req.Pagination.PageNumber, req.Pagination.ShowNumber) log.ZDebug(ctx, "GetGroupMemberList", "total", total, "members", members, "length", len(members)) @@ -423,7 +450,10 @@ func (s *groupServer) GetGroupMemberList(ctx context.Context, req *pbGroup.GetGr return resp, nil } -func (s *groupServer) KickGroupMember(ctx context.Context, req *pbGroup.KickGroupMemberReq) (*pbGroup.KickGroupMemberResp, error) { +func (s *groupServer) KickGroupMember( + ctx context.Context, + req *pbGroup.KickGroupMemberReq, +) (*pbGroup.KickGroupMemberResp, error) { resp := &pbGroup.KickGroupMemberResp{} group, err := s.GroupDatabase.TakeGroup(ctx, req.GroupID) if err != nil { @@ -531,7 +561,10 @@ func (s *groupServer) KickGroupMember(ctx context.Context, req *pbGroup.KickGrou return resp, nil } -func (s *groupServer) GetGroupMembersInfo(ctx context.Context, req *pbGroup.GetGroupMembersInfoReq) (*pbGroup.GetGroupMembersInfoResp, error) { +func (s *groupServer) GetGroupMembersInfo( + ctx context.Context, + req *pbGroup.GetGroupMembersInfoReq, +) (*pbGroup.GetGroupMembersInfoResp, error) { resp := &pbGroup.GetGroupMembersInfoResp{} if len(req.UserIDs) == 0 { return nil, errs.ErrArgs.Wrap("userIDs empty") @@ -558,7 +591,10 @@ func (s *groupServer) GetGroupMembersInfo(ctx context.Context, req *pbGroup.GetG return resp, nil } -func (s *groupServer) GetGroupApplicationList(ctx context.Context, req *pbGroup.GetGroupApplicationListReq) (*pbGroup.GetGroupApplicationListResp, error) { +func (s *groupServer) GetGroupApplicationList( + ctx context.Context, + req *pbGroup.GetGroupApplicationListReq, +) (*pbGroup.GetGroupApplicationListResp, error) { pageNumber, showNumber := utils.GetPage(req.Pagination) groupIDs, err := s.GroupDatabase.FindUserManagedGroupID(ctx, req.FromUserID) @@ -609,12 +645,19 @@ func (s *groupServer) GetGroupApplicationList(ctx context.Context, req *pbGroup. return e.GroupID }) resp.GroupRequests = utils.Slice(groupRequests, func(e *relationTb.GroupRequestModel) *sdkws.GroupRequest { - return convert.Db2PbGroupRequest(e, userMap[e.UserID], convert.Db2PbGroupInfo(groupMap[e.GroupID], ownerMap[e.GroupID].UserID, groupMemberNumMap[e.GroupID])) + return convert.Db2PbGroupRequest( + e, + userMap[e.UserID], + convert.Db2PbGroupInfo(groupMap[e.GroupID], ownerMap[e.GroupID].UserID, groupMemberNumMap[e.GroupID]), + ) }) return resp, nil } -func (s *groupServer) GetGroupsInfo(ctx context.Context, req *pbGroup.GetGroupsInfoReq) (*pbGroup.GetGroupsInfoResp, error) { +func (s *groupServer) GetGroupsInfo( + ctx context.Context, + req *pbGroup.GetGroupsInfoReq, +) (*pbGroup.GetGroupsInfoResp, error) { resp := &pbGroup.GetGroupsInfoResp{} if len(req.GroupIDs) == 0 { return nil, errs.ErrArgs.Wrap("groupID is empty") @@ -644,7 +687,10 @@ func (s *groupServer) GetGroupsInfo(ctx context.Context, req *pbGroup.GetGroupsI return resp, nil } -func (s *groupServer) GroupApplicationResponse(ctx context.Context, req *pbGroup.GroupApplicationResponseReq) (*pbGroup.GroupApplicationResponseResp, error) { +func (s *groupServer) GroupApplicationResponse( + ctx context.Context, + req *pbGroup.GroupApplicationResponseReq, +) (*pbGroup.GroupApplicationResponseResp, error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") if !utils.Contain(req.HandleResult, constant.GroupResponseAgree, constant.GroupResponseRefuse) { return nil, errs.ErrArgs.Wrap("HandleResult unknown") @@ -716,7 +762,10 @@ func (s *groupServer) GroupApplicationResponse(ctx context.Context, req *pbGroup return &pbGroup.GroupApplicationResponseResp{}, nil } -func (s *groupServer) JoinGroup(ctx context.Context, req *pbGroup.JoinGroupReq) (resp *pbGroup.JoinGroupResp, err error) { +func (s *groupServer) JoinGroup( + ctx context.Context, + req *pbGroup.JoinGroupReq, +) (resp *pbGroup.JoinGroupResp, err error) { defer log.ZInfo(ctx, "JoinGroup.Return") user, err := s.User.GetUserInfo(ctx, req.InviterUserID) if err != nil { @@ -816,7 +865,10 @@ func (s *groupServer) deleteMemberAndSetConversationSeq(ctx context.Context, gro return s.conversationRpcClient.SetConversationMaxSeq(ctx, userIDs, conevrsationID, maxSeq) } -func (s *groupServer) SetGroupInfo(ctx context.Context, req *pbGroup.SetGroupInfoReq) (*pbGroup.SetGroupInfoResp, error) { +func (s *groupServer) SetGroupInfo( + ctx context.Context, + req *pbGroup.SetGroupInfoReq, +) (*pbGroup.SetGroupInfoResp, error) { var opMember *relationTb.GroupMemberModel if !tokenverify.IsAppManagerUid(ctx) { var err error @@ -866,7 +918,10 @@ func (s *groupServer) SetGroupInfo(ctx context.Context, req *pbGroup.SetGroupInf var num int if req.GroupInfoForSet.Notification != "" { num++ - s.Notification.GroupInfoSetAnnouncementNotification(ctx, &sdkws.GroupInfoSetAnnouncementTips{Group: tips.Group, OpUser: tips.OpUser}) + s.Notification.GroupInfoSetAnnouncementNotification( + ctx, + &sdkws.GroupInfoSetAnnouncementTips{Group: tips.Group, OpUser: tips.OpUser}, + ) } switch len(data) - num { @@ -883,7 +938,10 @@ func (s *groupServer) SetGroupInfo(ctx context.Context, req *pbGroup.SetGroupInf return resp, nil } -func (s *groupServer) TransferGroupOwner(ctx context.Context, req *pbGroup.TransferGroupOwnerReq) (*pbGroup.TransferGroupOwnerResp, error) { +func (s *groupServer) TransferGroupOwner( + ctx context.Context, + req *pbGroup.TransferGroupOwnerReq, +) (*pbGroup.TransferGroupOwnerResp, error) { resp := &pbGroup.TransferGroupOwnerResp{} group, err := s.GroupDatabase.TakeGroup(ctx, req.GroupID) if err != nil { @@ -962,9 +1020,20 @@ func (s *groupServer) GetGroups(ctx context.Context, req *pbGroup.GetGroupsReq) return resp, nil } -func (s *groupServer) GetGroupMembersCMS(ctx context.Context, req *pbGroup.GetGroupMembersCMSReq) (*pbGroup.GetGroupMembersCMSResp, error) { +func (s *groupServer) GetGroupMembersCMS( + ctx context.Context, + req *pbGroup.GetGroupMembersCMSReq, +) (*pbGroup.GetGroupMembersCMSResp, error) { resp := &pbGroup.GetGroupMembersCMSResp{} - total, members, err := s.GroupDatabase.SearchGroupMember(ctx, req.UserName, []string{req.GroupID}, nil, nil, req.Pagination.PageNumber, req.Pagination.ShowNumber) + total, members, err := s.GroupDatabase.SearchGroupMember( + ctx, + req.UserName, + []string{req.GroupID}, + nil, + nil, + req.Pagination.PageNumber, + req.Pagination.ShowNumber, + ) if err != nil { return nil, err } @@ -984,7 +1053,10 @@ func (s *groupServer) GetGroupMembersCMS(ctx context.Context, req *pbGroup.GetGr return resp, nil } -func (s *groupServer) GetUserReqApplicationList(ctx context.Context, req *pbGroup.GetUserReqApplicationListReq) (*pbGroup.GetUserReqApplicationListResp, error) { +func (s *groupServer) GetUserReqApplicationList( + ctx context.Context, + req *pbGroup.GetUserReqApplicationListReq, +) (*pbGroup.GetUserReqApplicationListResp, error) { resp := &pbGroup.GetUserReqApplicationListResp{} user, err := s.User.GetPublicUserInfo(ctx, req.UserID) if err != nil { @@ -1031,12 +1103,19 @@ func (s *groupServer) GetUserReqApplicationList(ctx context.Context, req *pbGrou return nil, err } resp.GroupRequests = utils.Slice(requests, func(e *relationTb.GroupRequestModel) *sdkws.GroupRequest { - return convert.Db2PbGroupRequest(e, user, convert.Db2PbGroupInfo(groupMap[e.GroupID], ownerMap[e.GroupID].UserID, uint32(groupMemberNum[e.GroupID]))) + return convert.Db2PbGroupRequest( + e, + user, + convert.Db2PbGroupInfo(groupMap[e.GroupID], ownerMap[e.GroupID].UserID, uint32(groupMemberNum[e.GroupID])), + ) }) return resp, nil } -func (s *groupServer) DismissGroup(ctx context.Context, req *pbGroup.DismissGroupReq) (*pbGroup.DismissGroupResp, error) { +func (s *groupServer) DismissGroup( + ctx context.Context, + req *pbGroup.DismissGroupReq, +) (*pbGroup.DismissGroupResp, error) { defer log.ZInfo(ctx, "DismissGroup.return") resp := &pbGroup.DismissGroupResp{} owner, err := s.TakeGroupOwner(ctx, req.GroupID) @@ -1085,7 +1164,10 @@ func (s *groupServer) DismissGroup(ctx context.Context, req *pbGroup.DismissGrou return resp, nil } -func (s *groupServer) MuteGroupMember(ctx context.Context, req *pbGroup.MuteGroupMemberReq) (*pbGroup.MuteGroupMemberResp, error) { +func (s *groupServer) MuteGroupMember( + ctx context.Context, + req *pbGroup.MuteGroupMemberReq, +) (*pbGroup.MuteGroupMemberResp, error) { resp := &pbGroup.MuteGroupMemberResp{} //if err := tokenverify.CheckAccessV3(ctx, req.UserID); err != nil { // return nil, err @@ -1120,7 +1202,10 @@ func (s *groupServer) MuteGroupMember(ctx context.Context, req *pbGroup.MuteGrou return resp, nil } -func (s *groupServer) CancelMuteGroupMember(ctx context.Context, req *pbGroup.CancelMuteGroupMemberReq) (*pbGroup.CancelMuteGroupMemberResp, error) { +func (s *groupServer) CancelMuteGroupMember( + ctx context.Context, + req *pbGroup.CancelMuteGroupMemberReq, +) (*pbGroup.CancelMuteGroupMemberResp, error) { resp := &pbGroup.CancelMuteGroupMemberResp{} //member, err := s.GroupDatabase.TakeGroupMember(ctx, req.GroupID, req.UserID) //if err != nil { @@ -1132,7 +1217,8 @@ func (s *groupServer) CancelMuteGroupMember(ctx context.Context, req *pbGroup.Ca // return nil, err // } // if opMember.RoleLevel <= member.RoleLevel { - // return nil, errs.ErrNoPermission.Wrap(fmt.Sprintf("self RoleLevel %d target %d", opMember.RoleLevel, member.RoleLevel)) + // return nil, errs.ErrNoPermission.Wrap(fmt.Sprintf("self RoleLevel %d target %d", opMember.RoleLevel, + // member.RoleLevel)) // } //} //if err := tokenverify.CheckAccessV3(ctx, req.UserID); err != nil { @@ -1180,7 +1266,10 @@ func (s *groupServer) MuteGroup(ctx context.Context, req *pbGroup.MuteGroupReq) return resp, nil } -func (s *groupServer) CancelMuteGroup(ctx context.Context, req *pbGroup.CancelMuteGroupReq) (*pbGroup.CancelMuteGroupResp, error) { +func (s *groupServer) CancelMuteGroup( + ctx context.Context, + req *pbGroup.CancelMuteGroupReq, +) (*pbGroup.CancelMuteGroupResp, error) { resp := &pbGroup.CancelMuteGroupResp{} if err := s.CheckGroupAdmin(ctx, req.GroupID); err != nil { return nil, err @@ -1192,7 +1281,10 @@ func (s *groupServer) CancelMuteGroup(ctx context.Context, req *pbGroup.CancelMu return resp, nil } -func (s *groupServer) SetGroupMemberInfo(ctx context.Context, req *pbGroup.SetGroupMemberInfoReq) (*pbGroup.SetGroupMemberInfoResp, error) { +func (s *groupServer) SetGroupMemberInfo( + ctx context.Context, + req *pbGroup.SetGroupMemberInfoReq, +) (*pbGroup.SetGroupMemberInfoResp, error) { resp := &pbGroup.SetGroupMemberInfoResp{} if len(req.Members) == 0 { return nil, errs.ErrArgs.Wrap("members empty") @@ -1219,9 +1311,11 @@ func (s *groupServer) SetGroupMemberInfo(ctx context.Context, req *pbGroup.SetGr delete(duplicateMap, [...]string{member.GroupID, member.UserID}) } if len(duplicateMap) > 0 { - return nil, errs.ErrArgs.Wrap("user not found" + strings.Join(utils.Slice(utils.Keys(duplicateMap), func(e [2]string) string { - return fmt.Sprintf("[group: %s user: %s]", e[0], e[1]) - }), ",")) + return nil, errs.ErrArgs.Wrap( + "user not found" + strings.Join(utils.Slice(utils.Keys(duplicateMap), func(e [2]string) string { + return fmt.Sprintf("[group: %s user: %s]", e[0], e[1]) + }), ","), + ) } memberMap := utils.SliceToMap(members, func(e *relationTb.GroupMemberModel) [2]string { return [...]string{e.GroupID, e.UserID} @@ -1251,7 +1345,9 @@ func (s *groupServer) SetGroupMemberInfo(ctx context.Context, req *pbGroup.SetGr } dbMember, ok := memberMap[[...]string{member.GroupID, member.UserID}] if !ok { - return nil, errs.ErrRecordNotFound.Wrap(fmt.Sprintf("user %s not in group %s", member.UserID, member.GroupID)) + return nil, errs.ErrRecordNotFound.Wrap( + fmt.Sprintf("user %s not in group %s", member.UserID, member.GroupID), + ) } //if opMember.RoleLevel == constant.GroupOwner { // continue @@ -1313,14 +1409,25 @@ func (s *groupServer) SetGroupMemberInfo(ctx context.Context, req *pbGroup.SetGr if member.Nickname != nil || member.FaceURL != nil || member.Ex != nil { log.ZDebug(ctx, "setGroupMemberInfo notification", "member", member.UserID) if err := s.Notification.GroupMemberInfoSetNotification(ctx, member.GroupID, member.UserID); err != nil { - log.ZError(ctx, "setGroupMemberInfo notification failed", err, "member", member.UserID, "groupID", member.GroupID) + log.ZError( + ctx, + "setGroupMemberInfo notification failed", + err, + "member", + member.UserID, + "groupID", + member.GroupID, + ) } } } return resp, nil } -func (s *groupServer) GetGroupAbstractInfo(ctx context.Context, req *pbGroup.GetGroupAbstractInfoReq) (*pbGroup.GetGroupAbstractInfoResp, error) { +func (s *groupServer) GetGroupAbstractInfo( + ctx context.Context, + req *pbGroup.GetGroupAbstractInfoReq, +) (*pbGroup.GetGroupAbstractInfoResp, error) { resp := &pbGroup.GetGroupAbstractInfoResp{} if len(req.GroupIDs) == 0 { return nil, errs.ErrArgs.Wrap("groupIDs empty") @@ -1351,7 +1458,10 @@ func (s *groupServer) GetGroupAbstractInfo(ctx context.Context, req *pbGroup.Get return resp, nil } -func (s *groupServer) GetUserInGroupMembers(ctx context.Context, req *pbGroup.GetUserInGroupMembersReq) (*pbGroup.GetUserInGroupMembersResp, error) { +func (s *groupServer) GetUserInGroupMembers( + ctx context.Context, + req *pbGroup.GetUserInGroupMembersReq, +) (*pbGroup.GetUserInGroupMembersResp, error) { resp := &pbGroup.GetUserInGroupMembersResp{} if len(req.GroupIDs) == 0 { return nil, errs.ErrArgs.Wrap("groupIDs empty") @@ -1375,7 +1485,10 @@ func (s *groupServer) GetUserInGroupMembers(ctx context.Context, req *pbGroup.Ge return resp, nil } -func (s *groupServer) GetGroupMemberUserIDs(ctx context.Context, req *pbGroup.GetGroupMemberUserIDsReq) (resp *pbGroup.GetGroupMemberUserIDsResp, err error) { +func (s *groupServer) GetGroupMemberUserIDs( + ctx context.Context, + req *pbGroup.GetGroupMemberUserIDsReq, +) (resp *pbGroup.GetGroupMemberUserIDsResp, err error) { resp = &pbGroup.GetGroupMemberUserIDsResp{} resp.UserIDs, err = s.GroupDatabase.FindGroupMemberUserID(ctx, req.GroupID) if err != nil { @@ -1384,7 +1497,10 @@ func (s *groupServer) GetGroupMemberUserIDs(ctx context.Context, req *pbGroup.Ge return resp, nil } -func (s *groupServer) GetGroupMemberRoleLevel(ctx context.Context, req *pbGroup.GetGroupMemberRoleLevelReq) (*pbGroup.GetGroupMemberRoleLevelResp, error) { +func (s *groupServer) GetGroupMemberRoleLevel( + ctx context.Context, + req *pbGroup.GetGroupMemberRoleLevelReq, +) (*pbGroup.GetGroupMemberRoleLevelResp, error) { resp := &pbGroup.GetGroupMemberRoleLevelResp{} if len(req.RoleLevels) == 0 { return nil, errs.ErrArgs.Wrap("RoleLevels empty") diff --git a/internal/rpc/group/super_group.go b/internal/rpc/group/super_group.go index 8d8937db4..ab4cbd5ff 100644 --- a/internal/rpc/group/super_group.go +++ b/internal/rpc/group/super_group.go @@ -15,7 +15,10 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" ) -func (s *groupServer) GetJoinedSuperGroupList(ctx context.Context, req *pbGroup.GetJoinedSuperGroupListReq) (*pbGroup.GetJoinedSuperGroupListResp, error) { +func (s *groupServer) GetJoinedSuperGroupList( + ctx context.Context, + req *pbGroup.GetJoinedSuperGroupListReq, +) (*pbGroup.GetJoinedSuperGroupListResp, error) { resp := &pbGroup.GetJoinedSuperGroupListResp{} groupIDs, err := s.GroupDatabase.FindJoinSuperGroup(ctx, req.UserID) if err != nil { @@ -48,16 +51,22 @@ func (s *groupServer) GetJoinedSuperGroupList(ctx context.Context, req *pbGroup. if err != nil { return nil, err } - superGroupMemberMap := utils.SliceToMapAny(superGroupMembers, func(e *unrelation.SuperGroupModel) (string, []string) { - return e.GroupID, e.MemberIDs - }) + superGroupMemberMap := utils.SliceToMapAny( + superGroupMembers, + func(e *unrelation.SuperGroupModel) (string, []string) { + return e.GroupID, e.MemberIDs + }, + ) resp.Groups = utils.Slice(groupIDs, func(groupID string) *sdkws.GroupInfo { return convert.Db2PbGroupInfo(groupMap[groupID], ownerMap[groupID].UserID, uint32(len(superGroupMemberMap))) }) return resp, nil } -func (s *groupServer) GetSuperGroupsInfo(ctx context.Context, req *pbGroup.GetSuperGroupsInfoReq) (resp *pbGroup.GetSuperGroupsInfoResp, err error) { +func (s *groupServer) GetSuperGroupsInfo( + ctx context.Context, + req *pbGroup.GetSuperGroupsInfoReq, +) (resp *pbGroup.GetSuperGroupsInfoResp, err error) { resp = &pbGroup.GetSuperGroupsInfoResp{} if len(req.GroupIDs) == 0 { return nil, errs.ErrArgs.Wrap("groupIDs empty") @@ -70,9 +79,12 @@ func (s *groupServer) GetSuperGroupsInfo(ctx context.Context, req *pbGroup.GetSu if err != nil { return nil, err } - superGroupMemberMap := utils.SliceToMapAny(superGroupMembers, func(e *unrelation.SuperGroupModel) (string, []string) { - return e.GroupID, e.MemberIDs - }) + superGroupMemberMap := utils.SliceToMapAny( + superGroupMembers, + func(e *unrelation.SuperGroupModel) (string, []string) { + return e.GroupID, e.MemberIDs + }, + ) owners, err := s.FindGroupMember(ctx, req.GroupIDs, nil, []int32{constant.GroupOwner}) if err != nil { return nil, err diff --git a/internal/rpc/msg/as_read.go b/internal/rpc/msg/as_read.go index f34e5fd2b..c4707c489 100644 --- a/internal/rpc/msg/as_read.go +++ b/internal/rpc/msg/as_read.go @@ -3,15 +3,19 @@ package msg import ( "context" + "github.com/redis/go-redis/v9" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" - "github.com/redis/go-redis/v9" ) -func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *msg.GetConversationsHasReadAndMaxSeqReq) (*msg.GetConversationsHasReadAndMaxSeqResp, error) { +func (m *msgServer) GetConversationsHasReadAndMaxSeq( + ctx context.Context, + req *msg.GetConversationsHasReadAndMaxSeqReq, +) (*msg.GetConversationsHasReadAndMaxSeqResp, error) { conversationIDs, err := m.ConversationLocalCache.GetConversationIDs(ctx, req.UserID) if err != nil { return nil, err @@ -47,7 +51,10 @@ func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *m return resp, nil } -func (m *msgServer) SetConversationHasReadSeq(ctx context.Context, req *msg.SetConversationHasReadSeqReq) (resp *msg.SetConversationHasReadSeqResp, err error) { +func (m *msgServer) SetConversationHasReadSeq( + ctx context.Context, + req *msg.SetConversationHasReadSeqReq, +) (resp *msg.SetConversationHasReadSeqResp, err error) { maxSeq, err := m.MsgDatabase.GetMaxSeq(ctx, req.ConversationID) if err != nil { return @@ -64,7 +71,10 @@ func (m *msgServer) SetConversationHasReadSeq(ctx context.Context, req *msg.SetC return &msg.SetConversationHasReadSeqResp{}, nil } -func (m *msgServer) MarkMsgsAsRead(ctx context.Context, req *msg.MarkMsgsAsReadReq) (resp *msg.MarkMsgsAsReadResp, err error) { +func (m *msgServer) MarkMsgsAsRead( + ctx context.Context, + req *msg.MarkMsgsAsReadReq, +) (resp *msg.MarkMsgsAsReadResp, err error) { if len(req.Seqs) < 1 { return nil, errs.ErrArgs.Wrap("seqs must not be empty") } @@ -99,7 +109,10 @@ func (m *msgServer) MarkMsgsAsRead(ctx context.Context, req *msg.MarkMsgsAsReadR return &msg.MarkMsgsAsReadResp{}, nil } -func (m *msgServer) MarkConversationAsRead(ctx context.Context, req *msg.MarkConversationAsReadReq) (resp *msg.MarkConversationAsReadResp, err error) { +func (m *msgServer) MarkConversationAsRead( + ctx context.Context, + req *msg.MarkConversationAsReadReq, +) (resp *msg.MarkConversationAsReadResp, err error) { conversation, err := m.Conversation.GetConversation(ctx, req.UserID, req.ConversationID) if err != nil { return @@ -136,7 +149,14 @@ func (m *msgServer) MarkConversationAsRead(ctx context.Context, req *msg.MarkCon return &msg.MarkConversationAsReadResp{}, nil } -func (m *msgServer) sendMarkAsReadNotification(ctx context.Context, conversationID string, sesstionType int32, sendID, recvID string, seqs []int64, hasReadSeq int64) error { +func (m *msgServer) sendMarkAsReadNotification( + ctx context.Context, + conversationID string, + sesstionType int32, + sendID, recvID string, + seqs []int64, + hasReadSeq int64, +) error { tips := &sdkws.MarkAsReadTips{ MarkAsReadUserID: sendID, ConversationID: conversationID, diff --git a/internal/rpc/msg/delete.go b/internal/rpc/msg/delete.go index 1b8306614..97efff41b 100644 --- a/internal/rpc/msg/delete.go +++ b/internal/rpc/msg/delete.go @@ -27,7 +27,10 @@ func (m *msgServer) validateDeleteSyncOpt(opt *msg.DeleteSyncOpt) (isSyncSelf, i return opt.IsSyncSelf, opt.IsSyncOther } -func (m *msgServer) ClearConversationsMsg(ctx context.Context, req *msg.ClearConversationsMsgReq) (*msg.ClearConversationsMsgResp, error) { +func (m *msgServer) ClearConversationsMsg( + ctx context.Context, + req *msg.ClearConversationsMsgReq, +) (*msg.ClearConversationsMsgResp, error) { if err := tokenverify.CheckAccessV3(ctx, req.UserID); err != nil { return nil, err } @@ -37,7 +40,10 @@ func (m *msgServer) ClearConversationsMsg(ctx context.Context, req *msg.ClearCon return &msg.ClearConversationsMsgResp{}, nil } -func (m *msgServer) UserClearAllMsg(ctx context.Context, req *msg.UserClearAllMsgReq) (*msg.UserClearAllMsgResp, error) { +func (m *msgServer) UserClearAllMsg( + ctx context.Context, + req *msg.UserClearAllMsgReq, +) (*msg.UserClearAllMsgResp, error) { if err := tokenverify.CheckAccessV3(ctx, req.UserID); err != nil { return nil, err } @@ -66,7 +72,14 @@ func (m *msgServer) DeleteMsgs(ctx context.Context, req *msg.DeleteMsgsReq) (*ms return nil, err } tips := &sdkws.DeleteMsgsTips{UserID: req.UserID, ConversationID: req.ConversationID, Seqs: req.Seqs} - m.notificationSender.NotificationWithSesstionType(ctx, req.UserID, m.conversationAndGetRecvID(conversations[0], req.UserID), constant.DeleteMsgsNotification, conversations[0].ConversationType, tips) + m.notificationSender.NotificationWithSesstionType( + ctx, + req.UserID, + m.conversationAndGetRecvID(conversations[0], req.UserID), + constant.DeleteMsgsNotification, + conversations[0].ConversationType, + tips, + ) } else { if err := m.MsgDatabase.DeleteUserMsgsBySeqs(ctx, req.UserID, req.ConversationID, req.Seqs); err != nil { return nil, err @@ -79,7 +92,10 @@ func (m *msgServer) DeleteMsgs(ctx context.Context, req *msg.DeleteMsgsReq) (*ms return &msg.DeleteMsgsResp{}, nil } -func (m *msgServer) DeleteMsgPhysicalBySeq(ctx context.Context, req *msg.DeleteMsgPhysicalBySeqReq) (*msg.DeleteMsgPhysicalBySeqResp, error) { +func (m *msgServer) DeleteMsgPhysicalBySeq( + ctx context.Context, + req *msg.DeleteMsgPhysicalBySeqReq, +) (*msg.DeleteMsgPhysicalBySeqResp, error) { err := m.MsgDatabase.DeleteMsgsPhysicalBySeqs(ctx, req.ConversationID, req.Seqs) if err != nil { return nil, err @@ -87,20 +103,36 @@ func (m *msgServer) DeleteMsgPhysicalBySeq(ctx context.Context, req *msg.DeleteM return &msg.DeleteMsgPhysicalBySeqResp{}, nil } -func (m *msgServer) DeleteMsgPhysical(ctx context.Context, req *msg.DeleteMsgPhysicalReq) (*msg.DeleteMsgPhysicalResp, error) { +func (m *msgServer) DeleteMsgPhysical( + ctx context.Context, + req *msg.DeleteMsgPhysicalReq, +) (*msg.DeleteMsgPhysicalResp, error) { if err := tokenverify.CheckAdmin(ctx); err != nil { return nil, err } remainTime := utils.GetCurrentTimestampBySecond() - req.Timestamp for _, conversationID := range req.ConversationIDs { if err := m.MsgDatabase.DeleteConversationMsgsAndSetMinSeq(ctx, conversationID, remainTime); err != nil { - log.ZWarn(ctx, "DeleteConversationMsgsAndSetMinSeq error", err, "conversationID", conversationID, "err", err) + log.ZWarn( + ctx, + "DeleteConversationMsgsAndSetMinSeq error", + err, + "conversationID", + conversationID, + "err", + err, + ) } } return &msg.DeleteMsgPhysicalResp{}, nil } -func (m *msgServer) clearConversation(ctx context.Context, conversationIDs []string, userID string, deleteSyncOpt *msg.DeleteSyncOpt) error { +func (m *msgServer) clearConversation( + ctx context.Context, + conversationIDs []string, + userID string, + deleteSyncOpt *msg.DeleteSyncOpt, +) error { defer log.ZDebug(ctx, "clearConversation return line") conversations, err := m.Conversation.GetConversationsByConversationID(ctx, conversationIDs) if err != nil { @@ -125,7 +157,14 @@ func (m *msgServer) clearConversation(ctx context.Context, conversationIDs []str // notification 2 self if isSyncSelf { tips := &sdkws.ClearConversationTips{UserID: userID, ConversationIDs: existConversationIDs} - m.notificationSender.NotificationWithSesstionType(ctx, userID, userID, constant.ClearConversationNotification, constant.SingleChatType, tips) + m.notificationSender.NotificationWithSesstionType( + ctx, + userID, + userID, + constant.ClearConversationNotification, + constant.SingleChatType, + tips, + ) } } else { if err := m.MsgDatabase.SetMinSeqs(ctx, m.getMinSeqs(maxSeqs)); err != nil { diff --git a/internal/rpc/msg/extend_msg.go b/internal/rpc/msg/extend_msg.go index 1da53c322..7ae56c71e 100644 --- a/internal/rpc/msg/extend_msg.go +++ b/internal/rpc/msg/extend_msg.go @@ -7,7 +7,10 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" ) -func (m *msgServer) SetMessageReactionExtensions(ctx context.Context, req *msg.SetMessageReactionExtensionsReq) (resp *msg.SetMessageReactionExtensionsResp, err error) { +func (m *msgServer) SetMessageReactionExtensions( + ctx context.Context, + req *msg.SetMessageReactionExtensionsReq, +) (resp *msg.SetMessageReactionExtensionsResp, err error) { //resp = &msg.SetMessageReactionExtensionsResp{} ////resp.ClientMsgID = req.ClientMsgID ////resp.MsgFirstModifyTime = req.MsgFirstModifyTime @@ -18,7 +21,8 @@ func (m *msgServer) SetMessageReactionExtensions(ctx context.Context, req *msg.S ////if ExternalExtension //if req.IsExternalExtensions { // resp.MsgFirstModifyTime = req.MsgFirstModifyTime - // notification.ExtendMessageUpdatedNotification(req.OperationID, req.OpUserID, req.conversationID, req.SessionType, req, &resp, !req.IsReact, false) + // notification.ExtendMessageUpdatedNotification(req.OperationID, req.OpUserID, req.conversationID, + // req.SessionType, req, &resp, !req.IsReact, false) // return resp, nil //} //isExists, err := m.MsgDatabase.JudgeMessageReactionExist(ctx, req.ClientMsgID, req.SessionType) @@ -35,12 +39,14 @@ func (m *msgServer) SetMessageReactionExtensions(ctx context.Context, req *msg.S // return nil, err // } // v.LatestUpdateTime = utils.GetCurrentTimestampByMill() - // if err := m.MsgDatabase.SetMessageTypeKeyValue(ctx, req.ClientMsgID, req.SessionType, k, utils.StructToJsonString(v)); err != nil { + // if err := m.MsgDatabase.SetMessageTypeKeyValue(ctx, req.ClientMsgID, req.SessionType, k, + // utils.StructToJsonString(v)); err != nil { // return nil, err // } // } // resp.IsReact = true - // _, err := m.MsgDatabase.SetMessageReactionExpire(ctx, req.ClientMsgID, req.SessionType, time.Duration(24*3)*time.Hour) + // _, err := m.MsgDatabase.SetMessageReactionExpire(ctx, req.ClientMsgID, req.SessionType, + // time.Duration(24*3)*time.Hour) // if err != nil { // return nil, err // } @@ -49,7 +55,8 @@ func (m *msgServer) SetMessageReactionExtensions(ctx context.Context, req *msg.S // if err != nil { // return nil, err // } - // mongoValue, err := m.MsgDatabase.GetExtendMsg(ctx, req.conversationID, req.SessionType, req.ClientMsgID, req.MsgFirstModifyTime) + // mongoValue, err := m.MsgDatabase.GetExtendMsg(ctx, req.conversationID, req.SessionType, req.ClientMsgID, + // req.MsgFirstModifyTime) // if err != nil { // return nil, err // } @@ -69,7 +76,8 @@ func (m *msgServer) SetMessageReactionExtensions(ctx context.Context, req *msg.S // temp.LatestUpdateTime = utils.GetCurrentTimestampByMill() // setValue[k] = temp // } - // err = db.DB.InsertOrUpdateReactionExtendMsgSet(req.conversationID, req.SessionType, req.ClientMsgID, req.MsgFirstModifyTime, setValue) + // err = db.DB.InsertOrUpdateReactionExtendMsgSet(req.conversationID, req.SessionType, req.ClientMsgID, + // req.MsgFirstModifyTime, setValue) // if err != nil { // for _, value := range setValue { // temp := new(msg.KeyValueResp) @@ -112,7 +120,7 @@ func (m *msgServer) SetMessageReactionExtensions(ctx context.Context, req *msg.S // continue // } else { // v.LatestUpdateTime = utils.GetCurrentTimestampByMill() - // newerr := db.DB.SetMessageTypeKeyValue(req.ClientMsgID, req.SessionType, k, utils.StructToJsonString(v)) + // newerr := db.DB.SetMessageTypeKeyValue(req.ClientMsgID, req.SessionType, k, utils.StructToJsonString(v)) // if newerr != nil { // setKeyResultInfo(&resp, 201, newerr.Error(), req.ClientMsgID, k, temp) // continue @@ -124,18 +132,28 @@ func (m *msgServer) SetMessageReactionExtensions(ctx context.Context, req *msg.S //} //if !isExists { // if !req.IsReact { - // notification.ExtendMessageUpdatedNotification(req.OperationID, req.OpUserID, req.conversationID, req.SessionType, req, &resp, true, true) + // notification.ExtendMessageUpdatedNotification(req.OperationID, req.OpUserID, req.conversationID, + // req.SessionType, req, &resp, true, true) // } else { - // notification.ExtendMessageUpdatedNotification(req.OperationID, req.OpUserID, req.conversationID, req.SessionType, req, &resp, false, false) + // notification.ExtendMessageUpdatedNotification(req.OperationID, req.OpUserID, req.conversationID, + // req.SessionType, req, &resp, false, false) // } //} else { - // notification.ExtendMessageUpdatedNotification(req.OperationID, req.OpUserID, req.conversationID, req.SessionType, req, &resp, false, true) + // notification.ExtendMessageUpdatedNotification(req.OperationID, req.OpUserID, req.conversationID, + // req.SessionType, req, &resp, false, true) //} //log.Debug(req.OperationID, utils.GetSelfFuncName(), "m return is:", resp.String()) return resp, nil } -func (m *msgServer) setKeyResultInfo(ctx context.Context, r *msg.SetMessageReactionExtensionsResp, errCode int32, errMsg, clientMsgID, typeKey string, keyValue *sdkws.KeyValue) { + +func (m *msgServer) setKeyResultInfo( + ctx context.Context, + r *msg.SetMessageReactionExtensionsResp, + errCode int32, + errMsg, clientMsgID, typeKey string, + keyValue *sdkws.KeyValue, +) { temp := new(msg.KeyValueResp) temp.KeyValue = keyValue temp.ErrCode = errCode @@ -143,7 +161,14 @@ func (m *msgServer) setKeyResultInfo(ctx context.Context, r *msg.SetMessageReact r.Result = append(r.Result, temp) _ = m.MessageLocker.UnLockMessageTypeKey(ctx, clientMsgID, typeKey) } -func (m *msgServer) setDeleteKeyResultInfo(ctx context.Context, r *msg.DeleteMessagesReactionExtensionsResp, errCode int32, errMsg, clientMsgID, typeKey string, keyValue *sdkws.KeyValue) { + +func (m *msgServer) setDeleteKeyResultInfo( + ctx context.Context, + r *msg.DeleteMessagesReactionExtensionsResp, + errCode int32, + errMsg, clientMsgID, typeKey string, + keyValue *sdkws.KeyValue, +) { temp := new(msg.KeyValueResp) temp.KeyValue = keyValue temp.ErrCode = errCode @@ -152,7 +177,10 @@ func (m *msgServer) setDeleteKeyResultInfo(ctx context.Context, r *msg.DeleteMes _ = m.MessageLocker.UnLockMessageTypeKey(ctx, clientMsgID, typeKey) } -func (m *msgServer) GetMessagesReactionExtensions(ctx context.Context, req *msg.GetMessagesReactionExtensionsReq) (resp *msg.GetMessagesReactionExtensionsResp, err error) { +func (m *msgServer) GetMessagesReactionExtensions( + ctx context.Context, + req *msg.GetMessagesReactionExtensionsReq, +) (resp *msg.GetMessagesReactionExtensionsResp, err error) { //log.Debug(req.OperationID, utils.GetSelfFuncName(), "m args is:", req.String()) //var rResp msg.GetMessageListReactionExtensionsResp //for _, messageValue := range req.MessageReactionKeyList { @@ -183,7 +211,8 @@ func (m *msgServer) GetMessagesReactionExtensions(ctx context.Context, req *msg. // oneMessage.Pb2Model = keyMap // // } else { - // mongoValue, err := db.DB.GetExtendMsg(req.conversationID, req.SessionType, messageValue.ClientMsgID, messageValue.MsgFirstModifyTime) + // mongoValue, err := db.DB.GetExtendMsg(req.conversationID, req.SessionType, messageValue.ClientMsgID, + // messageValue.MsgFirstModifyTime) // if err != nil { // oneMessage.ErrCode = 100 // oneMessage.ErrMsg = err.Error() @@ -208,11 +237,17 @@ func (m *msgServer) GetMessagesReactionExtensions(ctx context.Context, req *msg. } -func (m *msgServer) AddMessageReactionExtensions(ctx context.Context, req *msg.ModifyMessageReactionExtensionsReq) (resp *msg.ModifyMessageReactionExtensionsResp, err error) { +func (m *msgServer) AddMessageReactionExtensions( + ctx context.Context, + req *msg.ModifyMessageReactionExtensionsReq, +) (resp *msg.ModifyMessageReactionExtensionsResp, err error) { return } -func (m *msgServer) DeleteMessageReactionExtensions(ctx context.Context, req *msg.DeleteMessagesReactionExtensionsReq) (resp *msg.DeleteMessagesReactionExtensionsResp, err error) { +func (m *msgServer) DeleteMessageReactionExtensions( + ctx context.Context, + req *msg.DeleteMessagesReactionExtensionsReq, +) (resp *msg.DeleteMessagesReactionExtensionsResp, err error) { //log.Debug(req.OperationID, utils.GetSelfFuncName(), "m args is:", req.String()) //var rResp msg.DeleteMessagesReactionExtensionsResp //callbackResp := notification.callbackDeleteMessageReactionExtensions(req) @@ -231,7 +266,8 @@ func (m *msgServer) DeleteMessageReactionExtensions(ctx context.Context, req *ms ////if ExternalExtension //if req.IsExternalExtensions { // rResp.Result = callbackResp.ResultReactionExtensionList - // notification.ExtendMessageDeleteNotification(req.OperationID, req.OpUserID, req.conversationID, req.SessionType, req, &rResp, false, false) + // notification.ExtendMessageDeleteNotification(req.OperationID, req.OpUserID, req.conversationID, req.SessionType, + // req, &rResp, false, false) // return &rResp, nil // //} @@ -303,7 +339,8 @@ func (m *msgServer) DeleteMessageReactionExtensions(ctx context.Context, req *ms // } // return &rResp, nil // } - // mongoValue, err := db.DB.GetExtendMsg(req.conversationID, req.SessionType, req.ClientMsgID, req.MsgFirstModifyTime) + // mongoValue, err := db.DB.GetExtendMsg(req.conversationID, req.SessionType, req.ClientMsgID, + // req.MsgFirstModifyTime) // if err != nil { // rResp.ErrCode = 200 // rResp.ErrMsg = err.Error() @@ -333,7 +370,8 @@ func (m *msgServer) DeleteMessageReactionExtensions(ctx context.Context, req *ms // temp.TypeKey = v.TypeKey // setValue[v.TypeKey] = temp // } - // err = db.DB.DeleteReactionExtendMsgSet(req.conversationID, req.SessionType, req.ClientMsgID, req.MsgFirstModifyTime, setValue) + // err = db.DB.DeleteReactionExtendMsgSet(req.conversationID, req.SessionType, req.ClientMsgID, + // req.MsgFirstModifyTime, setValue) // if err != nil { // for _, value := range setValue { // temp := new(msg.KeyValueResp) @@ -355,7 +393,8 @@ func (m *msgServer) DeleteMessageReactionExtensions(ctx context.Context, req *ms // } // //} - //notification.ExtendMessageDeleteNotification(req.OperationID, req.OpUserID, req.conversationID, req.SessionType, req, &rResp, false, isExists) + // notification.ExtendMessageDeleteNotification(req.OperationID, req.OpUserID, req.conversationID, req.SessionType, + // req, &rResp, false, isExists) //log.Debug(req.OperationID, utils.GetSelfFuncName(), "m return is:", rResp.String()) return resp, nil } diff --git a/internal/rpc/msg/extend_msg_callback.go b/internal/rpc/msg/extend_msg_callback.go index 216d65dfa..09fa40735 100644 --- a/internal/rpc/msg/extend_msg_callback.go +++ b/internal/rpc/msg/extend_msg_callback.go @@ -35,7 +35,10 @@ func callbackSetMessageReactionExtensions(ctx context.Context, setReq *msg.SetMe return nil } -func callbackDeleteMessageReactionExtensions(ctx context.Context, setReq *msg.DeleteMessagesReactionExtensionsReq) error { +func callbackDeleteMessageReactionExtensions( + ctx context.Context, + setReq *msg.DeleteMessagesReactionExtensionsReq, +) error { if !config.Config.Callback.CallbackAfterSendGroupMsg.Enable { return nil } diff --git a/internal/rpc/msg/msg_status.go b/internal/rpc/msg/msg_status.go index 3e1f9053b..b867e3065 100644 --- a/internal/rpc/msg/msg_status.go +++ b/internal/rpc/msg/msg_status.go @@ -8,7 +8,10 @@ import ( pbMsg "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" ) -func (m *msgServer) SetSendMsgStatus(ctx context.Context, req *pbMsg.SetSendMsgStatusReq) (*pbMsg.SetSendMsgStatusResp, error) { +func (m *msgServer) SetSendMsgStatus( + ctx context.Context, + req *pbMsg.SetSendMsgStatusReq, +) (*pbMsg.SetSendMsgStatusResp, error) { resp := &pbMsg.SetSendMsgStatusResp{} if err := m.MsgDatabase.SetSendMsgStatus(ctx, mcontext.GetOperationID(ctx), req.Status); err != nil { return nil, err @@ -16,7 +19,10 @@ func (m *msgServer) SetSendMsgStatus(ctx context.Context, req *pbMsg.SetSendMsgS return resp, nil } -func (m *msgServer) GetSendMsgStatus(ctx context.Context, req *pbMsg.GetSendMsgStatusReq) (*pbMsg.GetSendMsgStatusResp, error) { +func (m *msgServer) GetSendMsgStatus( + ctx context.Context, + req *pbMsg.GetSendMsgStatusReq, +) (*pbMsg.GetSendMsgStatusResp, error) { resp := &pbMsg.GetSendMsgStatusResp{} status, err := m.MsgDatabase.GetSendMsgStatus(ctx, mcontext.GetOperationID(ctx)) if IsNotFound(err) { diff --git a/internal/rpc/msg/revoke.go b/internal/rpc/msg/revoke.go index c32050bfe..2ea89ee87 100644 --- a/internal/rpc/msg/revoke.go +++ b/internal/rpc/msg/revoke.go @@ -56,7 +56,12 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg. } role = user.AppMangerLevel case constant.SuperGroupChatType: - members, err := m.Group.GetGroupMemberInfoMap(ctx, msgs[0].GroupID, utils.Distinct([]string{req.UserID, msgs[0].SendID}), true) + members, err := m.Group.GetGroupMemberInfoMap( + ctx, + msgs[0].GroupID, + utils.Distinct([]string{req.UserID, msgs[0].SendID}), + true, + ) if err != nil { return nil, err } diff --git a/internal/rpc/msg/send.go b/internal/rpc/msg/send.go index deaee0bed..c7450fd00 100644 --- a/internal/rpc/msg/send.go +++ b/internal/rpc/msg/send.go @@ -34,7 +34,10 @@ func (m *msgServer) SendMsg(ctx context.Context, req *pbMsg.SendMsgReq) (resp *p } } -func (m *msgServer) sendMsgSuperGroupChat(ctx context.Context, req *pbMsg.SendMsgReq) (resp *pbMsg.SendMsgResp, err error) { +func (m *msgServer) sendMsgSuperGroupChat( + ctx context.Context, + req *pbMsg.SendMsgReq, +) (resp *pbMsg.SendMsgResp, err error) { promePkg.Inc(promePkg.WorkSuperGroupChatMsgRecvSuccessCounter) if err = m.messageVerification(ctx, req); err != nil { promePkg.Inc(promePkg.WorkSuperGroupChatMsgProcessFailedCounter) @@ -105,7 +108,10 @@ func (m *msgServer) setConversationAtInfo(nctx context.Context, msg *sdkws.MsgDa } -func (m *msgServer) sendMsgNotification(ctx context.Context, req *pbMsg.SendMsgReq) (resp *pbMsg.SendMsgResp, err error) { +func (m *msgServer) sendMsgNotification( + ctx context.Context, + req *pbMsg.SendMsgReq, +) (resp *pbMsg.SendMsgResp, err error) { promePkg.Inc(promePkg.SingleChatMsgRecvSuccessCounter) if err := m.MsgDatabase.MsgToMQ(ctx, utils.GenConversationUniqueKeyForSingle(req.MsgData.SendID, req.MsgData.RecvID), req.MsgData); err != nil { promePkg.Inc(promePkg.SingleChatMsgProcessFailedCounter) @@ -127,7 +133,13 @@ func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbMsg.SendMsgReq var isSend bool = true isNotification := utils.IsNotificationByMsg(req.MsgData) if !isNotification { - isSend, err = m.modifyMessageByUserMessageReceiveOpt(ctx, req.MsgData.RecvID, utils.GenConversationIDForSingle(req.MsgData.SendID, req.MsgData.RecvID), constant.SingleChatType, req) + isSend, err = m.modifyMessageByUserMessageReceiveOpt( + ctx, + req.MsgData.RecvID, + utils.GenConversationIDForSingle(req.MsgData.SendID, req.MsgData.RecvID), + constant.SingleChatType, + req, + ) if err != nil { return nil, err } diff --git a/internal/rpc/msg/seq.go b/internal/rpc/msg/seq.go index 0d5e23d38..892d39bfe 100644 --- a/internal/rpc/msg/seq.go +++ b/internal/rpc/msg/seq.go @@ -6,7 +6,10 @@ import ( pbMsg "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" ) -func (m *msgServer) GetConversationMaxSeq(ctx context.Context, req *pbMsg.GetConversationMaxSeqReq) (resp *pbMsg.GetConversationMaxSeqResp, err error) { +func (m *msgServer) GetConversationMaxSeq( + ctx context.Context, + req *pbMsg.GetConversationMaxSeqReq, +) (resp *pbMsg.GetConversationMaxSeqResp, err error) { maxSeq, err := m.MsgDatabase.GetMaxSeq(ctx, req.ConversationID) if err != nil { return nil, err diff --git a/internal/rpc/msg/server.go b/internal/rpc/msg/server.go index c70d28e35..a077adb83 100644 --- a/internal/rpc/msg/server.go +++ b/internal/rpc/msg/server.go @@ -3,6 +3,8 @@ package msg import ( "context" + "google.golang.org/grpc" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/controller" @@ -14,7 +16,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/conversation" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" - "google.golang.org/grpc" ) type MessageInterceptorChain []MessageInterceptorFunc @@ -64,7 +65,11 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e msgDocModel := unrelation.NewMsgMongoDriver(mongo.GetDatabase()) extendMsgModel := unrelation.NewExtendMsgSetMongoDriver(mongo.GetDatabase()) extendMsgCacheModel := cache.NewExtendMsgSetCacheRedis(rdb, extendMsgModel, cache.GetDefaultOpt()) - extendMsgDatabase := controller.NewExtendMsgDatabase(extendMsgModel, extendMsgCacheModel, tx.NewMongo(mongo.GetClient())) + extendMsgDatabase := controller.NewExtendMsgDatabase( + extendMsgModel, + extendMsgCacheModel, + tx.NewMongo(mongo.GetClient()), + ) msgDatabase := controller.NewCommonMsgDatabase(msgDocModel, cacheModel) conversationClient := rpcclient.NewConversationRpcClient(client) userRpcClient := rpcclient.NewUserRpcClient(client) @@ -106,7 +111,8 @@ func (m *msgServer) initPrometheus() { } func (m *msgServer) conversationAndGetRecvID(conversation *conversation.Conversation, userID string) (recvID string) { - if conversation.ConversationType == constant.SingleChatType || conversation.ConversationType == constant.NotificationChatType { + if conversation.ConversationType == constant.SingleChatType || + conversation.ConversationType == constant.NotificationChatType { if userID == conversation.OwnerUserID { recvID = conversation.UserID } else { diff --git a/internal/rpc/msg/sync_msg.go b/internal/rpc/msg/sync_msg.go index 052fa5245..b9cfed97c 100644 --- a/internal/rpc/msg/sync_msg.go +++ b/internal/rpc/msg/sync_msg.go @@ -9,7 +9,10 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" ) -func (m *msgServer) PullMessageBySeqs(ctx context.Context, req *sdkws.PullMessageBySeqsReq) (*sdkws.PullMessageBySeqsResp, error) { +func (m *msgServer) PullMessageBySeqs( + ctx context.Context, + req *sdkws.PullMessageBySeqsReq, +) (*sdkws.PullMessageBySeqsResp, error) { resp := &sdkws.PullMessageBySeqsResp{} resp.Msgs = make(map[string]*sdkws.PullMsgs) resp.NotificationMsgs = make(map[string]*sdkws.PullMsgs) @@ -20,7 +23,15 @@ func (m *msgServer) PullMessageBySeqs(ctx context.Context, req *sdkws.PullMessag log.ZError(ctx, "GetConversation error", err, "conversationID", seq.ConversationID) continue } - minSeq, maxSeq, msgs, err := m.MsgDatabase.GetMsgBySeqsRange(ctx, req.UserID, seq.ConversationID, seq.Begin, seq.End, seq.Num, conversation.MaxSeq) + minSeq, maxSeq, msgs, err := m.MsgDatabase.GetMsgBySeqsRange( + ctx, + req.UserID, + seq.ConversationID, + seq.Begin, + seq.End, + seq.Num, + conversation.MaxSeq, + ) if err != nil { log.ZWarn(ctx, "GetMsgBySeqsRange error", err, "conversationID", seq.ConversationID, "seq", seq) continue diff --git a/internal/rpc/msg/utils.go b/internal/rpc/msg/utils.go index 468b1b617..199f1535f 100644 --- a/internal/rpc/msg/utils.go +++ b/internal/rpc/msg/utils.go @@ -1,12 +1,13 @@ package msg import ( + "github.com/redis/go-redis/v9" + "gorm.io/gorm" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "github.com/redis/go-redis/v9" - "gorm.io/gorm" ) func isMessageHasReadEnabled(msgData *sdkws.MsgData) bool { diff --git a/internal/rpc/msg/verify.go b/internal/rpc/msg/verify.go index 9d5d05efd..dd5b3436b 100644 --- a/internal/rpc/msg/verify.go +++ b/internal/rpc/msg/verify.go @@ -41,7 +41,8 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe if utils.IsContain(data.MsgData.SendID, config.Config.Manager.UserID) { return nil } - if data.MsgData.ContentType <= constant.NotificationEnd && data.MsgData.ContentType >= constant.NotificationBegin { + if data.MsgData.ContentType <= constant.NotificationEnd && + data.MsgData.ContentType >= constant.NotificationBegin { return nil } black, err := m.friend.IsBlocked(ctx, data.MsgData.SendID, data.MsgData.RecvID) @@ -67,7 +68,8 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe if err != nil { return err } - if groupInfo.Status == constant.GroupStatusDismissed && data.MsgData.ContentType != constant.GroupDismissedNotification { + if groupInfo.Status == constant.GroupStatusDismissed && + data.MsgData.ContentType != constant.GroupDismissedNotification { return errs.ErrDismissedAlready.Wrap() } if groupInfo.GroupType == constant.SuperGroup { @@ -76,7 +78,8 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe if utils.IsContain(data.MsgData.SendID, config.Config.Manager.UserID) { return nil } - if data.MsgData.ContentType <= constant.NotificationEnd && data.MsgData.ContentType >= constant.NotificationBegin { + if data.MsgData.ContentType <= constant.NotificationEnd && + data.MsgData.ContentType >= constant.NotificationBegin { return nil } // memberIDs, err := m.GroupLocalCache.GetGroupMemberIDs(ctx, data.MsgData.GroupID) @@ -161,7 +164,12 @@ func GetMsgID(sendID string) string { return utils.Md5(t + "-" + sendID + "-" + strconv.Itoa(rand.Int())) } -func (m *msgServer) modifyMessageByUserMessageReceiveOpt(ctx context.Context, userID, conversationID string, sessionType int, pb *msg.SendMsgReq) (bool, error) { +func (m *msgServer) modifyMessageByUserMessageReceiveOpt( + ctx context.Context, + userID, conversationID string, + sessionType int, + pb *msg.SendMsgReq, +) (bool, error) { opt, err := m.User.GetUserGlobalMsgRecvOpt(ctx, userID) if err != nil { return false, err diff --git a/internal/rpc/third/third.go b/internal/rpc/third/third.go index 33d9b0a82..1ac63c245 100644 --- a/internal/rpc/third/third.go +++ b/internal/rpc/third/third.go @@ -4,6 +4,8 @@ import ( "context" "net/url" + "google.golang.org/grpc" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/controller" @@ -13,7 +15,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/third" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" - "google.golang.org/grpc" ) func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { @@ -39,7 +40,13 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e third.RegisterThirdServer(server, &thirdServer{ thirdDatabase: controller.NewThirdDatabase(cache.NewMsgCacheModel(rdb)), userRpcClient: rpcclient.NewUserRpcClient(client), - s3dataBase: controller.NewS3Database(o, relation.NewObjectHash(db), relation.NewObjectInfo(db), relation.NewObjectPut(db), u), + s3dataBase: controller.NewS3Database( + o, + relation.NewObjectHash(db), + relation.NewObjectInfo(db), + relation.NewObjectPut(db), + u, + ), }) return nil } @@ -50,7 +57,10 @@ type thirdServer struct { userRpcClient rpcclient.UserRpcClient } -func (t *thirdServer) FcmUpdateToken(ctx context.Context, req *third.FcmUpdateTokenReq) (resp *third.FcmUpdateTokenResp, err error) { +func (t *thirdServer) FcmUpdateToken( + ctx context.Context, + req *third.FcmUpdateTokenReq, +) (resp *third.FcmUpdateTokenResp, err error) { err = t.thirdDatabase.FcmUpdateToken(ctx, req.Account, int(req.PlatformID), req.FcmToken, req.ExpireTime) if err != nil { return nil, err @@ -58,7 +68,10 @@ func (t *thirdServer) FcmUpdateToken(ctx context.Context, req *third.FcmUpdateTo return &third.FcmUpdateTokenResp{}, nil } -func (t *thirdServer) SetAppBadge(ctx context.Context, req *third.SetAppBadgeReq) (resp *third.SetAppBadgeResp, err error) { +func (t *thirdServer) SetAppBadge( + ctx context.Context, + req *third.SetAppBadgeReq, +) (resp *third.SetAppBadgeResp, err error) { err = t.thirdDatabase.SetAppBadge(ctx, req.UserID, int(req.AppUnreadCount)) if err != nil { return nil, err diff --git a/internal/rpc/user/statistics.go b/internal/rpc/user/statistics.go index 12ec0d55f..7cbbcb82d 100644 --- a/internal/rpc/user/statistics.go +++ b/internal/rpc/user/statistics.go @@ -8,7 +8,10 @@ import ( pbuser "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/user" ) -func (s *userServer) UserRegisterCount(ctx context.Context, req *pbuser.UserRegisterCountReq) (*pbuser.UserRegisterCountResp, error) { +func (s *userServer) UserRegisterCount( + ctx context.Context, + req *pbuser.UserRegisterCountReq, +) (*pbuser.UserRegisterCountResp, error) { if req.Start > req.End { return nil, errs.ErrArgs.Wrap("start > end") } diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index 277bf4284..5004dc1f6 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -23,8 +23,9 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient/notification" - "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" "google.golang.org/grpc" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" ) type userServer struct { @@ -59,16 +60,22 @@ func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { friendRpcClient := rpcclient.NewFriendRpcClient(client) msgRpcClient := rpcclient.NewMessageRpcClient(client) u := &userServer{ - UserDatabase: database, - RegisterCenter: client, - friendRpcClient: &friendRpcClient, - notificationSender: notification.NewFriendNotificationSender(&msgRpcClient, notification.WithDBFunc(database.FindWithError)), + UserDatabase: database, + RegisterCenter: client, + friendRpcClient: &friendRpcClient, + notificationSender: notification.NewFriendNotificationSender( + &msgRpcClient, + notification.WithDBFunc(database.FindWithError), + ), } pbuser.RegisterUserServer(server, u) return u.UserDatabase.InitOnce(context.Background(), users) } -func (s *userServer) GetDesignateUsers(ctx context.Context, req *pbuser.GetDesignateUsersReq) (resp *pbuser.GetDesignateUsersResp, err error) { +func (s *userServer) GetDesignateUsers( + ctx context.Context, + req *pbuser.GetDesignateUsersReq, +) (resp *pbuser.GetDesignateUsersResp, err error) { resp = &pbuser.GetDesignateUsersResp{} users, err := s.FindWithError(ctx, req.UserIDs) if err != nil { @@ -81,7 +88,10 @@ func (s *userServer) GetDesignateUsers(ctx context.Context, req *pbuser.GetDesig return resp, nil } -func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInfoReq) (resp *pbuser.UpdateUserInfoResp, err error) { +func (s *userServer) UpdateUserInfo( + ctx context.Context, + req *pbuser.UpdateUserInfoReq, +) (resp *pbuser.UpdateUserInfoResp, err error) { resp = &pbuser.UpdateUserInfoResp{} err = tokenverify.CheckAccessV3(ctx, req.UserInfo.UserID) if err != nil { @@ -106,7 +116,10 @@ func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserI return resp, nil } -func (s *userServer) SetGlobalRecvMessageOpt(ctx context.Context, req *pbuser.SetGlobalRecvMessageOptReq) (resp *pbuser.SetGlobalRecvMessageOptResp, err error) { +func (s *userServer) SetGlobalRecvMessageOpt( + ctx context.Context, + req *pbuser.SetGlobalRecvMessageOptReq, +) (resp *pbuser.SetGlobalRecvMessageOptResp, err error) { resp = &pbuser.SetGlobalRecvMessageOptResp{} if _, err := s.FindWithError(ctx, []string{req.UserID}); err != nil { return nil, err @@ -120,7 +133,10 @@ func (s *userServer) SetGlobalRecvMessageOpt(ctx context.Context, req *pbuser.Se return resp, nil } -func (s *userServer) AccountCheck(ctx context.Context, req *pbuser.AccountCheckReq) (resp *pbuser.AccountCheckResp, err error) { +func (s *userServer) AccountCheck( + ctx context.Context, + req *pbuser.AccountCheckReq, +) (resp *pbuser.AccountCheckResp, err error) { resp = &pbuser.AccountCheckResp{} if utils.Duplicate(req.CheckUserIDs) { return nil, errs.ErrArgs.Wrap("userID repeated") @@ -149,7 +165,10 @@ func (s *userServer) AccountCheck(ctx context.Context, req *pbuser.AccountCheckR return resp, nil } -func (s *userServer) GetPaginationUsers(ctx context.Context, req *pbuser.GetPaginationUsersReq) (resp *pbuser.GetPaginationUsersResp, err error) { +func (s *userServer) GetPaginationUsers( + ctx context.Context, + req *pbuser.GetPaginationUsersReq, +) (resp *pbuser.GetPaginationUsersResp, err error) { var pageNumber, showNumber int32 if req.Pagination != nil { pageNumber = req.Pagination.PageNumber @@ -162,7 +181,10 @@ func (s *userServer) GetPaginationUsers(ctx context.Context, req *pbuser.GetPagi return &pbuser.GetPaginationUsersResp{Total: int32(total), Users: convert.UsersDB2Pb(users)}, err } -func (s *userServer) UserRegister(ctx context.Context, req *pbuser.UserRegisterReq) (resp *pbuser.UserRegisterResp, err error) { +func (s *userServer) UserRegister( + ctx context.Context, + req *pbuser.UserRegisterReq, +) (resp *pbuser.UserRegisterResp, err error) { resp = &pbuser.UserRegisterResp{} if len(req.Users) == 0 { return nil, errs.ErrArgs.Wrap("users is empty") @@ -210,7 +232,10 @@ func (s *userServer) UserRegister(ctx context.Context, req *pbuser.UserRegisterR return resp, nil } -func (s *userServer) GetGlobalRecvMessageOpt(ctx context.Context, req *pbuser.GetGlobalRecvMessageOptReq) (resp *pbuser.GetGlobalRecvMessageOptResp, err error) { +func (s *userServer) GetGlobalRecvMessageOpt( + ctx context.Context, + req *pbuser.GetGlobalRecvMessageOptReq, +) (resp *pbuser.GetGlobalRecvMessageOptResp, err error) { user, err := s.FindWithError(ctx, []string{req.UserID}) if err != nil { return nil, err @@ -218,7 +243,10 @@ func (s *userServer) GetGlobalRecvMessageOpt(ctx context.Context, req *pbuser.Ge return &pbuser.GetGlobalRecvMessageOptResp{GlobalRecvMsgOpt: user[0].GlobalRecvMsgOpt}, nil } -func (s *userServer) GetAllUserID(ctx context.Context, req *pbuser.GetAllUserIDReq) (resp *pbuser.GetAllUserIDResp, err error) { +func (s *userServer) GetAllUserID( + ctx context.Context, + req *pbuser.GetAllUserIDReq, +) (resp *pbuser.GetAllUserIDResp, err error) { userIDs, err := s.UserDatabase.GetAllUserID(ctx) if err != nil { return nil, err diff --git a/internal/tools/cron_task.go b/internal/tools/cron_task.go index debf61a6d..bac433fc5 100644 --- a/internal/tools/cron_task.go +++ b/internal/tools/cron_task.go @@ -5,9 +5,10 @@ import ( "fmt" "sync" + "github.com/robfig/cron/v3" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" - "github.com/robfig/cron/v3" ) func StartCronTask() error { diff --git a/internal/tools/msg.go b/internal/tools/msg.go index fc810aa95..0c58791a0 100644 --- a/internal/tools/msg.go +++ b/internal/tools/msg.go @@ -26,7 +26,12 @@ type MsgTool struct { var errSeq = errors.New("cache max seq and mongo max seq is diff > 10") -func NewMsgTool(msgDatabase controller.CommonMsgDatabase, userDatabase controller.UserDatabase, groupDatabase controller.GroupDatabase, conversationDatabase controller.ConversationDatabase) *MsgTool { +func NewMsgTool( + msgDatabase controller.CommonMsgDatabase, + userDatabase controller.UserDatabase, + groupDatabase controller.GroupDatabase, + conversationDatabase controller.ConversationDatabase, +) *MsgTool { return &MsgTool{ msgDatabase: msgDatabase, userDatabase: userDatabase, @@ -50,9 +55,17 @@ func InitMsgTool() (*MsgTool, error) { } userDB := relation.NewUserGorm(db) msgDatabase := controller.InitCommonMsgDatabase(rdb, mongo.GetDatabase()) - userDatabase := controller.NewUserDatabase(userDB, cache.NewUserCacheRedis(rdb, relation.NewUserGorm(db), cache.GetDefaultOpt()), tx.NewGorm(db)) + userDatabase := controller.NewUserDatabase( + userDB, + cache.NewUserCacheRedis(rdb, relation.NewUserGorm(db), cache.GetDefaultOpt()), + tx.NewGorm(db), + ) groupDatabase := controller.InitGroupDatabase(db, rdb, mongo.GetDatabase()) - conversationDatabase := controller.NewConversationDatabase(relation.NewConversationGorm(db), cache.NewConversationRedis(rdb, cache.GetDefaultOpt(), relation.NewConversationGorm(db)), tx.NewGorm(db)) + conversationDatabase := controller.NewConversationDatabase( + relation.NewConversationGorm(db), + cache.NewConversationRedis(rdb, cache.GetDefaultOpt(), relation.NewConversationGorm(db)), + tx.NewGorm(db), + ) msgTool := NewMsgTool(msgDatabase, userDatabase, groupDatabase, conversationDatabase) return msgTool, nil } @@ -75,7 +88,15 @@ func (c *MsgTool) AllConversationClearMsgAndFixSeq() { func (c *MsgTool) ClearConversationsMsg(ctx context.Context, conversationIDs []string) { for _, conversationID := range conversationIDs { if err := c.msgDatabase.DeleteConversationMsgsAndSetMinSeq(ctx, conversationID, int64(config.Config.RetainChatRecords*24*60*60)); err != nil { - log.ZError(ctx, "DeleteUserSuperGroupMsgsAndSetMinSeq failed", err, "conversationID", conversationID, "DBRetainChatRecords", config.Config.RetainChatRecords) + log.ZError( + ctx, + "DeleteUserSuperGroupMsgsAndSetMinSeq failed", + err, + "conversationID", + conversationID, + "DBRetainChatRecords", + config.Config.RetainChatRecords, + ) } if err := c.checkMaxSeq(ctx, conversationID); err != nil { log.ZError(ctx, "fixSeq failed", err, "conversationID", conversationID) diff --git a/internal/tools/msg_test.go b/internal/tools/msg_test.go index bf548903d..241748f76 100644 --- a/internal/tools/msg_test.go +++ b/internal/tools/msg_test.go @@ -4,11 +4,12 @@ import ( "context" "strconv" + "go.mongodb.org/mongo-driver/bson" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "go.mongodb.org/mongo-driver/bson" unRelationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/unrelation" @@ -89,7 +90,10 @@ func TestDeleteMongoMsgAndResetRedisSeq(t *testing.T) { return } msgTools.ClearConversationsMsg(ctx, []string{conversationID}) - minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache, err := msgTools.msgDatabase.GetConversationMinMaxSeqInMongoAndCache(ctx, conversationID) + minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache, err := msgTools.msgDatabase.GetConversationMinMaxSeqInMongoAndCache( + ctx, + conversationID, + ) if err != nil { t.Error("GetSuperGroupMinMaxSeqInMongoAndCache failed") return @@ -132,7 +136,10 @@ func TestDeleteMongoMsgAndResetRedisSeq(t *testing.T) { } msgTools.ClearConversationsMsg(ctx, []string{conversationID}) - minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache, err = msgTools.msgDatabase.GetConversationMinMaxSeqInMongoAndCache(ctx, conversationID) + minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache, err = msgTools.msgDatabase.GetConversationMinMaxSeqInMongoAndCache( + ctx, + conversationID, + ) if err != nil { t.Error("GetSuperGroupMinMaxSeqInMongoAndCache failed") return @@ -164,7 +171,10 @@ func TestDeleteMongoMsgAndResetRedisSeq(t *testing.T) { } msgTools.ClearConversationsMsg(ctx, []string{conversationID}) - minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache, err = msgTools.msgDatabase.GetConversationMinMaxSeqInMongoAndCache(ctx, conversationID) + minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache, err = msgTools.msgDatabase.GetConversationMinMaxSeqInMongoAndCache( + ctx, + conversationID, + ) if err != nil { t.Error("GetSuperGroupMinMaxSeqInMongoAndCache failed") return @@ -214,7 +224,10 @@ func TestDeleteMongoMsgAndResetRedisSeq(t *testing.T) { t.Error("GetSuperGroupMinMaxSeqInMongoAndCache failed") return } - minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache, err = msgTools.msgDatabase.GetConversationMinMaxSeqInMongoAndCache(ctx, conversationID) + minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache, err = msgTools.msgDatabase.GetConversationMinMaxSeqInMongoAndCache( + ctx, + conversationID, + ) if err != nil { t.Error("GetSuperGroupMinMaxSeqInMongoAndCache failed") return @@ -255,7 +268,10 @@ func TestDeleteMongoMsgAndResetRedisSeq(t *testing.T) { t.Error("GetSuperGroupMinMaxSeqInMongoAndCache failed") return } - minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache, err = msgTools.msgDatabase.GetConversationMinMaxSeqInMongoAndCache(ctx, conversationID) + minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache, err = msgTools.msgDatabase.GetConversationMinMaxSeqInMongoAndCache( + ctx, + conversationID, + ) if err != nil { t.Error("GetSuperGroupMinMaxSeqInMongoAndCache failed") return @@ -305,7 +321,10 @@ func TestDeleteMongoMsgAndResetRedisSeq(t *testing.T) { if _, err := mongoClient.InsertOne(ctx, msgDoc4); err != nil { t.Error("InsertOne failed", testUID4) } - minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache, err = msgTools.msgDatabase.GetConversationMinMaxSeqInMongoAndCache(ctx, conversationID) + minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache, err = msgTools.msgDatabase.GetConversationMinMaxSeqInMongoAndCache( + ctx, + conversationID, + ) if err != nil { t.Error("GetSuperGroupMinMaxSeqInMongoAndCache failed") return diff --git a/pkg/a2r/api2rpc.go b/pkg/a2r/api2rpc.go index 39bd70ce4..1a064176c 100644 --- a/pkg/a2r/api2rpc.go +++ b/pkg/a2r/api2rpc.go @@ -5,11 +5,12 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/checker" + "github.com/gin-gonic/gin" + "google.golang.org/grpc" + "github.com/OpenIMSDK/Open-IM-Server/pkg/apiresp" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" - "github.com/gin-gonic/gin" - "google.golang.org/grpc" ) func Call[A, B, C any]( diff --git a/pkg/apistruct/auth.go b/pkg/apistruct/auth.go index 664919876..b760dcd08 100644 --- a/pkg/apistruct/auth.go +++ b/pkg/apistruct/auth.go @@ -1,8 +1,8 @@ package apistruct type UserRegisterReq struct { - Secret string `json:"secret" binding:"required,max=32"` - Platform int32 `json:"platform" binding:"required,min=1,max=12"` + Secret string `json:"secret" binding:"required,max=32"` + Platform int32 `json:"platform" binding:"required,min=1,max=12"` ApiUserInfo OperationID string `json:"operationID" binding:"required"` } @@ -17,9 +17,9 @@ type UserRegisterResp struct { } type UserTokenReq struct { - Secret string `json:"secret" binding:"required,max=32"` - Platform int32 `json:"platform" binding:"required,min=1,max=12"` - UserID string `json:"userID" binding:"required,min=1,max=64"` + Secret string `json:"secret" binding:"required,max=32"` + Platform int32 `json:"platform" binding:"required,min=1,max=12"` + UserID string `json:"userID" binding:"required,min=1,max=64"` OperationID string `json:"operationID" binding:"required"` } @@ -28,8 +28,8 @@ type UserTokenResp struct { } type ForceLogoutReq struct { - Platform int32 `json:"platform" binding:"required,min=1,max=12"` - FromUserID string `json:"fromUserID" binding:"required,min=1,max=64"` + Platform int32 `json:"platform" binding:"required,min=1,max=12"` + FromUserID string `json:"fromUserID" binding:"required,min=1,max=64"` OperationID string `json:"operationID" binding:"required"` } @@ -46,7 +46,7 @@ type ParseTokenReq struct { //} type ExpireTime struct { - ExpireTimeSeconds uint32 `json:"expireTimeSeconds" ` + ExpireTimeSeconds uint32 `json:"expireTimeSeconds"` } type ParseTokenResp struct { diff --git a/pkg/apistruct/conversation.go b/pkg/apistruct/conversation.go index 2abd566e5..1c6ba6e29 100644 --- a/pkg/apistruct/conversation.go +++ b/pkg/apistruct/conversation.go @@ -6,23 +6,23 @@ type OptResult struct { } type GetAllConversationMessageOptReq struct { OperationID string `json:"operationID" binding:"required"` - FromUserID string `json:"fromUserID" binding:"required"` + FromUserID string `json:"fromUserID" binding:"required"` } type GetAllConversationMessageOptResp struct { ConversationOptResultList []*OptResult `json:"data"` } type GetReceiveMessageOptReq struct { ConversationIDList []string `json:"conversationIDList" binding:"required"` - OperationID string `json:"operationID" binding:"required"` - FromUserID string `json:"fromUserID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` + FromUserID string `json:"fromUserID" binding:"required"` } type GetReceiveMessageOptResp struct { ConversationOptResultList []*OptResult `json:"data"` } type SetReceiveMessageOptReq struct { - FromUserID string `json:"fromUserID" binding:"required"` - OperationID string `json:"operationID" binding:"required"` - Opt *int32 `json:"opt" binding:"required"` + FromUserID string `json:"fromUserID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` + Opt *int32 `json:"opt" binding:"required"` ConversationIDList []string `json:"conversationIDList" binding:"required"` } type SetReceiveMessageOptResp struct { @@ -30,15 +30,15 @@ type SetReceiveMessageOptResp struct { } type Conversation struct { - OwnerUserID string `json:"ownerUserID" binding:"required"` - ConversationID string `json:"conversationID" binding:"required"` - ConversationType int32 `json:"conversationType" binding:"required"` + OwnerUserID string `json:"ownerUserID" binding:"required"` + ConversationID string `json:"conversationID" binding:"required"` + ConversationType int32 `json:"conversationType" binding:"required"` UserID string `json:"userID"` GroupID string `json:"groupID"` - RecvMsgOpt int32 `json:"recvMsgOpt" binding:"omitempty,oneof=0 1 2"` - UnreadCount int32 `json:"unreadCount" binding:"omitempty"` + RecvMsgOpt int32 `json:"recvMsgOpt" binding:"omitempty,oneof=0 1 2"` + UnreadCount int32 `json:"unreadCount" binding:"omitempty"` DraftTextTime int64 `json:"draftTextTime"` - IsPinned bool `json:"isPinned" binding:"omitempty"` + IsPinned bool `json:"isPinned" binding:"omitempty"` IsPrivateChat bool `json:"isPrivateChat"` BurnDuration int32 `json:"burnDuration"` GroupAtType int32 `json:"groupAtType"` @@ -51,25 +51,25 @@ type Conversation struct { type SetConversationReq struct { Conversation NotificationType int32 `json:"notificationType"` - OperationID string `json:"operationID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` } type SetConversationResp struct { } type ModifyConversationFieldReq struct { Conversation - FieldType int32 `json:"fieldType" binding:"required"` - UserIDList []string `json:"userIDList" binding:"required"` + FieldType int32 `json:"fieldType" binding:"required"` + UserIDList []string `json:"userIDList" binding:"required"` OperationID string `json:"operationID" binding:"required"` } type ModifyConversationFieldResp struct { } type BatchSetConversationsReq struct { - Conversations []Conversation `json:"conversations" binding:"required"` + Conversations []Conversation `json:"conversations" binding:"required"` NotificationType int32 `json:"notificationType"` - OwnerUserID string `json:"ownerUserID" binding:"required"` - OperationID string `json:"operationID" binding:"required"` + OwnerUserID string `json:"ownerUserID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` } type BatchSetConversationsResp struct { @@ -81,8 +81,8 @@ type BatchSetConversationsResp struct { type GetConversationReq struct { ConversationID string `json:"conversationID" binding:"required"` - OwnerUserID string `json:"ownerUserID" binding:"required"` - OperationID string `json:"operationID" binding:"required"` + OwnerUserID string `json:"ownerUserID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` } type GetConversationResp struct { @@ -100,8 +100,8 @@ type GetAllConversationsResp struct { type GetConversationsReq struct { ConversationIDs []string `json:"conversationIDs" binding:"required"` - OwnerUserID string `json:"ownerUserID" binding:"required"` - OperationID string `json:"operationID" binding:"required"` + OwnerUserID string `json:"ownerUserID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` } type GetConversationsResp struct { @@ -109,10 +109,10 @@ type GetConversationsResp struct { } type SetRecvMsgOptReq struct { - OwnerUserID string `json:"ownerUserID" binding:"required"` + OwnerUserID string `json:"ownerUserID" binding:"required"` ConversationID string `json:"conversationID"` - RecvMsgOpt int32 `json:"recvMsgOpt" binding:"omitempty,oneof=0 1 2"` - OperationID string `json:"operationID" binding:"required"` + RecvMsgOpt int32 `json:"recvMsgOpt" binding:"omitempty,oneof=0 1 2"` + OperationID string `json:"operationID" binding:"required"` NotificationType int32 `json:"notificationType"` } diff --git a/pkg/apistruct/friend.go b/pkg/apistruct/friend.go index 4bfc39a91..14a7438ac 100644 --- a/pkg/apistruct/friend.go +++ b/pkg/apistruct/friend.go @@ -168,7 +168,7 @@ type FriendRequest struct { } type AddBlacklistReq struct { - ToUserID string `json:"toUserID" binding:"required"` + ToUserID string `json:"toUserID" binding:"required"` FromUserID string `json:"fromUserID" binding:"required"` } type AddBlacklistResp struct { @@ -176,7 +176,7 @@ type AddBlacklistResp struct { type ImportFriendReq struct { FriendUserIDList []string `json:"friendUserIDList" binding:"required"` - FromUserID string `json:"fromUserID" binding:"required"` + FromUserID string `json:"fromUserID" binding:"required"` } type ImportFriendResp struct { @@ -184,7 +184,7 @@ type ImportFriendResp struct { } type AddFriendReq struct { - ToUserID string `json:"toUserID" binding:"required"` + ToUserID string `json:"toUserID" binding:"required"` FromUserID string `json:"fromUserID" binding:"required"` ReqMsg string `json:"reqMsg"` } @@ -193,16 +193,16 @@ type AddFriendResp struct { } type AddFriendResponseReq struct { - ToUserID string `json:"toUserID" binding:"required"` + ToUserID string `json:"toUserID" binding:"required"` FromUserID string `json:"fromUserID" binding:"required"` - HandleResult int32 `json:"flag" binding:"required,oneof=-1 0 1"` + HandleResult int32 `json:"flag" binding:"required,oneof=-1 0 1"` HandleMsg string `json:"handleMsg"` } type AddFriendResponseResp struct { } type DeleteFriendReq struct { - ToUserID string `json:"toUserID" binding:"required"` + ToUserID string `json:"toUserID" binding:"required"` FromUserID string `json:"fromUserID" binding:"required"` } type DeleteFriendResp struct { @@ -216,7 +216,7 @@ type GetBlackListResp struct { } type SetFriendRemarkReq struct { - ToUserID string `json:"toUserID" binding:"required"` + ToUserID string `json:"toUserID" binding:"required"` FromUserID string `json:"fromUserID" binding:"required"` Remark string `json:"remark"` } @@ -224,14 +224,14 @@ type SetFriendRemarkResp struct { } type RemoveBlacklistReq struct { - ToUserID string `json:"toUserID" binding:"required"` + ToUserID string `json:"toUserID" binding:"required"` FromUserID string `json:"fromUserID" binding:"required"` } type RemoveBlacklistResp struct { } type IsFriendReq struct { - ToUserID string `json:"toUserID" binding:"required"` + ToUserID string `json:"toUserID" binding:"required"` FromUserID string `json:"fromUserID" binding:"required"` } type Response struct { @@ -243,7 +243,7 @@ type IsFriendResp struct { type GetFriendListReq struct { OperationID string `json:"operationID" binding:"required"` - FromUserID string `json:"fromUserID" binding:"required"` + FromUserID string `json:"fromUserID" binding:"required"` } type GetFriendListResp struct { OwnerUserID string `json:"ownerUserID"` @@ -257,7 +257,7 @@ type GetFriendListResp struct { type GetFriendApplyListReq struct { OperationID string `json:"operationID" binding:"required"` - FromUserID string `json:"fromUserID" binding:"required"` + FromUserID string `json:"fromUserID" binding:"required"` } type GetFriendApplyListResp struct { @@ -266,7 +266,7 @@ type GetFriendApplyListResp struct { type GetSelfApplyListReq struct { OperationID string `json:"operationID" binding:"required"` - FromUserID string `json:"fromUserID" binding:"required"` + FromUserID string `json:"fromUserID" binding:"required"` } type GetSelfApplyListResp struct { FriendRequestList []FriendRequest `json:"friendRequestList"` diff --git a/pkg/apistruct/group.go b/pkg/apistruct/group.go index 22b628492..32af09551 100644 --- a/pkg/apistruct/group.go +++ b/pkg/apistruct/group.go @@ -5,10 +5,10 @@ import ( ) type KickGroupMemberReq struct { - GroupID string `json:"groupID" binding:"required"` + GroupID string `json:"groupID" binding:"required"` KickedUserIDList []string `json:"kickedUserIDList" binding:"required"` Reason string `json:"reason"` - OperationID string `json:"operationID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` } type KickGroupMemberResp struct { @@ -16,8 +16,8 @@ type KickGroupMemberResp struct { } type GetGroupMembersInfoReq struct { - GroupID string `json:"groupID" binding:"required"` - MemberList []string `json:"memberList" binding:"required"` + GroupID string `json:"groupID" binding:"required"` + MemberList []string `json:"memberList" binding:"required"` OperationID string `json:"operationID" binding:"required"` } type GetGroupMembersInfoResp struct { @@ -26,10 +26,10 @@ type GetGroupMembersInfoResp struct { } type InviteUserToGroupReq struct { - GroupID string `json:"groupID" binding:"required"` + GroupID string `json:"groupID" binding:"required"` InvitedUserIDList []string `json:"invitedUserIDList" binding:"required"` Reason string `json:"reason"` - OperationID string `json:"operationID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` } type InviteUserToGroupResp struct { @@ -38,7 +38,7 @@ type InviteUserToGroupResp struct { type GetJoinedGroupListReq struct { OperationID string `json:"operationID" binding:"required"` - FromUserID string `json:"fromUserID" binding:"required"` + FromUserID string `json:"fromUserID" binding:"required"` } type GetJoinedGroupListResp struct { GroupInfoList []*sdkws.GroupInfo `json:"-"` @@ -54,11 +54,11 @@ type GetGroupMemberListReq struct { type GetGroupMemberListResp struct { NextSeq int32 `json:"nextSeq"` MemberList []*sdkws.GroupMemberFullInfo `json:"-"` - Data []map[string]interface{} `json:"data" swaggerignore:"true"` + Data []map[string]interface{} `json:"data" swaggerignore:"true"` } type GetGroupAllMemberReq struct { - GroupID string `json:"groupID" binding:"required"` + GroupID string `json:"groupID" binding:"required"` OperationID string `json:"operationID" binding:"required"` Offset int32 `json:"offset"` Count int32 `json:"count"` @@ -90,7 +90,7 @@ type CreateGroupReq struct { Introduction string `json:"introduction"` FaceURL string `json:"faceURL"` Ex string `json:"ex"` - OperationID string `json:"operationID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` GroupID string `json:"groupID"` } type CreateGroupResp struct { @@ -100,7 +100,7 @@ type CreateGroupResp struct { type GetGroupApplicationListReq struct { OperationID string `json:"operationID" binding:"required"` - FromUserID string `json:"fromUserID" binding:"required"` //作为管理员或群主收到的 进群申请 + FromUserID string `json:"fromUserID" binding:"required"` //作为管理员或群主收到的 进群申请 } type GetGroupApplicationListResp struct { GroupRequestList []*sdkws.GroupRequest `json:"-"` @@ -109,7 +109,7 @@ type GetGroupApplicationListResp struct { type GetUserReqGroupApplicationListReq struct { OperationID string `json:"operationID" binding:"required"` - UserID string `json:"userID" binding:"required"` + UserID string `json:"userID" binding:"required"` } type GetUserRespGroupApplicationResp struct { @@ -147,9 +147,9 @@ type GetGroupInfoResp struct { //} type ApplicationGroupResponseReq struct { - OperationID string `json:"operationID" binding:"required"` - GroupID string `json:"groupID" binding:"required"` - FromUserID string `json:"fromUserID" binding:"required"` //application from FromUserID + OperationID string `json:"operationID" binding:"required"` + GroupID string `json:"groupID" binding:"required"` + FromUserID string `json:"fromUserID" binding:"required"` //application from FromUserID HandledMsg string `json:"handledMsg"` HandleResult int32 `json:"handleResult" binding:"required,oneof=-1 1"` } @@ -157,9 +157,9 @@ type ApplicationGroupResponseResp struct { } type JoinGroupReq struct { - GroupID string `json:"groupID" binding:"required"` + GroupID string `json:"groupID" binding:"required"` ReqMessage string `json:"reqMessage"` - OperationID string `json:"operationID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` JoinSource int32 `json:"joinSource"` InviterUserID string `json:"inviterUserID"` } @@ -168,20 +168,20 @@ type JoinGroupResp struct { } type QuitGroupReq struct { - GroupID string `json:"groupID" binding:"required"` + GroupID string `json:"groupID" binding:"required"` OperationID string `json:"operationID" binding:"required"` } type QuitGroupResp struct { } type SetGroupInfoReq struct { - GroupID string `json:"groupID" binding:"required"` + GroupID string `json:"groupID" binding:"required"` GroupName string `json:"groupName"` Notification string `json:"notification"` Introduction string `json:"introduction"` FaceURL string `json:"faceURL"` Ex string `json:"ex"` - OperationID string `json:"operationID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` NeedVerification *int32 `json:"needVerification"` LookMemberInfo *int32 `json:"lookMemberInfo"` ApplyMemberFriend *int32 `json:"applyMemberFriend"` @@ -191,25 +191,25 @@ type SetGroupInfoResp struct { } type TransferGroupOwnerReq struct { - GroupID string `json:"groupID" binding:"required"` + GroupID string `json:"groupID" binding:"required"` OldOwnerUserID string `json:"oldOwnerUserID" binding:"required"` NewOwnerUserID string `json:"newOwnerUserID" binding:"required"` - OperationID string `json:"operationID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` } type TransferGroupOwnerResp struct { } type DismissGroupReq struct { - GroupID string `json:"groupID" binding:"required"` + GroupID string `json:"groupID" binding:"required"` OperationID string `json:"operationID" binding:"required"` } type DismissGroupResp struct { } type MuteGroupMemberReq struct { - OperationID string `json:"operationID" binding:"required"` - GroupID string `json:"groupID" binding:"required"` - UserID string `json:"userID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` + GroupID string `json:"groupID" binding:"required"` + UserID string `json:"userID" binding:"required"` MutedSeconds uint32 `json:"mutedSeconds" binding:"required"` } type MuteGroupMemberResp struct { @@ -217,30 +217,30 @@ type MuteGroupMemberResp struct { type CancelMuteGroupMemberReq struct { OperationID string `json:"operationID" binding:"required"` - GroupID string `json:"groupID" binding:"required"` - UserID string `json:"userID" binding:"required"` + GroupID string `json:"groupID" binding:"required"` + UserID string `json:"userID" binding:"required"` } type CancelMuteGroupMemberResp struct { } type MuteGroupReq struct { OperationID string `json:"operationID" binding:"required"` - GroupID string `json:"groupID" binding:"required"` + GroupID string `json:"groupID" binding:"required"` } type MuteGroupResp struct { } type CancelMuteGroupReq struct { OperationID string `json:"operationID" binding:"required"` - GroupID string `json:"groupID" binding:"required"` + GroupID string `json:"groupID" binding:"required"` } type CancelMuteGroupResp struct { } type SetGroupMemberNicknameReq struct { OperationID string `json:"operationID" binding:"required"` - GroupID string `json:"groupID" binding:"required"` - UserID string `json:"userID" binding:"required"` + GroupID string `json:"groupID" binding:"required"` + UserID string `json:"userID" binding:"required"` Nickname string `json:"nickname"` } @@ -248,12 +248,12 @@ type SetGroupMemberNicknameResp struct { } type SetGroupMemberInfoReq struct { - OperationID string `json:"operationID" binding:"required"` - GroupID string `json:"groupID" binding:"required"` - UserID string `json:"userID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` + GroupID string `json:"groupID" binding:"required"` + UserID string `json:"userID" binding:"required"` Nickname *string `json:"nickname"` FaceURL *string `json:"userGroupFaceUrl"` - RoleLevel *int32 `json:"roleLevel" validate:"gte=1,lte=3"` + RoleLevel *int32 `json:"roleLevel" validate:"gte=1,lte=3"` Ex *string `json:"ex"` } diff --git a/pkg/apistruct/manage.go b/pkg/apistruct/manage.go index 1aa70fa1d..832597fd0 100644 --- a/pkg/apistruct/manage.go +++ b/pkg/apistruct/manage.go @@ -5,7 +5,7 @@ import ( ) type DeleteUsersReq struct { - OperationID string `json:"operationID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` DeleteUserIDList []string `json:"deleteUserIDList" binding:"required"` } type DeleteUsersResp struct { @@ -19,43 +19,43 @@ type GetAllUsersUidResp struct { } type GetUsersOnlineStatusReq struct { OperationID string `json:"operationID" binding:"required"` - UserIDList []string `json:"userIDList" binding:"required,lte=200"` + UserIDList []string `json:"userIDList" binding:"required,lte=200"` } type GetUsersOnlineStatusResp struct { //SuccessResult []*msggateway.GetUsersOnlineStatusResp_SuccessResult `json:"data"` } type AccountCheckReq struct { - OperationID string `json:"operationID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` CheckUserIDList []string `json:"checkUserIDList" binding:"required,lte=100"` } type AccountCheckResp struct { } type ManagementSendMsg struct { - SendID string `json:"sendID" binding:"required"` - GroupID string `json:"groupID" binding:"required_if=SessionType 2|required_if=SessionType 3"` - SenderNickname string `json:"senderNickname" ` - SenderFaceURL string `json:"senderFaceURL" ` + SendID string `json:"sendID" binding:"required"` + GroupID string `json:"groupID" binding:"required_if=SessionType 2|required_if=SessionType 3"` + SenderNickname string `json:"senderNickname"` + SenderFaceURL string `json:"senderFaceURL"` SenderPlatformID int32 `json:"senderPlatformID"` - Content map[string]interface{} `json:"content" binding:"required" swaggerignore:"true"` - ContentType int32 `json:"contentType" binding:"required"` - SessionType int32 `json:"sessionType" binding:"required"` + Content map[string]interface{} `json:"content" binding:"required" swaggerignore:"true"` + ContentType int32 `json:"contentType" binding:"required"` + SessionType int32 `json:"sessionType" binding:"required"` IsOnlineOnly bool `json:"isOnlineOnly"` NotOfflinePush bool `json:"notOfflinePush"` OfflinePushInfo *sdkws.OfflinePushInfo `json:"offlinePushInfo"` } type ManagementSendMsgReq struct { - SendID string `json:"sendID" binding:"required"` - RecvID string `json:"recvID" binding:"required_if" message:"recvID is required if sessionType is SingleChatType or NotificationChatType"` - GroupID string `json:"groupID" binding:"required_if" message:"groupID is required if sessionType is GroupChatType or SuperGroupChatType"` - SenderNickname string `json:"senderNickname" ` - SenderFaceURL string `json:"senderFaceURL" ` + SendID string `json:"sendID" binding:"required"` + RecvID string `json:"recvID" binding:"required_if" message:"recvID is required if sessionType is SingleChatType or NotificationChatType"` + GroupID string `json:"groupID" binding:"required_if" message:"groupID is required if sessionType is GroupChatType or SuperGroupChatType"` + SenderNickname string `json:"senderNickname"` + SenderFaceURL string `json:"senderFaceURL"` SenderPlatformID int32 `json:"senderPlatformID"` - Content map[string]interface{} `json:"content" binding:"required" swaggerignore:"true"` - ContentType int32 `json:"contentType" binding:"required"` - SessionType int32 `json:"sessionType" binding:"required"` + Content map[string]interface{} `json:"content" binding:"required" swaggerignore:"true"` + ContentType int32 `json:"contentType" binding:"required"` + SessionType int32 `json:"sessionType" binding:"required"` IsOnlineOnly bool `json:"isOnlineOnly"` NotOfflinePush bool `json:"notOfflinePush"` OfflinePushInfo *sdkws.OfflinePushInfo `json:"offlinePushInfo"` @@ -81,7 +81,7 @@ type SingleReturnResult struct { ServerMsgID string `json:"serverMsgID"` ClientMsgID string `json:"clientMsgID"` SendTime int64 `json:"sendTime"` - RecvID string `json:"recvID" ` + RecvID string `json:"recvID"` } type CheckMsgIsSendSuccessReq struct { diff --git a/pkg/apistruct/msg.go b/pkg/apistruct/msg.go index 042d760b8..5efa917d1 100644 --- a/pkg/apistruct/msg.go +++ b/pkg/apistruct/msg.go @@ -6,8 +6,8 @@ import ( ) type DelMsgReq struct { - UserID string `json:"userID,omitempty" binding:"required"` - SeqList []uint32 `json:"seqList,omitempty" binding:"required"` + UserID string `json:"userID,omitempty" binding:"required"` + SeqList []uint32 `json:"seqList,omitempty" binding:"required"` OperationID string `json:"operationID,omitempty" binding:"required"` } @@ -15,19 +15,19 @@ type DelMsgResp struct { } type CleanUpMsgReq struct { - UserID string `json:"userID" binding:"required"` - OperationID string `json:"operationID" binding:"required"` + UserID string `json:"userID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` } type CleanUpMsgResp struct { } type DelSuperGroupMsgReq struct { - UserID string `json:"userID" binding:"required"` - GroupID string `json:"groupID" binding:"required"` + UserID string `json:"userID" binding:"required"` + GroupID string `json:"groupID" binding:"required"` SeqList []uint32 `json:"seqList,omitempty"` IsAllDelete bool `json:"isAllDelete"` - OperationID string `json:"operationID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` } type DelSuperGroupMsgResp struct { @@ -40,21 +40,21 @@ type MsgDeleteNotificationElem struct { } type SetMsgMinSeqReq struct { - UserID string `json:"userID" binding:"required"` + UserID string `json:"userID" binding:"required"` GroupID string `json:"groupID"` - MinSeq uint32 `json:"minSeq" binding:"required"` - OperationID string `json:"operationID" binding:"required"` + MinSeq uint32 `json:"minSeq" binding:"required"` + OperationID string `json:"operationID" binding:"required"` } type SetMsgMinSeqResp struct { } type ModifyMessageReactionExtensionsReq struct { - OperationID string `json:"operationID" binding:"required"` - conversationID string `json:"conversationID" binding:"required"` - SessionType int32 `json:"sessionType" binding:"required"` + OperationID string `json:"operationID" binding:"required"` + conversationID string `json:"conversationID" binding:"required"` + SessionType int32 `json:"sessionType" binding:"required"` ReactionExtensionList map[string]*sdkws.KeyValue `json:"reactionExtensionList,omitempty" binding:"required"` - ClientMsgID string `json:"clientMsgID" binding:"required"` + ClientMsgID string `json:"clientMsgID" binding:"required"` Ex *string `json:"ex"` AttachedInfo *string `json:"attachedInfo"` IsReact bool `json:"isReact"` @@ -71,10 +71,11 @@ type ModifyMessageReactionExtensionsResp struct { } //type OperateMessageListReactionExtensionsReq struct { -// OperationID string `json:"operationID" binding:"required"` -// conversationID string `json:"conversationID" binding:"required"` -// SessionType string `json:"sessionType" binding:"required"` -// MessageReactionKeyList []*msg.GetMessageListReactionExtensionsReq_MessageReactionKey `json:"messageReactionKeyList" binding:"required"` +// OperationID string `json:"operationID" +// binding:"required"` conversationID string +// `json:"conversationID" binding:"required"` SessionType string +// `json:"sessionType" binding:"required"` MessageReactionKeyList +// []*msg.GetMessageListReactionExtensionsReq_MessageReactionKey `json:"messageReactionKeyList" binding:"required"` //} type OperateMessageListReactionExtensionsResp struct { @@ -99,12 +100,12 @@ type AddMessageReactionExtensionsReq ModifyMessageReactionExtensionsReq type AddMessageReactionExtensionsResp ModifyMessageReactionExtensionsResp type DeleteMessageReactionExtensionsReq struct { - OperationID string `json:"operationID" binding:"required"` - conversationID string `json:"conversationID" binding:"required"` - SessionType int32 `json:"sessionType" binding:"required"` - ClientMsgID string `json:"clientMsgID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` + conversationID string `json:"conversationID" binding:"required"` + SessionType int32 `json:"sessionType" binding:"required"` + ClientMsgID string `json:"clientMsgID" binding:"required"` IsExternalExtensions bool `json:"isExternalExtensions"` - MsgFirstModifyTime int64 `json:"msgFirstModifyTime" binding:"required"` + MsgFirstModifyTime int64 `json:"msgFirstModifyTime" binding:"required"` ReactionExtensionList []*sdkws.KeyValue `json:"reactionExtensionList" binding:"required"` } @@ -114,17 +115,17 @@ type DeleteMessageReactionExtensionsResp struct { type PictureBaseInfo struct { UUID string `mapstructure:"uuid"` - Type string `mapstructure:"type" ` - Size int64 `mapstructure:"size" ` - Width int32 `mapstructure:"width" ` + Type string `mapstructure:"type"` + Size int64 `mapstructure:"size"` + Width int32 `mapstructure:"width"` Height int32 `mapstructure:"height"` - Url string `mapstructure:"url" ` + Url string `mapstructure:"url"` } type PictureElem struct { SourcePath string `mapstructure:"sourcePath"` SourcePicture PictureBaseInfo `mapstructure:"sourcePicture"` - BigPicture PictureBaseInfo `mapstructure:"bigPicture" ` + BigPicture PictureBaseInfo `mapstructure:"bigPicture"` SnapshotPicture PictureBaseInfo `mapstructure:"snapshotPicture"` } type SoundElem struct { @@ -166,7 +167,7 @@ type LocationElem struct { Latitude float64 `mapstructure:"latitude"` } type CustomElem struct { - Data string `mapstructure:"data" validate:"required"` + Data string `mapstructure:"data" validate:"required"` Description string `mapstructure:"description"` Extension string `mapstructure:"extension"` } @@ -178,23 +179,23 @@ type RevokeElem struct { RevokeMsgClientID string `mapstructure:"revokeMsgClientID" validate:"required"` } type OANotificationElem struct { - NotificationName string `mapstructure:"notificationName" json:"notificationName" validate:"required"` + NotificationName string `mapstructure:"notificationName" json:"notificationName" validate:"required"` NotificationFaceURL string `mapstructure:"notificationFaceURL" json:"notificationFaceURL"` - NotificationType int32 `mapstructure:"notificationType" json:"notificationType" validate:"required"` - Text string `mapstructure:"text" json:"text" validate:"required"` - Url string `mapstructure:"url" json:"url"` - MixType int32 `mapstructure:"mixType" json:"mixType"` - PictureElem PictureElem `mapstructure:"pictureElem" json:"pictureElem"` - SoundElem SoundElem `mapstructure:"soundElem" json:"soundElem"` - VideoElem VideoElem `mapstructure:"videoElem" json:"videoElem"` - FileElem FileElem `mapstructure:"fileElem" json:"fileElem"` - Ex string `mapstructure:"ex" json:"ex"` + NotificationType int32 `mapstructure:"notificationType" json:"notificationType" validate:"required"` + Text string `mapstructure:"text" json:"text" validate:"required"` + Url string `mapstructure:"url" json:"url"` + MixType int32 `mapstructure:"mixType" json:"mixType"` + PictureElem PictureElem `mapstructure:"pictureElem" json:"pictureElem"` + SoundElem SoundElem `mapstructure:"soundElem" json:"soundElem"` + VideoElem VideoElem `mapstructure:"videoElem" json:"videoElem"` + FileElem FileElem `mapstructure:"fileElem" json:"fileElem"` + Ex string `mapstructure:"ex" json:"ex"` } type MessageRevoked struct { - RevokerID string `mapstructure:"revokerID" json:"revokerID" validate:"required"` - RevokerRole int32 `mapstructure:"revokerRole" json:"revokerRole" validate:"required"` - ClientMsgID string `mapstructure:"clientMsgID" json:"clientMsgID" validate:"required"` + RevokerID string `mapstructure:"revokerID" json:"revokerID" validate:"required"` + RevokerRole int32 `mapstructure:"revokerRole" json:"revokerRole" validate:"required"` + ClientMsgID string `mapstructure:"clientMsgID" json:"clientMsgID" validate:"required"` RevokerNickname string `mapstructure:"revokerNickname" json:"revokerNickname"` - SessionType int32 `mapstructure:"sessionType" json:"sessionType" validate:"required"` - Seq uint32 `mapstructure:"seq" json:"seq" validate:"required"` + SessionType int32 `mapstructure:"sessionType" json:"sessionType" validate:"required"` + Seq uint32 `mapstructure:"seq" json:"seq" validate:"required"` } diff --git a/pkg/apistruct/public.go b/pkg/apistruct/public.go index e943a4af1..fc3d3e707 100644 --- a/pkg/apistruct/public.go +++ b/pkg/apistruct/public.go @@ -1,18 +1,18 @@ package apistruct type ApiUserInfo struct { - UserID string `json:"userID" binding:"required,min=1,max=64" swaggo:"true,用户ID,"` - Nickname string `json:"nickname" binding:"omitempty,min=1,max=64" swaggo:"true,my id,19"` - FaceURL string `json:"faceURL" binding:"omitempty,max=1024"` - Gender int32 `json:"gender" binding:"omitempty,oneof=0 1 2"` + UserID string `json:"userID" binding:"required,min=1,max=64" swaggo:"true,用户ID,"` + Nickname string `json:"nickname" binding:"omitempty,min=1,max=64" swaggo:"true,my id,19"` + FaceURL string `json:"faceURL" binding:"omitempty,max=1024"` + Gender int32 `json:"gender" binding:"omitempty,oneof=0 1 2"` PhoneNumber string `json:"phoneNumber" binding:"omitempty,max=32"` - Birth int64 `json:"birth" binding:"omitempty"` - Email string `json:"email" binding:"omitempty,max=64"` + Birth int64 `json:"birth" binding:"omitempty"` + Email string `json:"email" binding:"omitempty,max=64"` CreateTime int64 `json:"createTime"` - Ex string `json:"ex" binding:"omitempty,max=1024"` + Ex string `json:"ex" binding:"omitempty,max=1024"` } type GroupAddMemberInfo struct { - UserID string `json:"userID" binding:"required"` + UserID string `json:"userID" binding:"required"` RoleLevel int32 `json:"roleLevel" binding:"required,oneof= 1 3"` } diff --git a/pkg/apistruct/third.go b/pkg/apistruct/third.go index 355700fee..80e9d4930 100644 --- a/pkg/apistruct/third.go +++ b/pkg/apistruct/third.go @@ -18,7 +18,7 @@ type MiniostorageCredentialResp struct { type MinioUploadFileReq struct { OperationID string `form:"operationID" binding:"required"` - FileType int `form:"fileType" binding:"required"` + FileType int `form:"fileType" binding:"required"` } type MinioUploadFile struct { @@ -36,12 +36,12 @@ type MinioUploadFileResp struct { type UploadUpdateAppReq struct { OperationID string `form:"operationID" binding:"required"` - Type int `form:"type" binding:"required"` - Version string `form:"version" binding:"required"` - File *multipart.FileHeader `form:"file" binding:"required"` + Type int `form:"type" binding:"required"` + Version string `form:"version" binding:"required"` + File *multipart.FileHeader `form:"file" binding:"required"` Yaml *multipart.FileHeader `form:"yaml"` ForceUpdate bool `form:"forceUpdate"` - UpdateLog string `form:"updateLog" binding:"required"` + UpdateLog string `form:"updateLog" binding:"required"` } type UploadUpdateAppResp struct { @@ -49,8 +49,8 @@ type UploadUpdateAppResp struct { type GetDownloadURLReq struct { OperationID string `json:"operationID" binding:"required"` - Type int `json:"type" binding:"required"` - Version string `json:"version" binding:"required"` + Type int `json:"type" binding:"required"` + Version string `json:"version" binding:"required"` } type GetDownloadURLResp struct { @@ -101,15 +101,15 @@ type GetRTCInvitationInfoStartAppResp struct { */ type FcmUpdateTokenReq struct { OperationID string `json:"operationID" binding:"required"` - Platform int `json:"platform" binding:"required,min=1,max=2"` //only for ios + android - FcmToken string `json:"fcmToken" binding:"required"` + Platform int `json:"platform" binding:"required,min=1,max=2"` //only for ios + android + FcmToken string `json:"fcmToken" binding:"required"` } type FcmUpdateTokenResp struct { } type SetAppBadgeReq struct { - OperationID string `json:"operationID" binding:"required"` - FromUserID string `json:"fromUserID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` + FromUserID string `json:"fromUserID" binding:"required"` AppUnreadCount int32 `json:"appUnreadCount"` } diff --git a/pkg/common/cmd/msg_gateway.go b/pkg/common/cmd/msg_gateway.go index 824e6163e..deea1c106 100644 --- a/pkg/common/cmd/msg_gateway.go +++ b/pkg/common/cmd/msg_gateway.go @@ -3,8 +3,9 @@ package cmd import ( "github.com/OpenIMSDK/Open-IM-Server/internal/msggateway" //"github.com/OpenIMSDK/Open-IM-Server/internal/msggateway" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/spf13/cobra" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" ) type MsgGatewayCmd struct { diff --git a/pkg/common/cmd/msg_transfer.go b/pkg/common/cmd/msg_transfer.go index 433faa5b5..d6d65a1dd 100644 --- a/pkg/common/cmd/msg_transfer.go +++ b/pkg/common/cmd/msg_transfer.go @@ -1,8 +1,9 @@ package cmd import ( - "github.com/OpenIMSDK/Open-IM-Server/internal/msgtransfer" "github.com/spf13/cobra" + + "github.com/OpenIMSDK/Open-IM-Server/internal/msgtransfer" ) type MsgTransferCmd struct { diff --git a/pkg/common/cmd/msg_utils.go b/pkg/common/cmd/msg_utils.go index f762976e7..00b3f7359 100644 --- a/pkg/common/cmd/msg_utils.go +++ b/pkg/common/cmd/msg_utils.go @@ -1,8 +1,9 @@ package cmd import ( - "github.com/OpenIMSDK/Open-IM-Server/internal/tools" "github.com/spf13/cobra" + + "github.com/OpenIMSDK/Open-IM-Server/internal/tools" ) type MsgUtilsCmd struct { diff --git a/pkg/common/cmd/root.go b/pkg/common/cmd/root.go index be04ac79a..ee586e247 100644 --- a/pkg/common/cmd/root.go +++ b/pkg/common/cmd/root.go @@ -3,10 +3,11 @@ package cmd import ( "fmt" + "github.com/spf13/cobra" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" - "github.com/spf13/cobra" ) type RootCmd struct { diff --git a/pkg/common/cmd/rpc.go b/pkg/common/cmd/rpc.go index d24073940..be44f7ce0 100644 --- a/pkg/common/cmd/rpc.go +++ b/pkg/common/cmd/rpc.go @@ -3,10 +3,11 @@ package cmd import ( "errors" - "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" - "github.com/OpenIMSDK/Open-IM-Server/pkg/startrpc" "github.com/spf13/cobra" "google.golang.org/grpc" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" + "github.com/OpenIMSDK/Open-IM-Server/pkg/startrpc" ) type RpcCmd struct { @@ -26,7 +27,10 @@ func (a *RpcCmd) Exec() error { return a.Execute() } -func (a *RpcCmd) StartSvr(name string, rpcFn func(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error) error { +func (a *RpcCmd) StartSvr( + name string, + rpcFn func(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error, +) error { if a.GetPortFlag() == 0 { return errors.New("port is required") } diff --git a/pkg/common/config/config.go b/pkg/common/config/config.go index a49dcec86..40161932b 100644 --- a/pkg/common/config/config.go +++ b/pkg/common/config/config.go @@ -303,6 +303,15 @@ type notification struct { } func GetServiceNames() []string { - return []string{Config.RpcRegisterName.OpenImUserName, Config.RpcRegisterName.OpenImFriendName, Config.RpcRegisterName.OpenImMsgName, Config.RpcRegisterName.OpenImPushName, Config.RpcRegisterName.OpenImMessageGatewayName, - Config.RpcRegisterName.OpenImGroupName, Config.RpcRegisterName.OpenImAuthName, Config.RpcRegisterName.OpenImConversationName, Config.RpcRegisterName.OpenImThirdName} + return []string{ + Config.RpcRegisterName.OpenImUserName, + Config.RpcRegisterName.OpenImFriendName, + Config.RpcRegisterName.OpenImMsgName, + Config.RpcRegisterName.OpenImPushName, + Config.RpcRegisterName.OpenImMessageGatewayName, + Config.RpcRegisterName.OpenImGroupName, + Config.RpcRegisterName.OpenImAuthName, + Config.RpcRegisterName.OpenImConversationName, + Config.RpcRegisterName.OpenImThirdName, + } } diff --git a/pkg/common/config/parse.go b/pkg/common/config/parse.go index 0a32e81c7..ebd6c49fc 100644 --- a/pkg/common/config/parse.go +++ b/pkg/common/config/parse.go @@ -7,10 +7,11 @@ import ( "path/filepath" "runtime" + "gopkg.in/yaml.v3" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "gopkg.in/yaml.v3" ) var ( diff --git a/pkg/common/constant/constant.go b/pkg/common/constant/constant.go index 72cc34af8..1d0a98558 100644 --- a/pkg/common/constant/constant.go +++ b/pkg/common/constant/constant.go @@ -125,7 +125,8 @@ const ( SingleTerminalLogin = 2 //The web side can be online at the same time, and the other side can only log in at one end WebAndOther = 3 - //The PC side is mutually exclusive, and the mobile side is mutually exclusive, but the web side can be online at the same time + // The PC side is mutually exclusive, and the mobile side is mutually exclusive, but the web side can be online at + // the same time PcMobileAndWeb = 4 //The PC terminal can be online at the same time,but other terminal only one of the endpoints can login PCAndOther = 5 diff --git a/pkg/common/convert/black.go b/pkg/common/convert/black.go index d09c30910..cadd822c8 100644 --- a/pkg/common/convert/black.go +++ b/pkg/common/convert/black.go @@ -8,7 +8,11 @@ import ( sdk "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" ) -func BlackDB2Pb(ctx context.Context, blackDBs []*relation.BlackModel, f func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error)) (blackPbs []*sdk.BlackInfo, err error) { +func BlackDB2Pb( + ctx context.Context, + blackDBs []*relation.BlackModel, + f func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error), +) (blackPbs []*sdk.BlackInfo, err error) { var userIDs []string for _, blackDB := range blackDBs { userIDs = append(userIDs, blackDB.BlockUserID) diff --git a/pkg/common/convert/friend.go b/pkg/common/convert/friend.go index d7e2345ae..b907f9ad0 100644 --- a/pkg/common/convert/friend.go +++ b/pkg/common/convert/friend.go @@ -16,7 +16,11 @@ func FriendPb2DB(friend *sdkws.FriendInfo) *relation.FriendModel { return dbFriend } -func FriendDB2Pb(ctx context.Context, friendDB *relation.FriendModel, getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error)) (*sdkws.FriendInfo, error) { +func FriendDB2Pb( + ctx context.Context, + friendDB *relation.FriendModel, + getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error), +) (*sdkws.FriendInfo, error) { pbfriend := &sdkws.FriendInfo{FriendUser: &sdkws.UserInfo{}} utils.CopyStructFields(pbfriend, friendDB) users, err := getUsers(ctx, []string{friendDB.FriendUserID}) @@ -31,7 +35,11 @@ func FriendDB2Pb(ctx context.Context, friendDB *relation.FriendModel, getUsers f return pbfriend, nil } -func FriendsDB2Pb(ctx context.Context, friendsDB []*relation.FriendModel, getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error)) (friendsPb []*sdkws.FriendInfo, err error) { +func FriendsDB2Pb( + ctx context.Context, + friendsDB []*relation.FriendModel, + getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error), +) (friendsPb []*sdkws.FriendInfo, err error) { var userID []string for _, friendDB := range friendsDB { userID = append(userID, friendDB.FriendUserID) @@ -53,7 +61,11 @@ func FriendsDB2Pb(ctx context.Context, friendsDB []*relation.FriendModel, getUse return friendsPb, nil } -func FriendRequestDB2Pb(ctx context.Context, friendRequests []*relation.FriendRequestModel, getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error)) ([]*sdkws.FriendRequest, error) { +func FriendRequestDB2Pb( + ctx context.Context, + friendRequests []*relation.FriendRequestModel, + getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error), +) ([]*sdkws.FriendRequest, error) { userIDMap := make(map[string]struct{}) for _, friendRequest := range friendRequests { userIDMap[friendRequest.ToUserID] = struct{}{} diff --git a/pkg/common/convert/group.go b/pkg/common/convert/group.go index 74972b6ce..adec0e6f3 100644 --- a/pkg/common/convert/group.go +++ b/pkg/common/convert/group.go @@ -41,7 +41,12 @@ func Pb2DbGroupRequest(req *pbGroup.GroupApplicationResponseReq, handleUserID st } } -func Db2PbCMSGroup(m *relation.GroupModel, ownerUserID string, ownerUserName string, memberCount uint32) *pbGroup.CMSGroup { +func Db2PbCMSGroup( + m *relation.GroupModel, + ownerUserID string, + ownerUserName string, + memberCount uint32, +) *pbGroup.CMSGroup { return &pbGroup.CMSGroup{ GroupInfo: Db2PbGroupInfo(m, ownerUserID, memberCount), GroupOwnerUserID: ownerUserID, @@ -66,7 +71,11 @@ func Db2PbGroupMember(m *relation.GroupMemberModel) *sdkws.GroupMemberFullInfo { } } -func Db2PbGroupRequest(m *relation.GroupRequestModel, user *sdkws.PublicUserInfo, group *sdkws.GroupInfo) *sdkws.GroupRequest { +func Db2PbGroupRequest( + m *relation.GroupRequestModel, + user *sdkws.PublicUserInfo, + group *sdkws.GroupInfo, +) *sdkws.GroupRequest { return &sdkws.GroupRequest{ UserInfo: user, GroupInfo: group, @@ -82,7 +91,11 @@ func Db2PbGroupRequest(m *relation.GroupRequestModel, user *sdkws.PublicUserInfo } } -func Db2PbGroupAbstractInfo(groupID string, groupMemberNumber uint32, groupMemberListHash uint64) *pbGroup.GroupAbstractInfo { +func Db2PbGroupAbstractInfo( + groupID string, + groupMemberNumber uint32, + groupMemberListHash uint64, +) *pbGroup.GroupAbstractInfo { return &pbGroup.GroupAbstractInfo{ GroupID: groupID, GroupMemberNumber: groupMemberNumber, diff --git a/pkg/common/db/cache/black.go b/pkg/common/db/cache/black.go index 18928567c..ca2f2ed9a 100644 --- a/pkg/common/db/cache/black.go +++ b/pkg/common/db/cache/black.go @@ -4,9 +4,10 @@ import ( "context" "time" - relationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/dtm-labs/rockscache" "github.com/redis/go-redis/v9" + + relationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" ) const ( @@ -31,7 +32,11 @@ type BlackCacheRedis struct { blackDB relationTb.BlackModelInterface } -func NewBlackCacheRedis(rdb redis.UniversalClient, blackDB relationTb.BlackModelInterface, options rockscache.Options) BlackCache { +func NewBlackCacheRedis( + rdb redis.UniversalClient, + blackDB relationTb.BlackModelInterface, + options rockscache.Options, +) BlackCache { rcClient := rockscache.NewClient(rdb, options) return &BlackCacheRedis{ expireTime: blackExpireTime, @@ -55,9 +60,15 @@ func (b *BlackCacheRedis) getBlackIDsKey(ownerUserID string) string { } func (b *BlackCacheRedis) GetBlackIDs(ctx context.Context, userID string) (blackIDs []string, err error) { - return getCache(ctx, b.rcClient, b.getBlackIDsKey(userID), b.expireTime, func(ctx context.Context) ([]string, error) { - return b.blackDB.FindBlackUserIDs(ctx, userID) - }) + return getCache( + ctx, + b.rcClient, + b.getBlackIDsKey(userID), + b.expireTime, + func(ctx context.Context) ([]string, error) { + return b.blackDB.FindBlackUserIDs(ctx, userID) + }, + ) } func (b *BlackCacheRedis) DelBlackIDs(ctx context.Context, userID string) BlackCache { diff --git a/pkg/common/db/cache/conversation.go b/pkg/common/db/cache/conversation.go index bc2f86cae..1005ee325 100644 --- a/pkg/common/db/cache/conversation.go +++ b/pkg/common/db/cache/conversation.go @@ -7,11 +7,12 @@ import ( "strings" "time" + "github.com/dtm-labs/rockscache" + "github.com/redis/go-redis/v9" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/relation" relationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "github.com/dtm-labs/rockscache" - "github.com/redis/go-redis/v9" ) const ( @@ -42,7 +43,11 @@ type ConversationCache interface { DelConvsersations(ownerUserID string, conversationIDs ...string) ConversationCache DelUsersConversation(conversationID string, ownerUserIDs ...string) ConversationCache // get one conversation from msgCache - GetConversations(ctx context.Context, ownerUserID string, conversationIDs []string) ([]*relationTb.ConversationModel, error) + GetConversations( + ctx context.Context, + ownerUserID string, + conversationIDs []string, + ) ([]*relationTb.ConversationModel, error) // get one user's all conversations from msgCache GetUserAllConversations(ctx context.Context, ownerUserID string) ([]*relationTb.ConversationModel, error) // get user conversation recv msg from msgCache @@ -58,13 +63,25 @@ type ConversationCache interface { GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (map[string]int64, error) DelUserAllHasReadSeqs(ownerUserID string, conversationIDs ...string) ConversationCache - GetConversationsByConversationID(ctx context.Context, conversationIDs []string) ([]*relationTb.ConversationModel, error) + GetConversationsByConversationID( + ctx context.Context, + conversationIDs []string, + ) ([]*relationTb.ConversationModel, error) DelConversationByConversationID(conversationIDs ...string) ConversationCache } -func NewConversationRedis(rdb redis.UniversalClient, opts rockscache.Options, db relationTb.ConversationModelInterface) ConversationCache { +func NewConversationRedis( + rdb redis.UniversalClient, + opts rockscache.Options, + db relationTb.ConversationModelInterface, +) ConversationCache { rcClient := rockscache.NewClient(rdb, opts) - return &ConversationRedisCache{rcClient: rcClient, metaCache: NewMetaCacheRedis(rcClient), conversationDB: db, expireTime: conversationExpireTime} + return &ConversationRedisCache{ + rcClient: rcClient, + metaCache: NewMetaCacheRedis(rcClient), + conversationDB: db, + expireTime: conversationExpireTime, + } } type ConversationRedisCache struct { @@ -74,13 +91,27 @@ type ConversationRedisCache struct { expireTime time.Duration } -func NewNewConversationRedis(rdb redis.UniversalClient, conversationDB *relation.ConversationGorm, options rockscache.Options) ConversationCache { +func NewNewConversationRedis( + rdb redis.UniversalClient, + conversationDB *relation.ConversationGorm, + options rockscache.Options, +) ConversationCache { rcClient := rockscache.NewClient(rdb, options) - return &ConversationRedisCache{rcClient: rcClient, metaCache: NewMetaCacheRedis(rcClient), conversationDB: conversationDB, expireTime: conversationExpireTime} + return &ConversationRedisCache{ + rcClient: rcClient, + metaCache: NewMetaCacheRedis(rcClient), + conversationDB: conversationDB, + expireTime: conversationExpireTime, + } } func (c *ConversationRedisCache) NewCache() ConversationCache { - return &ConversationRedisCache{rcClient: c.rcClient, metaCache: NewMetaCacheRedis(c.rcClient, c.metaCache.GetPreDelKeys()...), conversationDB: c.conversationDB, expireTime: c.expireTime} + return &ConversationRedisCache{ + rcClient: c.rcClient, + metaCache: NewMetaCacheRedis(c.rcClient, c.metaCache.GetPreDelKeys()...), + conversationDB: c.conversationDB, + expireTime: c.expireTime, + } } func (c *ConversationRedisCache) getConversationKey(ownerUserID, conversationID string) string { @@ -108,9 +139,15 @@ func (c *ConversationRedisCache) getConversationHasReadSeqKey(ownerUserID, conve } func (c *ConversationRedisCache) GetUserConversationIDs(ctx context.Context, ownerUserID string) ([]string, error) { - return getCache(ctx, c.rcClient, c.getConversationIDsKey(ownerUserID), c.expireTime, func(ctx context.Context) ([]string, error) { - return c.conversationDB.FindUserIDAllConversationID(ctx, ownerUserID) - }) + return getCache( + ctx, + c.rcClient, + c.getConversationIDsKey(ownerUserID), + c.expireTime, + func(ctx context.Context) ([]string, error) { + return c.conversationDB.FindUserIDAllConversationID(ctx, ownerUserID) + }, + ) } func (c *ConversationRedisCache) DelConversationIDs(userIDs ...string) ConversationCache { @@ -127,17 +164,26 @@ func (c *ConversationRedisCache) getUserConversationIDsHashKey(ownerUserID strin return conversationIDsHashKey + ownerUserID } -func (c *ConversationRedisCache) GetUserConversationIDsHash(ctx context.Context, ownerUserID string) (hash uint64, err error) { - return getCache(ctx, c.rcClient, c.getUserConversationIDsHashKey(ownerUserID), c.expireTime, func(ctx context.Context) (uint64, error) { - conversationIDs, err := c.GetUserConversationIDs(ctx, ownerUserID) - if err != nil { - return 0, err - } - utils.Sort(conversationIDs, true) - bi := big.NewInt(0) - bi.SetString(utils.Md5(strings.Join(conversationIDs, ";"))[0:8], 16) - return bi.Uint64(), nil - }) +func (c *ConversationRedisCache) GetUserConversationIDsHash( + ctx context.Context, + ownerUserID string, +) (hash uint64, err error) { + return getCache( + ctx, + c.rcClient, + c.getUserConversationIDsHashKey(ownerUserID), + c.expireTime, + func(ctx context.Context) (uint64, error) { + conversationIDs, err := c.GetUserConversationIDs(ctx, ownerUserID) + if err != nil { + return 0, err + } + utils.Sort(conversationIDs, true) + bi := big.NewInt(0) + bi.SetString(utils.Md5(strings.Join(conversationIDs, ";"))[0:8], 16) + return bi.Uint64(), nil + }, + ) } func (c *ConversationRedisCache) DelUserConversationIDsHash(ownerUserIDs ...string) ConversationCache { @@ -150,10 +196,19 @@ func (c *ConversationRedisCache) DelUserConversationIDsHash(ownerUserIDs ...stri return cache } -func (c *ConversationRedisCache) GetConversation(ctx context.Context, ownerUserID, conversationID string) (*relationTb.ConversationModel, error) { - return getCache(ctx, c.rcClient, c.getConversationKey(ownerUserID, conversationID), c.expireTime, func(ctx context.Context) (*relationTb.ConversationModel, error) { - return c.conversationDB.Take(ctx, ownerUserID, conversationID) - }) +func (c *ConversationRedisCache) GetConversation( + ctx context.Context, + ownerUserID, conversationID string, +) (*relationTb.ConversationModel, error) { + return getCache( + ctx, + c.rcClient, + c.getConversationKey(ownerUserID, conversationID), + c.expireTime, + func(ctx context.Context) (*relationTb.ConversationModel, error) { + return c.conversationDB.Take(ctx, ownerUserID, conversationID) + }, + ) } func (c *ConversationRedisCache) DelConvsersations(ownerUserID string, convsersationIDs ...string) ConversationCache { @@ -166,7 +221,10 @@ func (c *ConversationRedisCache) DelConvsersations(ownerUserID string, convsersa return cache } -func (c *ConversationRedisCache) getConversationIndex(convsation *relationTb.ConversationModel, keys []string) (int, error) { +func (c *ConversationRedisCache) getConversationIndex( + convsation *relationTb.ConversationModel, + keys []string, +) (int, error) { key := c.getConversationKey(convsation.OwnerUserID, convsation.ConversationID) for _i, _key := range keys { if _key == key { @@ -176,17 +234,31 @@ func (c *ConversationRedisCache) getConversationIndex(convsation *relationTb.Con return 0, errors.New("not found key:" + key + " in keys") } -func (c *ConversationRedisCache) GetConversations(ctx context.Context, ownerUserID string, conversationIDs []string) ([]*relationTb.ConversationModel, error) { +func (c *ConversationRedisCache) GetConversations( + ctx context.Context, + ownerUserID string, + conversationIDs []string, +) ([]*relationTb.ConversationModel, error) { var keys []string for _, conversarionID := range conversationIDs { keys = append(keys, c.getConversationKey(ownerUserID, conversarionID)) } - return batchGetCache(ctx, c.rcClient, keys, c.expireTime, c.getConversationIndex, func(ctx context.Context) ([]*relationTb.ConversationModel, error) { - return c.conversationDB.Find(ctx, ownerUserID, conversationIDs) - }) -} - -func (c *ConversationRedisCache) GetUserAllConversations(ctx context.Context, ownerUserID string) ([]*relationTb.ConversationModel, error) { + return batchGetCache( + ctx, + c.rcClient, + keys, + c.expireTime, + c.getConversationIndex, + func(ctx context.Context) ([]*relationTb.ConversationModel, error) { + return c.conversationDB.Find(ctx, ownerUserID, conversationIDs) + }, + ) +} + +func (c *ConversationRedisCache) GetUserAllConversations( + ctx context.Context, + ownerUserID string, +) ([]*relationTb.ConversationModel, error) { conversationIDs, err := c.GetUserConversationIDs(ctx, ownerUserID) if err != nil { return nil, err @@ -195,21 +267,46 @@ func (c *ConversationRedisCache) GetUserAllConversations(ctx context.Context, ow for _, conversarionID := range conversationIDs { keys = append(keys, c.getConversationKey(ownerUserID, conversarionID)) } - return batchGetCache(ctx, c.rcClient, keys, c.expireTime, c.getConversationIndex, func(ctx context.Context) ([]*relationTb.ConversationModel, error) { - return c.conversationDB.FindUserIDAllConversations(ctx, ownerUserID) - }) -} - -func (c *ConversationRedisCache) GetUserRecvMsgOpt(ctx context.Context, ownerUserID, conversationID string) (opt int, err error) { - return getCache(ctx, c.rcClient, c.getRecvMsgOptKey(ownerUserID, conversationID), c.expireTime, func(ctx context.Context) (opt int, err error) { - return c.conversationDB.GetUserRecvMsgOpt(ctx, ownerUserID, conversationID) - }) -} - -func (c *ConversationRedisCache) GetSuperGroupRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) (userIDs []string, err error) { - return getCache(ctx, c.rcClient, c.getSuperGroupRecvNotNotifyUserIDsKey(groupID), c.expireTime, func(ctx context.Context) (userIDs []string, err error) { - return c.conversationDB.FindSuperGroupRecvMsgNotNotifyUserIDs(ctx, groupID) - }) + return batchGetCache( + ctx, + c.rcClient, + keys, + c.expireTime, + c.getConversationIndex, + func(ctx context.Context) ([]*relationTb.ConversationModel, error) { + return c.conversationDB.FindUserIDAllConversations(ctx, ownerUserID) + }, + ) +} + +func (c *ConversationRedisCache) GetUserRecvMsgOpt( + ctx context.Context, + ownerUserID, conversationID string, +) (opt int, err error) { + return getCache( + ctx, + c.rcClient, + c.getRecvMsgOptKey(ownerUserID, conversationID), + c.expireTime, + func(ctx context.Context) (opt int, err error) { + return c.conversationDB.GetUserRecvMsgOpt(ctx, ownerUserID, conversationID) + }, + ) +} + +func (c *ConversationRedisCache) GetSuperGroupRecvMsgNotNotifyUserIDs( + ctx context.Context, + groupID string, +) (userIDs []string, err error) { + return getCache( + ctx, + c.rcClient, + c.getSuperGroupRecvNotNotifyUserIDsKey(groupID), + c.expireTime, + func(ctx context.Context) (userIDs []string, err error) { + return c.conversationDB.FindSuperGroupRecvMsgNotNotifyUserIDs(ctx, groupID) + }, + ) } func (c *ConversationRedisCache) DelUsersConversation(conversationID string, ownerUserIDs ...string) ConversationCache { @@ -234,17 +331,26 @@ func (c *ConversationRedisCache) DelSuperGroupRecvMsgNotNotifyUserIDs(groupID st return cache } -func (c *ConversationRedisCache) GetSuperGroupRecvMsgNotNotifyUserIDsHash(ctx context.Context, groupID string) (hash uint64, err error) { - return getCache(ctx, c.rcClient, c.getSuperGroupRecvNotNotifyUserIDsHashKey(groupID), c.expireTime, func(ctx context.Context) (hash uint64, err error) { - userIDs, err := c.GetSuperGroupRecvMsgNotNotifyUserIDs(ctx, groupID) - if err != nil { - return 0, err - } - utils.Sort(userIDs, true) - bi := big.NewInt(0) - bi.SetString(utils.Md5(strings.Join(userIDs, ";"))[0:8], 16) - return bi.Uint64(), nil - }) +func (c *ConversationRedisCache) GetSuperGroupRecvMsgNotNotifyUserIDsHash( + ctx context.Context, + groupID string, +) (hash uint64, err error) { + return getCache( + ctx, + c.rcClient, + c.getSuperGroupRecvNotNotifyUserIDsHashKey(groupID), + c.expireTime, + func(ctx context.Context) (hash uint64, err error) { + userIDs, err := c.GetSuperGroupRecvMsgNotNotifyUserIDs(ctx, groupID) + if err != nil { + return 0, err + } + utils.Sort(userIDs, true) + bi := big.NewInt(0) + bi.SetString(utils.Md5(strings.Join(userIDs, ";"))[0:8], 16) + return bi.Uint64(), nil + }, + ) } func (c *ConversationRedisCache) DelSuperGroupRecvMsgNotNotifyUserIDsHash(groupID string) ConversationCache { @@ -253,7 +359,10 @@ func (c *ConversationRedisCache) DelSuperGroupRecvMsgNotNotifyUserIDsHash(groupI return cache } -func (c *ConversationRedisCache) getUserAllHasReadSeqsIndex(conversationID string, conversationIDs []string) (int, error) { +func (c *ConversationRedisCache) getUserAllHasReadSeqsIndex( + conversationID string, + conversationIDs []string, +) (int, error) { for _i, _conversationID := range conversationIDs { if _conversationID == conversationID { return _i, nil @@ -262,7 +371,10 @@ func (c *ConversationRedisCache) getUserAllHasReadSeqsIndex(conversationID strin return 0, errors.New("not found key:" + conversationID + " in keys") } -func (c *ConversationRedisCache) GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (map[string]int64, error) { +func (c *ConversationRedisCache) GetUserAllHasReadSeqs( + ctx context.Context, + ownerUserID string, +) (map[string]int64, error) { conversationIDs, err := c.GetUserConversationIDs(ctx, ownerUserID) if err != nil { return nil, err @@ -271,12 +383,23 @@ func (c *ConversationRedisCache) GetUserAllHasReadSeqs(ctx context.Context, owne for _, conversarionID := range conversationIDs { keys = append(keys, c.getConversationHasReadSeqKey(ownerUserID, conversarionID)) } - return batchGetCacheMap(ctx, c.rcClient, keys, conversationIDs, c.expireTime, c.getUserAllHasReadSeqsIndex, func(ctx context.Context) (map[string]int64, error) { - return c.conversationDB.GetUserAllHasReadSeqs(ctx, ownerUserID) - }) -} - -func (c *ConversationRedisCache) DelUserAllHasReadSeqs(ownerUserID string, conversationIDs ...string) ConversationCache { + return batchGetCacheMap( + ctx, + c.rcClient, + keys, + conversationIDs, + c.expireTime, + c.getUserAllHasReadSeqsIndex, + func(ctx context.Context) (map[string]int64, error) { + return c.conversationDB.GetUserAllHasReadSeqs(ctx, ownerUserID) + }, + ) +} + +func (c *ConversationRedisCache) DelUserAllHasReadSeqs( + ownerUserID string, + conversationIDs ...string, +) ConversationCache { cache := c.NewCache() for _, conversationID := range conversationIDs { cache.AddKeys(c.getConversationHasReadSeqKey(ownerUserID, conversationID)) @@ -284,7 +407,10 @@ func (c *ConversationRedisCache) DelUserAllHasReadSeqs(ownerUserID string, conve return cache } -func (c *ConversationRedisCache) GetConversationsByConversationID(ctx context.Context, conversationIDs []string) ([]*relationTb.ConversationModel, error) { +func (c *ConversationRedisCache) GetConversationsByConversationID( + ctx context.Context, + conversationIDs []string, +) ([]*relationTb.ConversationModel, error) { panic("implement me") } diff --git a/pkg/common/db/cache/extend_msg_set.go b/pkg/common/db/cache/extend_msg_set.go index c405a7567..a45bf87f8 100644 --- a/pkg/common/db/cache/extend_msg_set.go +++ b/pkg/common/db/cache/extend_msg_set.go @@ -4,9 +4,10 @@ import ( "context" "time" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" "github.com/dtm-labs/rockscache" "github.com/redis/go-redis/v9" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" ) const ( @@ -17,7 +18,13 @@ const ( type ExtendMsgSetCache interface { metaCache NewCache() ExtendMsgSetCache - GetExtendMsg(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, firstModifyTime int64) (extendMsg *unrelation.ExtendMsgModel, err error) + GetExtendMsg( + ctx context.Context, + conversationID string, + sessionType int32, + clientMsgID string, + firstModifyTime int64, + ) (extendMsg *unrelation.ExtendMsgModel, err error) DelExtendMsg(clientMsgID string) ExtendMsgSetCache } @@ -28,7 +35,11 @@ type ExtendMsgSetCacheRedis struct { extendMsgSetDB unrelation.ExtendMsgSetModelInterface } -func NewExtendMsgSetCacheRedis(rdb redis.UniversalClient, extendMsgSetDB unrelation.ExtendMsgSetModelInterface, options rockscache.Options) ExtendMsgSetCache { +func NewExtendMsgSetCacheRedis( + rdb redis.UniversalClient, + extendMsgSetDB unrelation.ExtendMsgSetModelInterface, + options rockscache.Options, +) ExtendMsgSetCache { rcClient := rockscache.NewClient(rdb, options) return &ExtendMsgSetCacheRedis{ metaCache: NewMetaCacheRedis(rcClient), @@ -51,10 +62,22 @@ func (e *ExtendMsgSetCacheRedis) getKey(clientMsgID string) string { return extendMsgCache + clientMsgID } -func (e *ExtendMsgSetCacheRedis) GetExtendMsg(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, firstModifyTime int64) (extendMsg *unrelation.ExtendMsgModel, err error) { - return getCache(ctx, e.rcClient, e.getKey(clientMsgID), e.expireTime, func(ctx context.Context) (*unrelation.ExtendMsgModel, error) { - return e.extendMsgSetDB.TakeExtendMsg(ctx, conversationID, sessionType, clientMsgID, firstModifyTime) - }) +func (e *ExtendMsgSetCacheRedis) GetExtendMsg( + ctx context.Context, + conversationID string, + sessionType int32, + clientMsgID string, + firstModifyTime int64, +) (extendMsg *unrelation.ExtendMsgModel, err error) { + return getCache( + ctx, + e.rcClient, + e.getKey(clientMsgID), + e.expireTime, + func(ctx context.Context) (*unrelation.ExtendMsgModel, error) { + return e.extendMsgSetDB.TakeExtendMsg(ctx, conversationID, sessionType, clientMsgID, firstModifyTime) + }, + ) } func (e *ExtendMsgSetCacheRedis) DelExtendMsg(clientMsgID string) ExtendMsgSetCache { diff --git a/pkg/common/db/cache/friend.go b/pkg/common/db/cache/friend.go index a3890bb58..c816f6d4f 100644 --- a/pkg/common/db/cache/friend.go +++ b/pkg/common/db/cache/friend.go @@ -4,10 +4,11 @@ import ( "context" "time" - relationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" - "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" "github.com/dtm-labs/rockscache" "github.com/redis/go-redis/v9" + + relationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" + "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" ) const ( @@ -37,7 +38,11 @@ type FriendCacheRedis struct { rcClient *rockscache.Client } -func NewFriendCacheRedis(rdb redis.UniversalClient, friendDB relationTb.FriendModelInterface, options rockscache.Options) FriendCache { +func NewFriendCacheRedis( + rdb redis.UniversalClient, + friendDB relationTb.FriendModelInterface, + options rockscache.Options, +) FriendCache { rcClient := rockscache.NewClient(rdb, options) return &FriendCacheRedis{ metaCache: NewMetaCacheRedis(rcClient), @@ -48,7 +53,12 @@ func NewFriendCacheRedis(rdb redis.UniversalClient, friendDB relationTb.FriendMo } func (c *FriendCacheRedis) NewCache() FriendCache { - return &FriendCacheRedis{rcClient: c.rcClient, metaCache: NewMetaCacheRedis(c.rcClient, c.metaCache.GetPreDelKeys()...), friendDB: c.friendDB, expireTime: c.expireTime} + return &FriendCacheRedis{ + rcClient: c.rcClient, + metaCache: NewMetaCacheRedis(c.rcClient, c.metaCache.GetPreDelKeys()...), + friendDB: c.friendDB, + expireTime: c.expireTime, + } } func (f *FriendCacheRedis) getFriendIDsKey(ownerUserID string) string { @@ -64,9 +74,15 @@ func (f *FriendCacheRedis) getFriendKey(ownerUserID, friendUserID string) string } func (f *FriendCacheRedis) GetFriendIDs(ctx context.Context, ownerUserID string) (friendIDs []string, err error) { - return getCache(ctx, f.rcClient, f.getFriendIDsKey(ownerUserID), f.expireTime, func(ctx context.Context) ([]string, error) { - return f.friendDB.FindFriendUserIDs(ctx, ownerUserID) - }) + return getCache( + ctx, + f.rcClient, + f.getFriendIDsKey(ownerUserID), + f.expireTime, + func(ctx context.Context) ([]string, error) { + return f.friendDB.FindFriendUserIDs(ctx, ownerUserID) + }, + ) } func (f *FriendCacheRedis) DelFriendIDs(ownerUserID ...string) FriendCache { @@ -80,7 +96,10 @@ func (f *FriendCacheRedis) DelFriendIDs(ownerUserID ...string) FriendCache { } // todo -func (f *FriendCacheRedis) GetTwoWayFriendIDs(ctx context.Context, ownerUserID string) (twoWayFriendIDs []string, err error) { +func (f *FriendCacheRedis) GetTwoWayFriendIDs( + ctx context.Context, + ownerUserID string, +) (twoWayFriendIDs []string, err error) { friendIDs, err := f.GetFriendIDs(ctx, ownerUserID) if err != nil { return nil, err @@ -103,10 +122,19 @@ func (f *FriendCacheRedis) DelTwoWayFriendIDs(ctx context.Context, ownerUserID s return new } -func (f *FriendCacheRedis) GetFriend(ctx context.Context, ownerUserID, friendUserID string) (friend *relationTb.FriendModel, err error) { - return getCache(ctx, f.rcClient, f.getFriendKey(ownerUserID, friendUserID), f.expireTime, func(ctx context.Context) (*relationTb.FriendModel, error) { - return f.friendDB.Take(ctx, ownerUserID, friendUserID) - }) +func (f *FriendCacheRedis) GetFriend( + ctx context.Context, + ownerUserID, friendUserID string, +) (friend *relationTb.FriendModel, err error) { + return getCache( + ctx, + f.rcClient, + f.getFriendKey(ownerUserID, friendUserID), + f.expireTime, + func(ctx context.Context) (*relationTb.FriendModel, error) { + return f.friendDB.Take(ctx, ownerUserID, friendUserID) + }, + ) } func (f *FriendCacheRedis) DelFriend(ownerUserID, friendUserID string) FriendCache { diff --git a/pkg/common/db/cache/group.go b/pkg/common/db/cache/group.go index a27cce393..11c733d2b 100644 --- a/pkg/common/db/cache/group.go +++ b/pkg/common/db/cache/group.go @@ -6,11 +6,12 @@ import ( "strings" "time" + "github.com/dtm-labs/rockscache" + "github.com/redis/go-redis/v9" + relationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" unrelationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "github.com/dtm-labs/rockscache" - "github.com/redis/go-redis/v9" ) const ( @@ -49,10 +50,22 @@ type GroupCache interface { GetJoinedGroupIDs(ctx context.Context, userID string) (joinedGroupIDs []string, err error) DelJoinedGroupID(userID ...string) GroupCache - GetGroupMemberInfo(ctx context.Context, groupID, userID string) (groupMember *relationTb.GroupMemberModel, err error) - GetGroupMembersInfo(ctx context.Context, groupID string, userID []string) (groupMembers []*relationTb.GroupMemberModel, err error) + GetGroupMemberInfo( + ctx context.Context, + groupID, userID string, + ) (groupMember *relationTb.GroupMemberModel, err error) + GetGroupMembersInfo( + ctx context.Context, + groupID string, + userID []string, + ) (groupMembers []*relationTb.GroupMemberModel, err error) GetAllGroupMembersInfo(ctx context.Context, groupID string) (groupMembers []*relationTb.GroupMemberModel, err error) - GetGroupMembersPage(ctx context.Context, groupID string, userID []string, showNumber, pageNumber int32) (total uint32, groupMembers []*relationTb.GroupMemberModel, err error) + GetGroupMembersPage( + ctx context.Context, + groupID string, + userID []string, + showNumber, pageNumber int32, + ) (total uint32, groupMembers []*relationTb.GroupMemberModel, err error) DelGroupMembersInfo(groupID string, userID ...string) GroupCache @@ -70,7 +83,14 @@ type GroupCacheRedis struct { rcClient *rockscache.Client } -func NewGroupCacheRedis(rdb redis.UniversalClient, groupDB relationTb.GroupModelInterface, groupMemberDB relationTb.GroupMemberModelInterface, groupRequestDB relationTb.GroupRequestModelInterface, mongoClient unrelationTb.SuperGroupModelInterface, opts rockscache.Options) GroupCache { +func NewGroupCacheRedis( + rdb redis.UniversalClient, + groupDB relationTb.GroupModelInterface, + groupMemberDB relationTb.GroupMemberModelInterface, + groupRequestDB relationTb.GroupRequestModelInterface, + mongoClient unrelationTb.SuperGroupModelInterface, + opts rockscache.Options, +) GroupCache { rcClient := rockscache.NewClient(rdb, opts) return &GroupCacheRedis{rcClient: rcClient, expireTime: groupExpireTime, groupDB: groupDB, groupMemberDB: groupMemberDB, groupRequestDB: groupRequestDB, @@ -79,7 +99,15 @@ func NewGroupCacheRedis(rdb redis.UniversalClient, groupDB relationTb.GroupModel } func (g *GroupCacheRedis) NewCache() GroupCache { - return &GroupCacheRedis{rcClient: g.rcClient, expireTime: g.expireTime, groupDB: g.groupDB, groupMemberDB: g.groupMemberDB, groupRequestDB: g.groupRequestDB, mongoDB: g.mongoDB, metaCache: NewMetaCacheRedis(g.rcClient, g.metaCache.GetPreDelKeys()...)} + return &GroupCacheRedis{ + rcClient: g.rcClient, + expireTime: g.expireTime, + groupDB: g.groupDB, + groupMemberDB: g.groupMemberDB, + groupRequestDB: g.groupRequestDB, + mongoDB: g.mongoDB, + metaCache: NewMetaCacheRedis(g.rcClient, g.metaCache.GetPreDelKeys()...), + } } func (g *GroupCacheRedis) getGroupInfoKey(groupID string) string { @@ -135,20 +163,36 @@ func (g *GroupCacheRedis) GetGroupMemberIndex(groupMember *relationTb.GroupMembe } // / groupInfo -func (g *GroupCacheRedis) GetGroupsInfo(ctx context.Context, groupIDs []string) (groups []*relationTb.GroupModel, err error) { +func (g *GroupCacheRedis) GetGroupsInfo( + ctx context.Context, + groupIDs []string, +) (groups []*relationTb.GroupModel, err error) { var keys []string for _, group := range groupIDs { keys = append(keys, g.getGroupInfoKey(group)) } - return batchGetCache(ctx, g.rcClient, keys, g.expireTime, g.GetGroupIndex, func(ctx context.Context) ([]*relationTb.GroupModel, error) { - return g.groupDB.Find(ctx, groupIDs) - }) + return batchGetCache( + ctx, + g.rcClient, + keys, + g.expireTime, + g.GetGroupIndex, + func(ctx context.Context) ([]*relationTb.GroupModel, error) { + return g.groupDB.Find(ctx, groupIDs) + }, + ) } func (g *GroupCacheRedis) GetGroupInfo(ctx context.Context, groupID string) (group *relationTb.GroupModel, err error) { - return getCache(ctx, g.rcClient, g.getGroupInfoKey(groupID), g.expireTime, func(ctx context.Context) (*relationTb.GroupModel, error) { - return g.groupDB.Take(ctx, groupID) - }) + return getCache( + ctx, + g.rcClient, + g.getGroupInfoKey(groupID), + g.expireTime, + func(ctx context.Context) (*relationTb.GroupModel, error) { + return g.groupDB.Take(ctx, groupID) + }, + ) } func (g *GroupCacheRedis) DelGroupsInfo(groupIDs ...string) GroupCache { @@ -161,31 +205,50 @@ func (g *GroupCacheRedis) DelGroupsInfo(groupIDs ...string) GroupCache { return new } -func (g *GroupCacheRedis) GetJoinedSuperGroupIDs(ctx context.Context, userID string) (joinedSuperGroupIDs []string, err error) { - return getCache(ctx, g.rcClient, g.getJoinedSuperGroupsIDKey(userID), g.expireTime, func(ctx context.Context) ([]string, error) { - userGroup, err := g.mongoDB.GetSuperGroupByUserID(ctx, userID) - if err != nil { - return nil, err - } - return userGroup.GroupIDs, nil - }) +func (g *GroupCacheRedis) GetJoinedSuperGroupIDs( + ctx context.Context, + userID string, +) (joinedSuperGroupIDs []string, err error) { + return getCache( + ctx, + g.rcClient, + g.getJoinedSuperGroupsIDKey(userID), + g.expireTime, + func(ctx context.Context) ([]string, error) { + userGroup, err := g.mongoDB.GetSuperGroupByUserID(ctx, userID) + if err != nil { + return nil, err + } + return userGroup.GroupIDs, nil + }, + ) } -func (g *GroupCacheRedis) GetSuperGroupMemberIDs(ctx context.Context, groupIDs ...string) (models []*unrelationTb.SuperGroupModel, err error) { +func (g *GroupCacheRedis) GetSuperGroupMemberIDs( + ctx context.Context, + groupIDs ...string, +) (models []*unrelationTb.SuperGroupModel, err error) { var keys []string for _, group := range groupIDs { keys = append(keys, g.getSuperGroupMemberIDsKey(group)) } - return batchGetCache(ctx, g.rcClient, keys, g.expireTime, func(model *unrelationTb.SuperGroupModel, keys []string) (int, error) { - for i, key := range keys { - if g.getSuperGroupMemberIDsKey(model.GroupID) == key { - return i, nil + return batchGetCache( + ctx, + g.rcClient, + keys, + g.expireTime, + func(model *unrelationTb.SuperGroupModel, keys []string) (int, error) { + for i, key := range keys { + if g.getSuperGroupMemberIDsKey(model.GroupID) == key { + return i, nil + } } - } - return 0, errIndex - }, func(ctx context.Context) ([]*unrelationTb.SuperGroupModel, error) { - return g.mongoDB.FindSuperGroup(ctx, groupIDs) - }) + return 0, errIndex + }, + func(ctx context.Context) ([]*unrelationTb.SuperGroupModel, error) { + return g.mongoDB.FindSuperGroup(ctx, groupIDs) + }, + ) } // userJoinSuperGroup @@ -211,19 +274,28 @@ func (g *GroupCacheRedis) DelSuperGroupMemberIDs(groupIDs ...string) GroupCache // groupMembersHash func (g *GroupCacheRedis) GetGroupMembersHash(ctx context.Context, groupID string) (hashCode uint64, err error) { - return getCache(ctx, g.rcClient, g.getGroupMembersHashKey(groupID), g.expireTime, func(ctx context.Context) (uint64, error) { - userIDs, err := g.GetGroupMemberIDs(ctx, groupID) - if err != nil { - return 0, err - } - utils.Sort(userIDs, true) - bi := big.NewInt(0) - bi.SetString(utils.Md5(strings.Join(userIDs, ";"))[0:8], 16) - return bi.Uint64(), nil - }) -} - -func (g *GroupCacheRedis) GetGroupMemberHashMap(ctx context.Context, groupIDs []string) (map[string]*relationTb.GroupSimpleUserID, error) { + return getCache( + ctx, + g.rcClient, + g.getGroupMembersHashKey(groupID), + g.expireTime, + func(ctx context.Context) (uint64, error) { + userIDs, err := g.GetGroupMemberIDs(ctx, groupID) + if err != nil { + return 0, err + } + utils.Sort(userIDs, true) + bi := big.NewInt(0) + bi.SetString(utils.Md5(strings.Join(userIDs, ";"))[0:8], 16) + return bi.Uint64(), nil + }, + ) +} + +func (g *GroupCacheRedis) GetGroupMemberHashMap( + ctx context.Context, + groupIDs []string, +) (map[string]*relationTb.GroupSimpleUserID, error) { res := make(map[string]*relationTb.GroupSimpleUserID) for _, groupID := range groupIDs { hash, err := g.GetGroupMembersHash(ctx, groupID) @@ -247,9 +319,15 @@ func (g *GroupCacheRedis) DelGroupMembersHash(groupID string) GroupCache { // groupMemberIDs func (g *GroupCacheRedis) GetGroupMemberIDs(ctx context.Context, groupID string) (groupMemberIDs []string, err error) { - return getCache(ctx, g.rcClient, g.getGroupMemberIDsKey(groupID), g.expireTime, func(ctx context.Context) ([]string, error) { - return g.groupMemberDB.FindMemberUserID(ctx, groupID) - }) + return getCache( + ctx, + g.rcClient, + g.getGroupMemberIDsKey(groupID), + g.expireTime, + func(ctx context.Context) ([]string, error) { + return g.groupMemberDB.FindMemberUserID(ctx, groupID) + }, + ) } func (g *GroupCacheRedis) GetGroupsMemberIDs(ctx context.Context, groupIDs []string) (map[string][]string, error) { @@ -271,9 +349,15 @@ func (g *GroupCacheRedis) DelGroupMemberIDs(groupID string) GroupCache { } func (g *GroupCacheRedis) GetJoinedGroupIDs(ctx context.Context, userID string) (joinedGroupIDs []string, err error) { - return getCache(ctx, g.rcClient, g.getJoinedGroupsKey(userID), g.expireTime, func(ctx context.Context) ([]string, error) { - return g.groupMemberDB.FindUserJoinedGroupID(ctx, userID) - }) + return getCache( + ctx, + g.rcClient, + g.getJoinedGroupsKey(userID), + g.expireTime, + func(ctx context.Context) ([]string, error) { + return g.groupMemberDB.FindUserJoinedGroupID(ctx, userID) + }, + ) } func (g *GroupCacheRedis) DelJoinedGroupID(userIDs ...string) GroupCache { @@ -286,23 +370,48 @@ func (g *GroupCacheRedis) DelJoinedGroupID(userIDs ...string) GroupCache { return cache } -func (g *GroupCacheRedis) GetGroupMemberInfo(ctx context.Context, groupID, userID string) (groupMember *relationTb.GroupMemberModel, err error) { - return getCache(ctx, g.rcClient, g.getGroupMemberInfoKey(groupID, userID), g.expireTime, func(ctx context.Context) (*relationTb.GroupMemberModel, error) { - return g.groupMemberDB.Take(ctx, groupID, userID) - }) -} - -func (g *GroupCacheRedis) GetGroupMembersInfo(ctx context.Context, groupID string, userIDs []string) ([]*relationTb.GroupMemberModel, error) { +func (g *GroupCacheRedis) GetGroupMemberInfo( + ctx context.Context, + groupID, userID string, +) (groupMember *relationTb.GroupMemberModel, err error) { + return getCache( + ctx, + g.rcClient, + g.getGroupMemberInfoKey(groupID, userID), + g.expireTime, + func(ctx context.Context) (*relationTb.GroupMemberModel, error) { + return g.groupMemberDB.Take(ctx, groupID, userID) + }, + ) +} + +func (g *GroupCacheRedis) GetGroupMembersInfo( + ctx context.Context, + groupID string, + userIDs []string, +) ([]*relationTb.GroupMemberModel, error) { var keys []string for _, userID := range userIDs { keys = append(keys, g.getGroupMemberInfoKey(groupID, userID)) } - return batchGetCache(ctx, g.rcClient, keys, g.expireTime, g.GetGroupMemberIndex, func(ctx context.Context) ([]*relationTb.GroupMemberModel, error) { - return g.groupMemberDB.Find(ctx, []string{groupID}, userIDs, nil) - }) -} - -func (g *GroupCacheRedis) GetGroupMembersPage(ctx context.Context, groupID string, userIDs []string, showNumber, pageNumber int32) (total uint32, groupMembers []*relationTb.GroupMemberModel, err error) { + return batchGetCache( + ctx, + g.rcClient, + keys, + g.expireTime, + g.GetGroupMemberIndex, + func(ctx context.Context) ([]*relationTb.GroupMemberModel, error) { + return g.groupMemberDB.Find(ctx, []string{groupID}, userIDs, nil) + }, + ) +} + +func (g *GroupCacheRedis) GetGroupMembersPage( + ctx context.Context, + groupID string, + userIDs []string, + showNumber, pageNumber int32, +) (total uint32, groupMembers []*relationTb.GroupMemberModel, err error) { groupMemberIDs, err := g.GetGroupMemberIDs(ctx, groupID) if err != nil { return 0, nil, err @@ -316,7 +425,10 @@ func (g *GroupCacheRedis) GetGroupMembersPage(ctx context.Context, groupID strin return uint32(len(userIDs)), groupMembers, err } -func (g *GroupCacheRedis) GetAllGroupMembersInfo(ctx context.Context, groupID string) (groupMembers []*relationTb.GroupMemberModel, err error) { +func (g *GroupCacheRedis) GetAllGroupMembersInfo( + ctx context.Context, + groupID string, +) (groupMembers []*relationTb.GroupMemberModel, err error) { groupMemberIDs, err := g.GetGroupMemberIDs(ctx, groupID) if err != nil { return nil, err @@ -324,7 +436,10 @@ func (g *GroupCacheRedis) GetAllGroupMembersInfo(ctx context.Context, groupID st return g.GetGroupMembersInfo(ctx, groupID, groupMemberIDs) } -func (g *GroupCacheRedis) GetAllGroupMemberInfo(ctx context.Context, groupID string) ([]*relationTb.GroupMemberModel, error) { +func (g *GroupCacheRedis) GetAllGroupMemberInfo( + ctx context.Context, + groupID string, +) ([]*relationTb.GroupMemberModel, error) { groupMemberIDs, err := g.GetGroupMemberIDs(ctx, groupID) if err != nil { return nil, err @@ -333,9 +448,16 @@ func (g *GroupCacheRedis) GetAllGroupMemberInfo(ctx context.Context, groupID str for _, groupMemberID := range groupMemberIDs { keys = append(keys, g.getGroupMemberInfoKey(groupID, groupMemberID)) } - return batchGetCache(ctx, g.rcClient, keys, g.expireTime, g.GetGroupMemberIndex, func(ctx context.Context) ([]*relationTb.GroupMemberModel, error) { - return g.groupMemberDB.Find(ctx, []string{groupID}, groupMemberIDs, nil) - }) + return batchGetCache( + ctx, + g.rcClient, + keys, + g.expireTime, + g.GetGroupMemberIndex, + func(ctx context.Context) ([]*relationTb.GroupMemberModel, error) { + return g.groupMemberDB.Find(ctx, []string{groupID}, groupMemberIDs, nil) + }, + ) } func (g *GroupCacheRedis) DelGroupMembersInfo(groupID string, userIDs ...string) GroupCache { @@ -349,9 +471,15 @@ func (g *GroupCacheRedis) DelGroupMembersInfo(groupID string, userIDs ...string) } func (g *GroupCacheRedis) GetGroupMemberNum(ctx context.Context, groupID string) (memberNum int64, err error) { - return getCache(ctx, g.rcClient, g.getGroupMemberNumKey(groupID), g.expireTime, func(ctx context.Context) (int64, error) { - return g.groupMemberDB.TakeGroupMemberNum(ctx, groupID) - }) + return getCache( + ctx, + g.rcClient, + g.getGroupMemberNumKey(groupID), + g.expireTime, + func(ctx context.Context) (int64, error) { + return g.groupMemberDB.TakeGroupMemberNum(ctx, groupID) + }, + ) } func (g *GroupCacheRedis) DelGroupsMemberNum(groupID ...string) GroupCache { diff --git a/pkg/common/db/cache/init_redis.go b/pkg/common/db/cache/init_redis.go index c0277c445..e4b950ee1 100644 --- a/pkg/common/db/cache/init_redis.go +++ b/pkg/common/db/cache/init_redis.go @@ -6,10 +6,11 @@ import ( "fmt" "time" + "github.com/redis/go-redis/v9" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mw/specialerror" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" - "github.com/redis/go-redis/v9" ) func NewRedis() (redis.UniversalClient, error) { diff --git a/pkg/common/db/cache/meta_cache.go b/pkg/common/db/cache/meta_cache.go index b001fa227..e12f8e93c 100644 --- a/pkg/common/db/cache/meta_cache.go +++ b/pkg/common/db/cache/meta_cache.go @@ -7,10 +7,11 @@ import ( "fmt" "time" + "github.com/dtm-labs/rockscache" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "github.com/dtm-labs/rockscache" ) const ( @@ -48,7 +49,14 @@ func (m *metaCacheRedis) ExecDel(ctx context.Context) error { for { if err := m.rcClient.TagAsDeletedBatch2(ctx, m.keys); err != nil { if retryTimes >= m.maxRetryTimes { - err = errs.ErrInternalServer.Wrap(fmt.Sprintf("delete cache error: %v, keys: %v, retry times %d, please check redis server", err, m.keys, retryTimes)) + err = errs.ErrInternalServer.Wrap( + fmt.Sprintf( + "delete cache error: %v, keys: %v, retry times %d, please check redis server", + err, + m.keys, + retryTimes, + ), + ) log.ZWarn(ctx, "delete cache failed, please handle keys", err, "keys", m.keys) return err } @@ -84,7 +92,13 @@ func GetDefaultOpt() rockscache.Options { return opts } -func getCache[T any](ctx context.Context, rcClient *rockscache.Client, key string, expire time.Duration, fn func(ctx context.Context) (T, error)) (T, error) { +func getCache[T any]( + ctx context.Context, + rcClient *rockscache.Client, + key string, + expire time.Duration, + fn func(ctx context.Context) (T, error), +) (T, error) { var t T var write bool v, err := rcClient.Fetch2(ctx, key, expire, func() (s string, err error) { @@ -116,7 +130,14 @@ func getCache[T any](ctx context.Context, rcClient *rockscache.Client, key strin return t, nil } -func batchGetCache[T any](ctx context.Context, rcClient *rockscache.Client, keys []string, expire time.Duration, keyIndexFn func(t T, keys []string) (int, error), fn func(ctx context.Context) ([]T, error)) ([]T, error) { +func batchGetCache[T any]( + ctx context.Context, + rcClient *rockscache.Client, + keys []string, + expire time.Duration, + keyIndexFn func(t T, keys []string) (int, error), + fn func(ctx context.Context) ([]T, error), +) ([]T, error) { batchMap, err := rcClient.FetchBatch2(ctx, keys, expire, func(idxs []int) (m map[int]string, err error) { values := make(map[int]string) tArrays, err := fn(ctx) @@ -153,7 +174,14 @@ func batchGetCache[T any](ctx context.Context, rcClient *rockscache.Client, keys return tArrays, nil } -func batchGetCacheMap[T any](ctx context.Context, rcClient *rockscache.Client, keys, originKeys []string, expire time.Duration, keyIndexFn func(s string, keys []string) (int, error), fn func(ctx context.Context) (map[string]T, error)) (map[string]T, error) { +func batchGetCacheMap[T any]( + ctx context.Context, + rcClient *rockscache.Client, + keys, originKeys []string, + expire time.Duration, + keyIndexFn func(s string, keys []string) (int, error), + fn func(ctx context.Context) (map[string]T, error), +) (map[string]T, error) { batchMap, err := rcClient.FetchBatch2(ctx, keys, expire, func(idxs []int) (m map[int]string, err error) { tArrays, err := fn(ctx) if err != nil { diff --git a/pkg/common/db/cache/msg.go b/pkg/common/db/cache/msg.go index 8baa047c0..e01836118 100644 --- a/pkg/common/db/cache/msg.go +++ b/pkg/common/db/cache/msg.go @@ -5,16 +5,18 @@ import ( "strconv" "time" - "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "github.com/dtm-labs/rockscache" + "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" + + "github.com/gogo/protobuf/jsonpb" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" unRelationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "github.com/gogo/protobuf/jsonpb" "github.com/redis/go-redis/v9" ) @@ -86,7 +88,11 @@ type MsgModel interface { GetTokensWithoutError(ctx context.Context, userID string, platformID int) (map[string]int, error) SetTokenMapByUidPid(ctx context.Context, userID string, platformID int, m map[string]int) error DeleteTokenByUidPid(ctx context.Context, userID string, platformID int, fields []string) error - GetMessagesBySeq(ctx context.Context, conversationID string, seqs []int64) (seqMsg []*sdkws.MsgData, failedSeqList []int64, err error) + GetMessagesBySeq( + ctx context.Context, + conversationID string, + seqs []int64, + ) (seqMsg []*sdkws.MsgData, failedSeqList []int64, err error) SetMessageToCache(ctx context.Context, conversationID string, msgs []*sdkws.MsgData) (int, error) UserDeleteMsgs(ctx context.Context, conversationID string, seqs []int64, userID string) error DelUserDeleteMsgsList(ctx context.Context, conversationID string, seqs []int64) @@ -99,7 +105,12 @@ type MsgModel interface { JudgeMessageReactionExist(ctx context.Context, clientMsgID string, sessionType int32) (bool, error) GetOneMessageAllReactionList(ctx context.Context, clientMsgID string, sessionType int32) (map[string]string, error) DeleteOneMessageKey(ctx context.Context, clientMsgID string, sessionType int32, subKey string) error - SetMessageReactionExpire(ctx context.Context, clientMsgID string, sessionType int32, expiration time.Duration) (bool, error) + SetMessageReactionExpire( + ctx context.Context, + clientMsgID string, + sessionType int32, + expiration time.Duration, + ) (bool, error) GetMessageTypeKeyValue(ctx context.Context, clientMsgID string, sessionType int32, typeKey string) (string, error) SetMessageTypeKeyValue(ctx context.Context, clientMsgID string, sessionType int32, typeKey, value string) error LockMessageTypeKey(ctx context.Context, clientMsgID string, TypeKey string) error @@ -130,15 +141,28 @@ func (c *msgCache) getHasReadSeqKey(conversationID string, userID string) string return hasReadSeq + userID + ":" + conversationID } -func (c *msgCache) setSeq(ctx context.Context, conversationID string, seq int64, getkey func(conversationID string) string) error { +func (c *msgCache) setSeq( + ctx context.Context, + conversationID string, + seq int64, + getkey func(conversationID string) string, +) error { return utils.Wrap1(c.rdb.Set(ctx, getkey(conversationID), seq, 0).Err()) } -func (c *msgCache) getSeq(ctx context.Context, conversationID string, getkey func(conversationID string) string) (int64, error) { +func (c *msgCache) getSeq( + ctx context.Context, + conversationID string, + getkey func(conversationID string) string, +) (int64, error) { return utils.Wrap2(c.rdb.Get(ctx, getkey(conversationID)).Int64()) } -func (c *msgCache) getSeqs(ctx context.Context, items []string, getkey func(s string) string) (m map[string]int64, err error) { +func (c *msgCache) getSeqs( + ctx context.Context, + items []string, + getkey func(s string) string, +) (m map[string]int64, err error) { pipe := c.rdb.Pipeline() for _, v := range items { if err := pipe.Get(ctx, getkey(v)).Err(); err != nil && err != redis.Nil { @@ -209,16 +233,30 @@ func (c *msgCache) GetConversationUserMinSeq(ctx context.Context, conversationID return utils.Wrap2(c.rdb.Get(ctx, c.getConversationUserMinSeqKey(conversationID, userID)).Int64()) } -func (c *msgCache) GetConversationUserMinSeqs(ctx context.Context, conversationID string, userIDs []string) (m map[string]int64, err error) { +func (c *msgCache) GetConversationUserMinSeqs( + ctx context.Context, + conversationID string, + userIDs []string, +) (m map[string]int64, err error) { return c.getSeqs(ctx, userIDs, func(userID string) string { return c.getConversationUserMinSeqKey(conversationID, userID) }) } -func (c *msgCache) SetConversationUserMinSeq(ctx context.Context, conversationID string, userID string, minSeq int64) error { + +func (c *msgCache) SetConversationUserMinSeq( + ctx context.Context, + conversationID string, + userID string, + minSeq int64, +) error { return utils.Wrap1(c.rdb.Set(ctx, c.getConversationUserMinSeqKey(conversationID, userID), minSeq, 0).Err()) } -func (c *msgCache) SetConversationUserMinSeqs(ctx context.Context, conversationID string, seqs map[string]int64) (err error) { +func (c *msgCache) SetConversationUserMinSeqs( + ctx context.Context, + conversationID string, + seqs map[string]int64, +) (err error) { return c.setSeqs(ctx, seqs, func(userID string) string { return c.getConversationUserMinSeqKey(conversationID, userID) }) @@ -246,7 +284,11 @@ func (c *msgCache) UserSetHasReadSeqs(ctx context.Context, userID string, hasRea }) } -func (c *msgCache) GetHasReadSeqs(ctx context.Context, userID string, conversationIDs []string) (map[string]int64, error) { +func (c *msgCache) GetHasReadSeqs( + ctx context.Context, + userID string, + conversationIDs []string, +) (map[string]int64, error) { return c.getSeqs(ctx, conversationIDs, func(conversationID string) string { return c.getHasReadSeqKey(conversationID, userID) }) @@ -296,7 +338,11 @@ func (c *msgCache) allMessageCacheKey(conversationID string) string { return messageCache + conversationID + "_*" } -func (c *msgCache) GetMessagesBySeq(ctx context.Context, conversationID string, seqs []int64) (seqMsgs []*sdkws.MsgData, failedSeqs []int64, err error) { +func (c *msgCache) GetMessagesBySeq( + ctx context.Context, + conversationID string, + seqs []int64, +) (seqMsgs []*sdkws.MsgData, failedSeqs []int64, err error) { pipe := c.rdb.Pipeline() for _, v := range seqs { //MESSAGE_CACHE:169.254.225.224_reliability1653387820_0_1 @@ -404,7 +450,17 @@ func (c *msgCache) DelUserDeleteMsgsList(ctx context.Context, conversationID str err = pipe.SRem(ctx, c.getUserDelList(conversationID, userID), seq).Err() if err != nil { failedFlag = true - log.ZWarn(ctx, "DelUserDeleteMsgsList failed", err, "conversationID", conversationID, "seq", seq, "userID", userID) + log.ZWarn( + ctx, + "DelUserDeleteMsgsList failed", + err, + "conversationID", + conversationID, + "seq", + seq, + "userID", + userID, + ) } } if !failedFlag { @@ -499,8 +555,17 @@ func (c *msgCache) GetSendMsgStatus(ctx context.Context, id string) (int32, erro return int32(result), errs.Wrap(err) } -func (c *msgCache) SetFcmToken(ctx context.Context, account string, platformID int, fcmToken string, expireTime int64) (err error) { - return errs.Wrap(c.rdb.Set(ctx, fcmToken+account+":"+strconv.Itoa(platformID), fcmToken, time.Duration(expireTime)*time.Second).Err()) +func (c *msgCache) SetFcmToken( + ctx context.Context, + account string, + platformID int, + fcmToken string, + expireTime int64, +) (err error) { + return errs.Wrap( + c.rdb.Set(ctx, fcmToken+account+":"+strconv.Itoa(platformID), fcmToken, time.Duration(expireTime)*time.Second). + Err(), + ) } func (c *msgCache) GetFcmToken(ctx context.Context, account string, platformID int) (string, error) { @@ -556,22 +621,46 @@ func (c *msgCache) JudgeMessageReactionExist(ctx context.Context, clientMsgID st return n > 0, nil } -func (c *msgCache) SetMessageTypeKeyValue(ctx context.Context, clientMsgID string, sessionType int32, typeKey, value string) error { +func (c *msgCache) SetMessageTypeKeyValue( + ctx context.Context, + clientMsgID string, + sessionType int32, + typeKey, value string, +) error { return errs.Wrap(c.rdb.HSet(ctx, c.getMessageReactionExPrefix(clientMsgID, sessionType), typeKey, value).Err()) } -func (c *msgCache) SetMessageReactionExpire(ctx context.Context, clientMsgID string, sessionType int32, expiration time.Duration) (bool, error) { +func (c *msgCache) SetMessageReactionExpire( + ctx context.Context, + clientMsgID string, + sessionType int32, + expiration time.Duration, +) (bool, error) { return utils.Wrap2(c.rdb.Expire(ctx, c.getMessageReactionExPrefix(clientMsgID, sessionType), expiration).Result()) } -func (c *msgCache) GetMessageTypeKeyValue(ctx context.Context, clientMsgID string, sessionType int32, typeKey string) (string, error) { +func (c *msgCache) GetMessageTypeKeyValue( + ctx context.Context, + clientMsgID string, + sessionType int32, + typeKey string, +) (string, error) { return utils.Wrap2(c.rdb.HGet(ctx, c.getMessageReactionExPrefix(clientMsgID, sessionType), typeKey).Result()) } -func (c *msgCache) GetOneMessageAllReactionList(ctx context.Context, clientMsgID string, sessionType int32) (map[string]string, error) { +func (c *msgCache) GetOneMessageAllReactionList( + ctx context.Context, + clientMsgID string, + sessionType int32, +) (map[string]string, error) { return utils.Wrap2(c.rdb.HGetAll(ctx, c.getMessageReactionExPrefix(clientMsgID, sessionType)).Result()) } -func (c *msgCache) DeleteOneMessageKey(ctx context.Context, clientMsgID string, sessionType int32, subKey string) error { +func (c *msgCache) DeleteOneMessageKey( + ctx context.Context, + clientMsgID string, + sessionType int32, + subKey string, +) error { return errs.Wrap(c.rdb.HDel(ctx, c.getMessageReactionExPrefix(clientMsgID, sessionType), subKey).Err()) } diff --git a/pkg/common/db/cache/user.go b/pkg/common/db/cache/user.go index a9eae5035..c8e8b1cc5 100644 --- a/pkg/common/db/cache/user.go +++ b/pkg/common/db/cache/user.go @@ -4,9 +4,10 @@ import ( "context" "time" - relationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/dtm-labs/rockscache" "github.com/redis/go-redis/v9" + + relationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" ) const ( @@ -32,7 +33,11 @@ type UserCacheRedis struct { rcClient *rockscache.Client } -func NewUserCacheRedis(rdb redis.UniversalClient, userDB relationTb.UserModelInterface, options rockscache.Options) UserCache { +func NewUserCacheRedis( + rdb redis.UniversalClient, + userDB relationTb.UserModelInterface, + options rockscache.Options, +) UserCache { rcClient := rockscache.NewClient(rdb, options) return &UserCacheRedis{ metaCache: NewMetaCacheRedis(rcClient), @@ -60,9 +65,15 @@ func (u *UserCacheRedis) getUserGlobalRecvMsgOptKey(userID string) string { } func (u *UserCacheRedis) GetUserInfo(ctx context.Context, userID string) (userInfo *relationTb.UserModel, err error) { - return getCache(ctx, u.rcClient, u.getUserInfoKey(userID), u.expireTime, func(ctx context.Context) (*relationTb.UserModel, error) { - return u.userDB.Take(ctx, userID) - }) + return getCache( + ctx, + u.rcClient, + u.getUserInfoKey(userID), + u.expireTime, + func(ctx context.Context) (*relationTb.UserModel, error) { + return u.userDB.Take(ctx, userID) + }, + ) } func (u *UserCacheRedis) GetUsersInfo(ctx context.Context, userIDs []string) ([]*relationTb.UserModel, error) { @@ -70,16 +81,23 @@ func (u *UserCacheRedis) GetUsersInfo(ctx context.Context, userIDs []string) ([] for _, userID := range userIDs { keys = append(keys, u.getUserInfoKey(userID)) } - return batchGetCache(ctx, u.rcClient, keys, u.expireTime, func(user *relationTb.UserModel, keys []string) (int, error) { - for i, key := range keys { - if key == u.getUserInfoKey(user.UserID) { - return i, nil + return batchGetCache( + ctx, + u.rcClient, + keys, + u.expireTime, + func(user *relationTb.UserModel, keys []string) (int, error) { + for i, key := range keys { + if key == u.getUserInfoKey(user.UserID) { + return i, nil + } } - } - return 0, errIndex - }, func(ctx context.Context) ([]*relationTb.UserModel, error) { - return u.userDB.Find(ctx, userIDs) - }) + return 0, errIndex + }, + func(ctx context.Context) ([]*relationTb.UserModel, error) { + return u.userDB.Find(ctx, userIDs) + }, + ) } func (u *UserCacheRedis) DelUsersInfo(userIDs ...string) UserCache { @@ -93,9 +111,15 @@ func (u *UserCacheRedis) DelUsersInfo(userIDs ...string) UserCache { } func (u *UserCacheRedis) GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) (opt int, err error) { - return getCache(ctx, u.rcClient, u.getUserGlobalRecvMsgOptKey(userID), u.expireTime, func(ctx context.Context) (int, error) { - return u.userDB.GetUserGlobalRecvMsgOpt(ctx, userID) - }) + return getCache( + ctx, + u.rcClient, + u.getUserGlobalRecvMsgOptKey(userID), + u.expireTime, + func(ctx context.Context) (int, error) { + return u.userDB.GetUserGlobalRecvMsgOpt(ctx, userID) + }, + ) } func (u *UserCacheRedis) DelUsersGlobalRecvMsgOpt(userIDs ...string) UserCache { diff --git a/pkg/common/db/controller/auth.go b/pkg/common/db/controller/auth.go index 6d6add902..46d2eb55a 100644 --- a/pkg/common/db/controller/auth.go +++ b/pkg/common/db/controller/auth.go @@ -3,11 +3,12 @@ package controller import ( "context" + "github.com/golang-jwt/jwt/v4" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/tokenverify" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "github.com/golang-jwt/jwt/v4" ) type AuthDatabase interface { @@ -29,7 +30,11 @@ func NewAuthDatabase(cache cache.MsgModel, accessSecret string, accessExpire int } // 结果为空 不返回错误 -func (a *authDatabase) GetTokensWithoutError(ctx context.Context, userID string, platformID int) (map[string]int, error) { +func (a *authDatabase) GetTokensWithoutError( + ctx context.Context, + userID string, + platformID int, +) (map[string]int, error) { return a.cache.GetTokensWithoutError(ctx, userID, platformID) } diff --git a/pkg/common/db/controller/black.go b/pkg/common/db/controller/black.go index 61fe19c79..fad4d8acb 100644 --- a/pkg/common/db/controller/black.go +++ b/pkg/common/db/controller/black.go @@ -15,7 +15,11 @@ type BlackDatabase interface { // Delete 删除黑名单 Delete(ctx context.Context, blacks []*relation.BlackModel) (err error) // FindOwnerBlacks 获取黑名单列表 - FindOwnerBlacks(ctx context.Context, ownerUserID string, pageNumber, showNumber int32) (blacks []*relation.BlackModel, total int64, err error) + FindOwnerBlacks( + ctx context.Context, + ownerUserID string, + pageNumber, showNumber int32, + ) (blacks []*relation.BlackModel, total int64, err error) FindBlackIDs(ctx context.Context, ownerUserID string) (blackIDs []string, err error) // CheckIn 检查user2是否在user1的黑名单列表中(inUser1Blacks==true) 检查user1是否在user2的黑名单列表中(inUser2Blacks==true) CheckIn(ctx context.Context, userID1, userID2 string) (inUser1Blacks bool, inUser2Blacks bool, err error) @@ -55,12 +59,19 @@ func (b *blackDatabase) deleteBlackIDsCache(ctx context.Context, blacks []*relat } // FindOwnerBlacks 获取黑名单列表 -func (b *blackDatabase) FindOwnerBlacks(ctx context.Context, ownerUserID string, pageNumber, showNumber int32) (blacks []*relation.BlackModel, total int64, err error) { +func (b *blackDatabase) FindOwnerBlacks( + ctx context.Context, + ownerUserID string, + pageNumber, showNumber int32, +) (blacks []*relation.BlackModel, total int64, err error) { return b.black.FindOwnerBlacks(ctx, ownerUserID, pageNumber, showNumber) } // CheckIn 检查user2是否在user1的黑名单列表中(inUser1Blacks==true) 检查user1是否在user2的黑名单列表中(inUser2Blacks==true) -func (b *blackDatabase) CheckIn(ctx context.Context, userID1, userID2 string) (inUser1Blacks bool, inUser2Blacks bool, err error) { +func (b *blackDatabase) CheckIn( + ctx context.Context, + userID1, userID2 string, +) (inUser1Blacks bool, inUser2Blacks bool, err error) { userID1BlackIDs, err := b.cache.GetBlackIDs(ctx, userID1) if err != nil { return diff --git a/pkg/common/db/controller/chatlog.go b/pkg/common/db/controller/chatlog.go index fc03719f1..cb161699f 100644 --- a/pkg/common/db/controller/chatlog.go +++ b/pkg/common/db/controller/chatlog.go @@ -7,7 +7,11 @@ import ( type ChatLogDatabase interface { CreateChatLog(msg *pbMsg.MsgDataToMQ) error - GetChatLog(chatLog *relationTb.ChatLogModel, pageNumber, showNumber int32, contentTypes []int32) (int64, []relationTb.ChatLogModel, error) + GetChatLog( + chatLog *relationTb.ChatLogModel, + pageNumber, showNumber int32, + contentTypes []int32, + ) (int64, []relationTb.ChatLogModel, error) } func NewChatLogDatabase(chatLogModelInterface relationTb.ChatLogModelInterface) ChatLogDatabase { @@ -22,6 +26,10 @@ func (c *chatLogDatabase) CreateChatLog(msg *pbMsg.MsgDataToMQ) error { return c.chatLogModel.Create(msg) } -func (c *chatLogDatabase) GetChatLog(chatLog *relationTb.ChatLogModel, pageNumber, showNumber int32, contentTypes []int32) (int64, []relationTb.ChatLogModel, error) { +func (c *chatLogDatabase) GetChatLog( + chatLog *relationTb.ChatLogModel, + pageNumber, showNumber int32, + contentTypes []int32, +) (int64, []relationTb.ChatLogModel, error) { return c.chatLogModel.GetChatLog(chatLog, pageNumber, showNumber, contentTypes) } diff --git a/pkg/common/db/controller/conversation.go b/pkg/common/db/controller/conversation.go index 62c57c0fc..d5bec4376 100644 --- a/pkg/common/db/controller/conversation.go +++ b/pkg/common/db/controller/conversation.go @@ -13,13 +13,22 @@ import ( type ConversationDatabase interface { //UpdateUserConversationFiled 更新用户该会话的属性信息 - UpdateUsersConversationFiled(ctx context.Context, userIDs []string, conversationID string, args map[string]interface{}) error + UpdateUsersConversationFiled( + ctx context.Context, + userIDs []string, + conversationID string, + args map[string]interface{}, + ) error //CreateConversation 创建一批新的会话 CreateConversation(ctx context.Context, conversations []*relationTb.ConversationModel) error //SyncPeerUserPrivateConversation 同步对端私聊会话内部保证事务操作 SyncPeerUserPrivateConversationTx(ctx context.Context, conversation []*relationTb.ConversationModel) error //FindConversations 根据会话ID获取某个用户的多个会话 - FindConversations(ctx context.Context, ownerUserID string, conversationIDs []string) ([]*relationTb.ConversationModel, error) + FindConversations( + ctx context.Context, + ownerUserID string, + conversationIDs []string, + ) ([]*relationTb.ConversationModel, error) //FindRecvMsgNotNotifyUserIDs 获取超级大群开启免打扰的用户ID FindRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) ([]string, error) //GetUserAllConversation 获取一个用户在服务器上所有的会话 @@ -27,16 +36,28 @@ type ConversationDatabase interface { //SetUserConversations 设置用户多个会话属性,如果会话不存在则创建,否则更新,内部保证原子性 SetUserConversations(ctx context.Context, ownerUserID string, conversations []*relationTb.ConversationModel) error //SetUsersConversationFiledTx 设置多个用户会话关于某个字段的更新操作,如果会话不存在则创建,否则更新,内部保证事务操作 - SetUsersConversationFiledTx(ctx context.Context, userIDs []string, conversation *relationTb.ConversationModel, filedMap map[string]interface{}) error + SetUsersConversationFiledTx( + ctx context.Context, + userIDs []string, + conversation *relationTb.ConversationModel, + filedMap map[string]interface{}, + ) error CreateGroupChatConversation(ctx context.Context, groupID string, userIDs []string) error GetConversationIDs(ctx context.Context, userID string) ([]string, error) GetUserConversationIDsHash(ctx context.Context, ownerUserID string) (hash uint64, err error) GetAllConversationIDs(ctx context.Context) ([]string, error) GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (map[string]int64, error) - GetConversationsByConversationID(ctx context.Context, conversationIDs []string) ([]*relationTb.ConversationModel, error) + GetConversationsByConversationID( + ctx context.Context, + conversationIDs []string, + ) ([]*relationTb.ConversationModel, error) } -func NewConversationDatabase(conversation relationTb.ConversationModelInterface, cache cache.ConversationCache, tx tx.Tx) ConversationDatabase { +func NewConversationDatabase( + conversation relationTb.ConversationModelInterface, + cache cache.ConversationCache, + tx tx.Tx, +) ConversationDatabase { return &conversationDatabase{ conversationDB: conversation, cache: cache, @@ -50,7 +71,12 @@ type conversationDatabase struct { tx tx.Tx } -func (c *conversationDatabase) SetUsersConversationFiledTx(ctx context.Context, userIDs []string, conversation *relationTb.ConversationModel, filedMap map[string]interface{}) (err error) { +func (c *conversationDatabase) SetUsersConversationFiledTx( + ctx context.Context, + userIDs []string, + conversation *relationTb.ConversationModel, + filedMap map[string]interface{}, +) (err error) { cache := c.cache.NewCache() if err := c.tx.Transaction(func(tx any) error { conversationTx := c.conversationDB.NewTx(tx) @@ -96,7 +122,12 @@ func (c *conversationDatabase) SetUsersConversationFiledTx(ctx context.Context, return cache.ExecDel(ctx) } -func (c *conversationDatabase) UpdateUsersConversationFiled(ctx context.Context, userIDs []string, conversationID string, args map[string]interface{}) error { +func (c *conversationDatabase) UpdateUsersConversationFiled( + ctx context.Context, + userIDs []string, + conversationID string, + args map[string]interface{}, +) error { _, err := c.conversationDB.UpdateByMap(ctx, userIDs, conversationID, args) if err != nil { return err @@ -104,7 +135,10 @@ func (c *conversationDatabase) UpdateUsersConversationFiled(ctx context.Context, return c.cache.DelUsersConversation(conversationID, userIDs...).ExecDel(ctx) } -func (c *conversationDatabase) CreateConversation(ctx context.Context, conversations []*relationTb.ConversationModel) error { +func (c *conversationDatabase) CreateConversation( + ctx context.Context, + conversations []*relationTb.ConversationModel, +) error { if err := c.conversationDB.Create(ctx, conversations); err != nil { return err } @@ -117,7 +151,10 @@ func (c *conversationDatabase) CreateConversation(ctx context.Context, conversat return cache.DelConversationIDs(userIDs...).DelUserConversationIDsHash(userIDs...).ExecDel(ctx) } -func (c *conversationDatabase) SyncPeerUserPrivateConversationTx(ctx context.Context, conversations []*relationTb.ConversationModel) error { +func (c *conversationDatabase) SyncPeerUserPrivateConversationTx( + ctx context.Context, + conversations []*relationTb.ConversationModel, +) error { cache := c.cache.NewCache() if err := c.tx.Transaction(func(tx any) error { conversationTx := c.conversationDB.NewTx(tx) @@ -153,19 +190,34 @@ func (c *conversationDatabase) SyncPeerUserPrivateConversationTx(ctx context.Con return c.cache.ExecDel(ctx) } -func (c *conversationDatabase) FindConversations(ctx context.Context, ownerUserID string, conversationIDs []string) ([]*relationTb.ConversationModel, error) { +func (c *conversationDatabase) FindConversations( + ctx context.Context, + ownerUserID string, + conversationIDs []string, +) ([]*relationTb.ConversationModel, error) { return c.cache.GetConversations(ctx, ownerUserID, conversationIDs) } -func (c *conversationDatabase) GetConversation(ctx context.Context, ownerUserID string, conversationID string) (*relationTb.ConversationModel, error) { +func (c *conversationDatabase) GetConversation( + ctx context.Context, + ownerUserID string, + conversationID string, +) (*relationTb.ConversationModel, error) { return c.cache.GetConversation(ctx, ownerUserID, conversationID) } -func (c *conversationDatabase) GetUserAllConversation(ctx context.Context, ownerUserID string) ([]*relationTb.ConversationModel, error) { +func (c *conversationDatabase) GetUserAllConversation( + ctx context.Context, + ownerUserID string, +) ([]*relationTb.ConversationModel, error) { return c.cache.GetUserAllConversations(ctx, ownerUserID) } -func (c *conversationDatabase) SetUserConversations(ctx context.Context, ownerUserID string, conversations []*relationTb.ConversationModel) error { +func (c *conversationDatabase) SetUserConversations( + ctx context.Context, + ownerUserID string, + conversations []*relationTb.ConversationModel, +) error { cache := c.cache.NewCache() if err := c.tx.Transaction(func(tx any) error { var conversationIDs []string @@ -215,7 +267,11 @@ func (c *conversationDatabase) FindRecvMsgNotNotifyUserIDs(ctx context.Context, return c.cache.GetSuperGroupRecvMsgNotNotifyUserIDs(ctx, groupID) } -func (c *conversationDatabase) CreateGroupChatConversation(ctx context.Context, groupID string, userIDs []string) error { +func (c *conversationDatabase) CreateGroupChatConversation( + ctx context.Context, + groupID string, + userIDs []string, +) error { cache := c.cache.NewCache() conversationID := utils.GetConversationIDBySessionType(constant.SuperGroupChatType, groupID) if err := c.tx.Transaction(func(tx any) error { @@ -255,7 +311,10 @@ func (c *conversationDatabase) GetConversationIDs(ctx context.Context, userID st return c.cache.GetUserConversationIDs(ctx, userID) } -func (c *conversationDatabase) GetUserConversationIDsHash(ctx context.Context, ownerUserID string) (hash uint64, err error) { +func (c *conversationDatabase) GetUserConversationIDsHash( + ctx context.Context, + ownerUserID string, +) (hash uint64, err error) { return c.cache.GetUserConversationIDsHash(ctx, ownerUserID) } @@ -263,10 +322,16 @@ func (c *conversationDatabase) GetAllConversationIDs(ctx context.Context) ([]str return c.conversationDB.GetAllConversationIDs(ctx) } -func (c *conversationDatabase) GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (map[string]int64, error) { +func (c *conversationDatabase) GetUserAllHasReadSeqs( + ctx context.Context, + ownerUserID string, +) (map[string]int64, error) { return c.cache.GetUserAllHasReadSeqs(ctx, ownerUserID) } -func (c *conversationDatabase) GetConversationsByConversationID(ctx context.Context, conversationIDs []string) ([]*relationTb.ConversationModel, error) { +func (c *conversationDatabase) GetConversationsByConversationID( + ctx context.Context, + conversationIDs []string, +) ([]*relationTb.ConversationModel, error) { return c.conversationDB.GetConversationsByConversationID(ctx, conversationIDs) } diff --git a/pkg/common/db/controller/extend_msg.go b/pkg/common/db/controller/extend_msg.go index 9717bd933..b9395db0e 100644 --- a/pkg/common/db/controller/extend_msg.go +++ b/pkg/common/db/controller/extend_msg.go @@ -11,12 +11,46 @@ import ( // for mongoDB type ExtendMsgDatabase interface { CreateExtendMsgSet(ctx context.Context, set *unRelationTb.ExtendMsgSetModel) error - GetAllExtendMsgSet(ctx context.Context, ID string, opts *unRelationTb.GetAllExtendMsgSetOpts) (sets []*unRelationTb.ExtendMsgSetModel, err error) - GetExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, maxMsgUpdateTime int64) (*unRelationTb.ExtendMsgSetModel, error) - InsertExtendMsg(ctx context.Context, conversationID string, sessionType int32, msg *unRelationTb.ExtendMsgModel) error - InsertOrUpdateReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensionList map[string]*unRelationTb.KeyValueModel) error - DeleteReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensionList map[string]*unRelationTb.KeyValueModel) error - GetExtendMsg(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, maxMsgUpdateTime int64) (extendMsg *unRelationTb.ExtendMsgModel, err error) + GetAllExtendMsgSet( + ctx context.Context, + ID string, + opts *unRelationTb.GetAllExtendMsgSetOpts, + ) (sets []*unRelationTb.ExtendMsgSetModel, err error) + GetExtendMsgSet( + ctx context.Context, + conversationID string, + sessionType int32, + maxMsgUpdateTime int64, + ) (*unRelationTb.ExtendMsgSetModel, error) + InsertExtendMsg( + ctx context.Context, + conversationID string, + sessionType int32, + msg *unRelationTb.ExtendMsgModel, + ) error + InsertOrUpdateReactionExtendMsgSet( + ctx context.Context, + conversationID string, + sessionType int32, + clientMsgID string, + msgFirstModifyTime int64, + reactionExtensionList map[string]*unRelationTb.KeyValueModel, + ) error + DeleteReactionExtendMsgSet( + ctx context.Context, + conversationID string, + sessionType int32, + clientMsgID string, + msgFirstModifyTime int64, + reactionExtensionList map[string]*unRelationTb.KeyValueModel, + ) error + GetExtendMsg( + ctx context.Context, + conversationID string, + sessionType int32, + clientMsgID string, + maxMsgUpdateTime int64, + ) (extendMsg *unRelationTb.ExtendMsgModel, err error) } type extendMsgDatabase struct { @@ -25,7 +59,11 @@ type extendMsgDatabase struct { ctxTx tx.CtxTx } -func NewExtendMsgDatabase(extendMsgModel unRelationTb.ExtendMsgSetModelInterface, cache cache.ExtendMsgSetCache, ctxTx tx.CtxTx) ExtendMsgDatabase { +func NewExtendMsgDatabase( + extendMsgModel unRelationTb.ExtendMsgSetModelInterface, + cache cache.ExtendMsgSetCache, + ctxTx tx.CtxTx, +) ExtendMsgDatabase { return &extendMsgDatabase{database: extendMsgModel, cache: cache, ctxTx: ctxTx} } @@ -33,26 +71,74 @@ func (e *extendMsgDatabase) CreateExtendMsgSet(ctx context.Context, set *unRelat return e.database.CreateExtendMsgSet(ctx, set) } -func (e *extendMsgDatabase) GetAllExtendMsgSet(ctx context.Context, conversationID string, opts *unRelationTb.GetAllExtendMsgSetOpts) (sets []*unRelationTb.ExtendMsgSetModel, err error) { +func (e *extendMsgDatabase) GetAllExtendMsgSet( + ctx context.Context, + conversationID string, + opts *unRelationTb.GetAllExtendMsgSetOpts, +) (sets []*unRelationTb.ExtendMsgSetModel, err error) { return e.database.GetAllExtendMsgSet(ctx, conversationID, opts) } -func (e *extendMsgDatabase) GetExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, maxMsgUpdateTime int64) (*unRelationTb.ExtendMsgSetModel, error) { +func (e *extendMsgDatabase) GetExtendMsgSet( + ctx context.Context, + conversationID string, + sessionType int32, + maxMsgUpdateTime int64, +) (*unRelationTb.ExtendMsgSetModel, error) { return e.database.GetExtendMsgSet(ctx, conversationID, sessionType, maxMsgUpdateTime) } -func (e *extendMsgDatabase) InsertExtendMsg(ctx context.Context, conversationID string, sessionType int32, msg *unRelationTb.ExtendMsgModel) error { +func (e *extendMsgDatabase) InsertExtendMsg( + ctx context.Context, + conversationID string, + sessionType int32, + msg *unRelationTb.ExtendMsgModel, +) error { return e.database.InsertExtendMsg(ctx, conversationID, sessionType, msg) } -func (e *extendMsgDatabase) InsertOrUpdateReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensionList map[string]*unRelationTb.KeyValueModel) error { - return e.database.InsertOrUpdateReactionExtendMsgSet(ctx, conversationID, sessionType, clientMsgID, msgFirstModifyTime, reactionExtensionList) +func (e *extendMsgDatabase) InsertOrUpdateReactionExtendMsgSet( + ctx context.Context, + conversationID string, + sessionType int32, + clientMsgID string, + msgFirstModifyTime int64, + reactionExtensionList map[string]*unRelationTb.KeyValueModel, +) error { + return e.database.InsertOrUpdateReactionExtendMsgSet( + ctx, + conversationID, + sessionType, + clientMsgID, + msgFirstModifyTime, + reactionExtensionList, + ) } -func (e *extendMsgDatabase) DeleteReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensionList map[string]*unRelationTb.KeyValueModel) error { - return e.database.DeleteReactionExtendMsgSet(ctx, conversationID, sessionType, clientMsgID, msgFirstModifyTime, reactionExtensionList) +func (e *extendMsgDatabase) DeleteReactionExtendMsgSet( + ctx context.Context, + conversationID string, + sessionType int32, + clientMsgID string, + msgFirstModifyTime int64, + reactionExtensionList map[string]*unRelationTb.KeyValueModel, +) error { + return e.database.DeleteReactionExtendMsgSet( + ctx, + conversationID, + sessionType, + clientMsgID, + msgFirstModifyTime, + reactionExtensionList, + ) } -func (e *extendMsgDatabase) GetExtendMsg(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, maxMsgUpdateTime int64) (extendMsg *unRelationTb.ExtendMsgModel, err error) { +func (e *extendMsgDatabase) GetExtendMsg( + ctx context.Context, + conversationID string, + sessionType int32, + clientMsgID string, + maxMsgUpdateTime int64, +) (extendMsg *unRelationTb.ExtendMsgModel, err error) { return e.cache.GetExtendMsg(ctx, conversationID, sessionType, clientMsgID, maxMsgUpdateTime) } diff --git a/pkg/common/db/controller/friend.go b/pkg/common/db/controller/friend.go index 90985c9ad..3a98bd8ec 100644 --- a/pkg/common/db/controller/friend.go +++ b/pkg/common/db/controller/friend.go @@ -4,6 +4,8 @@ import ( "context" "time" + "gorm.io/gorm" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" @@ -11,7 +13,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "gorm.io/gorm" ) type FriendDatabase interface { @@ -30,15 +31,35 @@ type FriendDatabase interface { // 更新好友备注 UpdateRemark(ctx context.Context, ownerUserID, friendUserID, remark string) (err error) // 获取ownerUserID的好友列表 - PageOwnerFriends(ctx context.Context, ownerUserID string, pageNumber, showNumber int32) (friends []*relation.FriendModel, total int64, err error) + PageOwnerFriends( + ctx context.Context, + ownerUserID string, + pageNumber, showNumber int32, + ) (friends []*relation.FriendModel, total int64, err error) // friendUserID在哪些人的好友列表中 - PageInWhoseFriends(ctx context.Context, friendUserID string, pageNumber, showNumber int32) (friends []*relation.FriendModel, total int64, err error) + PageInWhoseFriends( + ctx context.Context, + friendUserID string, + pageNumber, showNumber int32, + ) (friends []*relation.FriendModel, total int64, err error) // 获取我发出去的好友申请 - PageFriendRequestFromMe(ctx context.Context, userID string, pageNumber, showNumber int32) (friends []*relation.FriendRequestModel, total int64, err error) + PageFriendRequestFromMe( + ctx context.Context, + userID string, + pageNumber, showNumber int32, + ) (friends []*relation.FriendRequestModel, total int64, err error) // 获取我收到的的好友申请 - PageFriendRequestToMe(ctx context.Context, userID string, pageNumber, showNumber int32) (friends []*relation.FriendRequestModel, total int64, err error) + PageFriendRequestToMe( + ctx context.Context, + userID string, + pageNumber, showNumber int32, + ) (friends []*relation.FriendRequestModel, total int64, err error) // 获取某人指定好友的信息 - FindFriendsWithError(ctx context.Context, ownerUserID string, friendUserIDs []string) (friends []*relation.FriendModel, err error) + FindFriendsWithError( + ctx context.Context, + ownerUserID string, + friendUserIDs []string, + ) (friends []*relation.FriendModel, err error) FindFriendUserIDs(ctx context.Context, ownerUserID string) (friendUserIDs []string, err error) } @@ -49,12 +70,20 @@ type friendDatabase struct { cache cache.FriendCache } -func NewFriendDatabase(friend relation.FriendModelInterface, friendRequest relation.FriendRequestModelInterface, cache cache.FriendCache, tx tx.Tx) FriendDatabase { +func NewFriendDatabase( + friend relation.FriendModelInterface, + friendRequest relation.FriendRequestModelInterface, + cache cache.FriendCache, + tx tx.Tx, +) FriendDatabase { return &friendDatabase{friend: friend, friendRequest: friendRequest, cache: cache, tx: tx} } // ok 检查user2是否在user1的好友列表中(inUser1Friends==true) 检查user1是否在user2的好友列表中(inUser2Friends==true) -func (f *friendDatabase) CheckIn(ctx context.Context, userID1, userID2 string) (inUser1Friends bool, inUser2Friends bool, err error) { +func (f *friendDatabase) CheckIn( + ctx context.Context, + userID1, userID2 string, +) (inUser1Friends bool, inUser2Friends bool, err error) { userID1FriendIDs, err := f.cache.GetFriendIDs(ctx, userID1) if err != nil { return @@ -67,7 +96,12 @@ func (f *friendDatabase) CheckIn(ctx context.Context, userID1, userID2 string) ( } // 增加或者更新好友申请 如果之前有记录则更新,没有记录则新增 -func (f *friendDatabase) AddFriendRequest(ctx context.Context, fromUserID, toUserID string, reqMsg string, ex string) (err error) { +func (f *friendDatabase) AddFriendRequest( + ctx context.Context, + fromUserID, toUserID string, + reqMsg string, + ex string, +) (err error) { return f.tx.Transaction(func(tx any) error { _, err := f.friendRequest.NewTx(tx).Take(ctx, fromUserID, toUserID) //有db错误 @@ -96,7 +130,12 @@ func (f *friendDatabase) AddFriendRequest(ctx context.Context, fromUserID, toUse } // (1)先判断是否在好友表 (在不在都不返回错误) (2)对于不在好友列表的 插入即可 -func (f *friendDatabase) BecomeFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, addSource int32) (err error) { +func (f *friendDatabase) BecomeFriends( + ctx context.Context, + ownerUserID string, + friendUserIDs []string, + addSource int32, +) (err error) { cache := f.cache.NewCache() if err := f.tx.Transaction(func(tx any) error { //先find 找出重复的 去掉重复的 @@ -142,7 +181,10 @@ func (f *friendDatabase) BecomeFriends(ctx context.Context, ownerUserID string, } // 拒绝好友申请 (1)检查是否有申请记录且为未处理状态 (没有记录返回错误) (2)修改申请记录 已拒绝 -func (f *friendDatabase) RefuseFriendRequest(ctx context.Context, friendRequest *relation.FriendRequestModel) (err error) { +func (f *friendDatabase) RefuseFriendRequest( + ctx context.Context, + friendRequest *relation.FriendRequestModel, +) (err error) { fr, err := f.friendRequest.Take(ctx, friendRequest.FromUserID, friendRequest.ToUserID) if err != nil { return err @@ -160,7 +202,10 @@ func (f *friendDatabase) RefuseFriendRequest(ctx context.Context, friendRequest } // AgreeFriendRequest 同意好友申请 (1)检查是否有申请记录且为未处理状态 (没有记录返回错误) (2)检查是否好友(不返回错误) (3) 建立双向好友关系(存在的忽略) -func (f *friendDatabase) AgreeFriendRequest(ctx context.Context, friendRequest *relation.FriendRequestModel) (err error) { +func (f *friendDatabase) AgreeFriendRequest( + ctx context.Context, + friendRequest *relation.FriendRequestModel, +) (err error) { return f.tx.Transaction(func(tx any) error { fr, err := f.friendRequest.NewTx(tx).Take(ctx, friendRequest.FromUserID, friendRequest.ToUserID) if err != nil { @@ -185,10 +230,26 @@ func (f *friendDatabase) AgreeFriendRequest(ctx context.Context, friendRequest * })) var adds []*relation.FriendModel if _, ok := existsMap[[...]string{friendRequest.ToUserID, friendRequest.FromUserID}]; !ok { // 自己 - 好友 - adds = append(adds, &relation.FriendModel{OwnerUserID: friendRequest.ToUserID, FriendUserID: friendRequest.FromUserID, AddSource: int32(constant.BecomeFriendByApply), OperatorUserID: friendRequest.FromUserID}) + adds = append( + adds, + &relation.FriendModel{ + OwnerUserID: friendRequest.ToUserID, + FriendUserID: friendRequest.FromUserID, + AddSource: int32(constant.BecomeFriendByApply), + OperatorUserID: friendRequest.FromUserID, + }, + ) } if _, ok := existsMap[[...]string{friendRequest.FromUserID, friendRequest.ToUserID}]; !ok { // 好友 - 自己 - adds = append(adds, &relation.FriendModel{OwnerUserID: friendRequest.FromUserID, FriendUserID: friendRequest.ToUserID, AddSource: int32(constant.BecomeFriendByApply), OperatorUserID: friendRequest.FromUserID}) + adds = append( + adds, + &relation.FriendModel{ + OwnerUserID: friendRequest.FromUserID, + FriendUserID: friendRequest.ToUserID, + AddSource: int32(constant.BecomeFriendByApply), + OperatorUserID: friendRequest.FromUserID, + }, + ) } if len(adds) > 0 { if err := f.friend.NewTx(tx).Create(ctx, adds); err != nil { @@ -216,27 +277,47 @@ func (f *friendDatabase) UpdateRemark(ctx context.Context, ownerUserID, friendUs } // 获取ownerUserID的好友列表 无结果不返回错误 -func (f *friendDatabase) PageOwnerFriends(ctx context.Context, ownerUserID string, pageNumber, showNumber int32) (friends []*relation.FriendModel, total int64, err error) { +func (f *friendDatabase) PageOwnerFriends( + ctx context.Context, + ownerUserID string, + pageNumber, showNumber int32, +) (friends []*relation.FriendModel, total int64, err error) { return f.friend.FindOwnerFriends(ctx, ownerUserID, pageNumber, showNumber) } // friendUserID在哪些人的好友列表中 -func (f *friendDatabase) PageInWhoseFriends(ctx context.Context, friendUserID string, pageNumber, showNumber int32) (friends []*relation.FriendModel, total int64, err error) { +func (f *friendDatabase) PageInWhoseFriends( + ctx context.Context, + friendUserID string, + pageNumber, showNumber int32, +) (friends []*relation.FriendModel, total int64, err error) { return f.friend.FindInWhoseFriends(ctx, friendUserID, pageNumber, showNumber) } // 获取我发出去的好友申请 无结果不返回错误 -func (f *friendDatabase) PageFriendRequestFromMe(ctx context.Context, userID string, pageNumber, showNumber int32) (friends []*relation.FriendRequestModel, total int64, err error) { +func (f *friendDatabase) PageFriendRequestFromMe( + ctx context.Context, + userID string, + pageNumber, showNumber int32, +) (friends []*relation.FriendRequestModel, total int64, err error) { return f.friendRequest.FindFromUserID(ctx, userID, pageNumber, showNumber) } // 获取我收到的的好友申请 无结果不返回错误 -func (f *friendDatabase) PageFriendRequestToMe(ctx context.Context, userID string, pageNumber, showNumber int32) (friends []*relation.FriendRequestModel, total int64, err error) { +func (f *friendDatabase) PageFriendRequestToMe( + ctx context.Context, + userID string, + pageNumber, showNumber int32, +) (friends []*relation.FriendRequestModel, total int64, err error) { return f.friendRequest.FindToUserID(ctx, userID, pageNumber, showNumber) } // 获取某人指定好友的信息 如果有好友不存在,也返回错误 -func (f *friendDatabase) FindFriendsWithError(ctx context.Context, ownerUserID string, friendUserIDs []string) (friends []*relation.FriendModel, err error) { +func (f *friendDatabase) FindFriendsWithError( + ctx context.Context, + ownerUserID string, + friendUserIDs []string, +) (friends []*relation.FriendModel, err error) { friends, err = f.friend.FindFriends(ctx, ownerUserID, friendUserIDs) if err != nil { return @@ -247,6 +328,9 @@ func (f *friendDatabase) FindFriendsWithError(ctx context.Context, ownerUserID s return } -func (f *friendDatabase) FindFriendUserIDs(ctx context.Context, ownerUserID string) (friendUserIDs []string, err error) { +func (f *friendDatabase) FindFriendUserIDs( + ctx context.Context, + ownerUserID string, +) (friendUserIDs []string, err error) { return f.cache.GetFriendIDs(ctx, ownerUserID) } diff --git a/pkg/common/db/controller/group.go b/pkg/common/db/controller/group.go index 19ae3e87e..58ee9394b 100644 --- a/pkg/common/db/controller/group.go +++ b/pkg/common/db/controller/group.go @@ -4,6 +4,11 @@ import ( "context" "fmt" + "github.com/dtm-labs/rockscache" + "github.com/redis/go-redis/v9" + "go.mongodb.org/mongo-driver/mongo" + "gorm.io/gorm" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/relation" @@ -12,10 +17,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/tx" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/unrelation" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "github.com/dtm-labs/rockscache" - "github.com/redis/go-redis/v9" - "go.mongodb.org/mongo-driver/mongo" - "gorm.io/gorm" ) type GroupDatabase interface { @@ -23,33 +24,82 @@ type GroupDatabase interface { CreateGroup(ctx context.Context, groups []*relationTb.GroupModel, groupMembers []*relationTb.GroupMemberModel) error TakeGroup(ctx context.Context, groupID string) (group *relationTb.GroupModel, err error) FindGroup(ctx context.Context, groupIDs []string) (groups []*relationTb.GroupModel, err error) - SearchGroup(ctx context.Context, keyword string, pageNumber, showNumber int32) (uint32, []*relationTb.GroupModel, error) + SearchGroup( + ctx context.Context, + keyword string, + pageNumber, showNumber int32, + ) (uint32, []*relationTb.GroupModel, error) UpdateGroup(ctx context.Context, groupID string, data map[string]any) error DismissGroup(ctx context.Context, groupID string, deleteMember bool) error // 解散群,并删除群成员 GetGroupIDsByGroupType(ctx context.Context, groupType int) (groupIDs []string, err error) // GroupMember - TakeGroupMember(ctx context.Context, groupID string, userID string) (groupMember *relationTb.GroupMemberModel, err error) + TakeGroupMember( + ctx context.Context, + groupID string, + userID string, + ) (groupMember *relationTb.GroupMemberModel, err error) TakeGroupOwner(ctx context.Context, groupID string) (*relationTb.GroupMemberModel, error) - FindGroupMember(ctx context.Context, groupIDs []string, userIDs []string, roleLevels []int32) ([]*relationTb.GroupMemberModel, error) + FindGroupMember( + ctx context.Context, + groupIDs []string, + userIDs []string, + roleLevels []int32, + ) ([]*relationTb.GroupMemberModel, error) FindGroupMemberUserID(ctx context.Context, groupID string) ([]string, error) FindGroupMemberNum(ctx context.Context, groupID string) (uint32, error) FindUserManagedGroupID(ctx context.Context, userID string) (groupIDs []string, err error) - PageGroupRequest(ctx context.Context, groupIDs []string, pageNumber, showNumber int32) (uint32, []*relationTb.GroupRequestModel, error) - //PageGroupMember(ctx context.Context, groupIDs []string, userIDs []string, roleLevels []int32, pageNumber, showNumber int32) (uint32, []*relationTb.GroupMemberModel, error) - PageGetJoinGroup(ctx context.Context, userID string, pageNumber, showNumber int32) (total uint32, totalGroupMembers []*relationTb.GroupMemberModel, err error) - PageGetGroupMember(ctx context.Context, groupID string, pageNumber, showNumber int32) (total uint32, totalGroupMembers []*relationTb.GroupMemberModel, err error) - SearchGroupMember(ctx context.Context, keyword string, groupIDs []string, userIDs []string, roleLevels []int32, pageNumber, showNumber int32) (uint32, []*relationTb.GroupMemberModel, error) - HandlerGroupRequest(ctx context.Context, groupID string, userID string, handledMsg string, handleResult int32, member *relationTb.GroupMemberModel) error + PageGroupRequest( + ctx context.Context, + groupIDs []string, + pageNumber, showNumber int32, + ) (uint32, []*relationTb.GroupRequestModel, error) + // PageGroupMember(ctx context.Context, groupIDs []string, userIDs []string, roleLevels []int32, pageNumber, + // showNumber int32) (uint32, []*relationTb.GroupMemberModel, error) + PageGetJoinGroup( + ctx context.Context, + userID string, + pageNumber, showNumber int32, + ) (total uint32, totalGroupMembers []*relationTb.GroupMemberModel, err error) + PageGetGroupMember( + ctx context.Context, + groupID string, + pageNumber, showNumber int32, + ) (total uint32, totalGroupMembers []*relationTb.GroupMemberModel, err error) + SearchGroupMember( + ctx context.Context, + keyword string, + groupIDs []string, + userIDs []string, + roleLevels []int32, + pageNumber, showNumber int32, + ) (uint32, []*relationTb.GroupMemberModel, error) + HandlerGroupRequest( + ctx context.Context, + groupID string, + userID string, + handledMsg string, + handleResult int32, + member *relationTb.GroupMemberModel, + ) error DeleteGroupMember(ctx context.Context, groupID string, userIDs []string) error MapGroupMemberUserID(ctx context.Context, groupIDs []string) (map[string]*relationTb.GroupSimpleUserID, error) MapGroupMemberNum(ctx context.Context, groupIDs []string) (map[string]uint32, error) - TransferGroupOwner(ctx context.Context, groupID string, oldOwnerUserID, newOwnerUserID string, roleLevel int32) error // 转让群 + TransferGroupOwner( + ctx context.Context, + groupID string, + oldOwnerUserID, newOwnerUserID string, + roleLevel int32, + ) error // 转让群 UpdateGroupMember(ctx context.Context, groupID string, userID string, data map[string]any) error UpdateGroupMembers(ctx context.Context, data []*relationTb.BatchUpdateGroupMember) error // GroupRequest CreateGroupRequest(ctx context.Context, requests []*relationTb.GroupRequestModel) error TakeGroupRequest(ctx context.Context, groupID string, userID string) (*relationTb.GroupRequestModel, error) - PageGroupRequestUser(ctx context.Context, userID string, pageNumber, showNumber int32) (uint32, []*relationTb.GroupRequestModel, error) + PageGroupRequestUser( + ctx context.Context, + userID string, + pageNumber, showNumber int32, + ) (uint32, []*relationTb.GroupRequestModel, error) // SuperGroupModelInterface FindSuperGroup(ctx context.Context, groupIDs []string) ([]*unRelationTb.SuperGroupModel, error) FindJoinSuperGroup(ctx context.Context, userID string) ([]string, error) @@ -91,7 +141,14 @@ func InitGroupDatabase(db *gorm.DB, rdb redis.UniversalClient, database *mongo.D tx.NewGorm(db), tx.NewMongo(database.Client()), unrelation.NewSuperGroupMongoDriver(database), - cache.NewGroupCacheRedis(rdb, relation.NewGroupDB(db), relation.NewGroupMemberDB(db), relation.NewGroupRequest(db), unrelation.NewSuperGroupMongoDriver(database), rcOptions), + cache.NewGroupCacheRedis( + rdb, + relation.NewGroupDB(db), + relation.NewGroupMemberDB(db), + relation.NewGroupRequest(db), + unrelation.NewSuperGroupMongoDriver(database), + rcOptions, + ), ) } @@ -121,7 +178,11 @@ func (g *groupDatabase) FindGroupMemberNum(ctx context.Context, groupID string) return uint32(num), nil } -func (g *groupDatabase) CreateGroup(ctx context.Context, groups []*relationTb.GroupModel, groupMembers []*relationTb.GroupMemberModel) error { +func (g *groupDatabase) CreateGroup( + ctx context.Context, + groups []*relationTb.GroupModel, + groupMembers []*relationTb.GroupMemberModel, +) error { var cache = g.cache.NewCache() if err := g.tx.Transaction(func(tx any) error { if len(groups) > 0 { @@ -162,7 +223,11 @@ func (g *groupDatabase) FindGroup(ctx context.Context, groupIDs []string) (group return g.cache.GetGroupsInfo(ctx, groupIDs) } -func (g *groupDatabase) SearchGroup(ctx context.Context, keyword string, pageNumber, showNumber int32) (uint32, []*relationTb.GroupModel, error) { +func (g *groupDatabase) SearchGroup( + ctx context.Context, + keyword string, + pageNumber, showNumber int32, +) (uint32, []*relationTb.GroupModel, error) { return g.groupDB.Search(ctx, keyword, pageNumber, showNumber) } @@ -197,7 +262,11 @@ func (g *groupDatabase) DismissGroup(ctx context.Context, groupID string, delete return cache.ExecDel(ctx) } -func (g *groupDatabase) TakeGroupMember(ctx context.Context, groupID string, userID string) (groupMember *relationTb.GroupMemberModel, err error) { +func (g *groupDatabase) TakeGroupMember( + ctx context.Context, + groupID string, + userID string, +) (groupMember *relationTb.GroupMemberModel, err error) { return g.cache.GetGroupMemberInfo(ctx, groupID, userID) } @@ -209,11 +278,20 @@ func (g *groupDatabase) FindUserManagedGroupID(ctx context.Context, userID strin return g.groupMemberDB.FindUserManagedGroupID(ctx, userID) } -func (g *groupDatabase) PageGroupRequest(ctx context.Context, groupIDs []string, pageNumber, showNumber int32) (uint32, []*relationTb.GroupRequestModel, error) { +func (g *groupDatabase) PageGroupRequest( + ctx context.Context, + groupIDs []string, + pageNumber, showNumber int32, +) (uint32, []*relationTb.GroupRequestModel, error) { return g.groupRequestDB.PageGroup(ctx, groupIDs, pageNumber, showNumber) } -func (g *groupDatabase) FindGroupMember(ctx context.Context, groupIDs []string, userIDs []string, roleLevels []int32) (totalGroupMembers []*relationTb.GroupMemberModel, err error) { +func (g *groupDatabase) FindGroupMember( + ctx context.Context, + groupIDs []string, + userIDs []string, + roleLevels []int32, +) (totalGroupMembers []*relationTb.GroupMemberModel, err error) { if roleLevels == nil { for _, groupID := range groupIDs { groupMembers, err := g.cache.GetGroupMembersInfo(ctx, groupID, userIDs) @@ -227,7 +305,11 @@ func (g *groupDatabase) FindGroupMember(ctx context.Context, groupIDs []string, return g.groupMemberDB.Find(ctx, groupIDs, userIDs, roleLevels) } -func (g *groupDatabase) PageGetJoinGroup(ctx context.Context, userID string, pageNumber, showNumber int32) (total uint32, totalGroupMembers []*relationTb.GroupMemberModel, err error) { +func (g *groupDatabase) PageGetJoinGroup( + ctx context.Context, + userID string, + pageNumber, showNumber int32, +) (total uint32, totalGroupMembers []*relationTb.GroupMemberModel, err error) { groupIDs, err := g.cache.GetJoinedGroupIDs(ctx, userID) if err != nil { return 0, nil, err @@ -242,7 +324,11 @@ func (g *groupDatabase) PageGetJoinGroup(ctx context.Context, userID string, pag return uint32(len(groupIDs)), totalGroupMembers, nil } -func (g *groupDatabase) PageGetGroupMember(ctx context.Context, groupID string, pageNumber, showNumber int32) (total uint32, totalGroupMembers []*relationTb.GroupMemberModel, err error) { +func (g *groupDatabase) PageGetGroupMember( + ctx context.Context, + groupID string, + pageNumber, showNumber int32, +) (total uint32, totalGroupMembers []*relationTb.GroupMemberModel, err error) { groupMemberIDs, err := g.cache.GetGroupMemberIDs(ctx, groupID) if err != nil { return 0, nil, err @@ -258,11 +344,25 @@ func (g *groupDatabase) PageGetGroupMember(ctx context.Context, groupID string, return uint32(len(groupMemberIDs)), members, nil } -func (g *groupDatabase) SearchGroupMember(ctx context.Context, keyword string, groupIDs []string, userIDs []string, roleLevels []int32, pageNumber, showNumber int32) (uint32, []*relationTb.GroupMemberModel, error) { +func (g *groupDatabase) SearchGroupMember( + ctx context.Context, + keyword string, + groupIDs []string, + userIDs []string, + roleLevels []int32, + pageNumber, showNumber int32, +) (uint32, []*relationTb.GroupMemberModel, error) { return g.groupMemberDB.SearchMember(ctx, keyword, groupIDs, userIDs, roleLevels, pageNumber, showNumber) } -func (g *groupDatabase) HandlerGroupRequest(ctx context.Context, groupID string, userID string, handledMsg string, handleResult int32, member *relationTb.GroupMemberModel) error { +func (g *groupDatabase) HandlerGroupRequest( + ctx context.Context, + groupID string, + userID string, + handledMsg string, + handleResult int32, + member *relationTb.GroupMemberModel, +) error { cache := g.cache.NewCache() if err := g.tx.Transaction(func(tx any) error { if err := g.groupRequestDB.NewTx(tx).UpdateHandler(ctx, groupID, userID, handledMsg, handleResult); err != nil { @@ -285,10 +385,18 @@ func (g *groupDatabase) DeleteGroupMember(ctx context.Context, groupID string, u if err := g.groupMemberDB.Delete(ctx, groupID, userIDs); err != nil { return err } - return g.cache.DelGroupMembersHash(groupID).DelGroupMemberIDs(groupID).DelGroupsMemberNum(groupID).DelJoinedGroupID(userIDs...).DelGroupMembersInfo(groupID, userIDs...).ExecDel(ctx) -} - -func (g *groupDatabase) MapGroupMemberUserID(ctx context.Context, groupIDs []string) (map[string]*relationTb.GroupSimpleUserID, error) { + return g.cache.DelGroupMembersHash(groupID). + DelGroupMemberIDs(groupID). + DelGroupsMemberNum(groupID). + DelJoinedGroupID(userIDs...). + DelGroupMembersInfo(groupID, userIDs...). + ExecDel(ctx) +} + +func (g *groupDatabase) MapGroupMemberUserID( + ctx context.Context, + groupIDs []string, +) (map[string]*relationTb.GroupSimpleUserID, error) { return g.cache.GetGroupMemberHashMap(ctx, groupIDs) } @@ -304,7 +412,12 @@ func (g *groupDatabase) MapGroupMemberNum(ctx context.Context, groupIDs []string return m, nil } -func (g *groupDatabase) TransferGroupOwner(ctx context.Context, groupID string, oldOwnerUserID, newOwnerUserID string, roleLevel int32) error { +func (g *groupDatabase) TransferGroupOwner( + ctx context.Context, + groupID string, + oldOwnerUserID, newOwnerUserID string, + roleLevel int32, +) error { if err := g.tx.Transaction(func(tx any) error { rowsAffected, err := g.groupMemberDB.NewTx(tx).UpdateRoleLevel(ctx, groupID, oldOwnerUserID, roleLevel) if err != nil { @@ -327,7 +440,12 @@ func (g *groupDatabase) TransferGroupOwner(ctx context.Context, groupID string, return g.cache.DelGroupMembersInfo(groupID, oldOwnerUserID, newOwnerUserID).ExecDel(ctx) } -func (g *groupDatabase) UpdateGroupMember(ctx context.Context, groupID string, userID string, data map[string]any) error { +func (g *groupDatabase) UpdateGroupMember( + ctx context.Context, + groupID string, + userID string, + data map[string]any, +) error { if err := g.groupMemberDB.Update(ctx, groupID, userID, data); err != nil { return err } @@ -362,15 +480,26 @@ func (g *groupDatabase) CreateGroupRequest(ctx context.Context, requests []*rela }) } -func (g *groupDatabase) TakeGroupRequest(ctx context.Context, groupID string, userID string) (*relationTb.GroupRequestModel, error) { +func (g *groupDatabase) TakeGroupRequest( + ctx context.Context, + groupID string, + userID string, +) (*relationTb.GroupRequestModel, error) { return g.groupRequestDB.Take(ctx, groupID, userID) } -func (g *groupDatabase) PageGroupRequestUser(ctx context.Context, userID string, pageNumber, showNumber int32) (uint32, []*relationTb.GroupRequestModel, error) { +func (g *groupDatabase) PageGroupRequestUser( + ctx context.Context, + userID string, + pageNumber, showNumber int32, +) (uint32, []*relationTb.GroupRequestModel, error) { return g.groupRequestDB.Page(ctx, userID, pageNumber, showNumber) } -func (g *groupDatabase) FindSuperGroup(ctx context.Context, groupIDs []string) (models []*unRelationTb.SuperGroupModel, err error) { +func (g *groupDatabase) FindSuperGroup( + ctx context.Context, + groupIDs []string, +) (models []*unRelationTb.SuperGroupModel, err error) { return g.cache.GetSuperGroupMemberIDs(ctx, groupIDs...) } diff --git a/pkg/common/db/controller/msg.go b/pkg/common/db/controller/msg.go index dd62e0e2e..6ce55deae 100644 --- a/pkg/common/db/controller/msg.go +++ b/pkg/common/db/controller/msg.go @@ -19,10 +19,11 @@ import ( "context" "errors" + "go.mongodb.org/mongo-driver/mongo" + pbMsg "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "go.mongodb.org/mongo-driver/mongo" ) const ( @@ -41,12 +42,26 @@ type CommonMsgDatabase interface { DeleteMessagesFromCache(ctx context.Context, conversationID string, seqs []int64) error DelUserDeleteMsgsList(ctx context.Context, conversationID string, seqs []int64) // incrSeq然后批量插入缓存 - BatchInsertChat2Cache(ctx context.Context, conversationID string, msgs []*sdkws.MsgData) (seq int64, isNewConversation bool, err error) + BatchInsertChat2Cache( + ctx context.Context, + conversationID string, + msgs []*sdkws.MsgData, + ) (seq int64, isNewConversation bool, err error) // 通过seqList获取mongo中写扩散消息 - GetMsgBySeqsRange(ctx context.Context, userID string, conversationID string, begin, end, num, userMaxSeq int64) (minSeq int64, maxSeq int64, seqMsg []*sdkws.MsgData, err error) + GetMsgBySeqsRange( + ctx context.Context, + userID string, + conversationID string, + begin, end, num, userMaxSeq int64, + ) (minSeq int64, maxSeq int64, seqMsg []*sdkws.MsgData, err error) // 通过seqList获取大群在 mongo里面的消息 - GetMsgBySeqs(ctx context.Context, userID string, conversationID string, seqs []int64) (minSeq int64, maxSeq int64, seqMsg []*sdkws.MsgData, err error) + GetMsgBySeqs( + ctx context.Context, + userID string, + conversationID string, + seqs []int64, + ) (minSeq int64, maxSeq int64, seqMsg []*sdkws.MsgData, err error) // 删除会话消息重置最小seq, remainTime为消息保留的时间单位秒,超时消息删除, 传0删除所有消息(此方法不删除redis cache) DeleteConversationMsgsAndSetMinSeq(ctx context.Context, conversationID string, remainTime int64) error // 用户根据seq删除消息 @@ -73,7 +88,10 @@ type CommonMsgDatabase interface { UserSetHasReadSeqs(ctx context.Context, userID string, hasReadSeqs map[string]int64) error GetMongoMaxAndMinSeq(ctx context.Context, conversationID string) (maxSeq, minSeq int64, err error) - GetConversationMinMaxSeqInMongoAndCache(ctx context.Context, conversationID string) (minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache int64, err error) + GetConversationMinMaxSeqInMongoAndCache( + ctx context.Context, + conversationID string, + ) (minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache int64, err error) SetSendMsgStatus(ctx context.Context, id string, status int32) error GetSendMsgStatus(ctx context.Context, id string) (int32, error) @@ -86,13 +104,38 @@ type CommonMsgDatabase interface { // modify JudgeMessageReactionExist(ctx context.Context, clientMsgID string, sessionType int32) (bool, error) SetMessageTypeKeyValue(ctx context.Context, clientMsgID string, sessionType int32, typeKey, value string) error - SetMessageReactionExpire(ctx context.Context, clientMsgID string, sessionType int32, expiration time.Duration) (bool, error) - GetExtendMsg(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, maxMsgUpdateTime int64) (*pbMsg.ExtendMsg, error) - InsertOrUpdateReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensionList map[string]*sdkws.KeyValue) error + SetMessageReactionExpire( + ctx context.Context, + clientMsgID string, + sessionType int32, + expiration time.Duration, + ) (bool, error) + GetExtendMsg( + ctx context.Context, + conversationID string, + sessionType int32, + clientMsgID string, + maxMsgUpdateTime int64, + ) (*pbMsg.ExtendMsg, error) + InsertOrUpdateReactionExtendMsgSet( + ctx context.Context, + conversationID string, + sessionType int32, + clientMsgID string, + msgFirstModifyTime int64, + reactionExtensionList map[string]*sdkws.KeyValue, + ) error GetMessageTypeKeyValue(ctx context.Context, clientMsgID string, sessionType int32, typeKey string) (string, error) GetOneMessageAllReactionList(ctx context.Context, clientMsgID string, sessionType int32) (map[string]string, error) DeleteOneMessageKey(ctx context.Context, clientMsgID string, sessionType int32, subKey string) error - DeleteReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensionList map[string]*sdkws.KeyValue) error + DeleteReactionExtendMsgSet( + ctx context.Context, + conversationID string, + sessionType int32, + clientMsgID string, + msgFirstModifyTime int64, + reactionExtensionList map[string]*sdkws.KeyValue, + ) error } func NewCommonMsgDatabase(msgDocModel unRelationTb.MsgDocModelInterface, cacheModel cache.MsgModel) CommonMsgDatabase { @@ -130,16 +173,32 @@ func (db *commonMsgDatabase) MsgToMQ(ctx context.Context, key string, msg2mq *sd return err } -func (db *commonMsgDatabase) MsgToModifyMQ(ctx context.Context, key, conversationID string, messages []*sdkws.MsgData) error { +func (db *commonMsgDatabase) MsgToModifyMQ( + ctx context.Context, + key, conversationID string, + messages []*sdkws.MsgData, +) error { if len(messages) > 0 { - _, _, err := db.producerToModify.SendMessage(ctx, key, &pbMsg.MsgDataToModifyByMQ{ConversationID: conversationID, Messages: messages}) + _, _, err := db.producerToModify.SendMessage( + ctx, + key, + &pbMsg.MsgDataToModifyByMQ{ConversationID: conversationID, Messages: messages}, + ) return err } return nil } -func (db *commonMsgDatabase) MsgToPushMQ(ctx context.Context, key, conversationID string, msg2mq *sdkws.MsgData) (int32, int64, error) { - partition, offset, err := db.producerToPush.SendMessage(ctx, key, &pbMsg.PushMsgDataToMQ{MsgData: msg2mq, ConversationID: conversationID}) +func (db *commonMsgDatabase) MsgToPushMQ( + ctx context.Context, + key, conversationID string, + msg2mq *sdkws.MsgData, +) (int32, int64, error) { + partition, offset, err := db.producerToPush.SendMessage( + ctx, + key, + &pbMsg.PushMsgDataToMQ{MsgData: msg2mq, ConversationID: conversationID}, + ) if err != nil { log.ZError(ctx, "MsgToPushMQ", err, "key", key, "msg2mq", msg2mq) return 0, 0, err @@ -147,15 +206,30 @@ func (db *commonMsgDatabase) MsgToPushMQ(ctx context.Context, key, conversationI return partition, offset, nil } -func (db *commonMsgDatabase) MsgToMongoMQ(ctx context.Context, key, conversationID string, messages []*sdkws.MsgData, lastSeq int64) error { +func (db *commonMsgDatabase) MsgToMongoMQ( + ctx context.Context, + key, conversationID string, + messages []*sdkws.MsgData, + lastSeq int64, +) error { if len(messages) > 0 { - _, _, err := db.producerToMongo.SendMessage(ctx, key, &pbMsg.MsgDataToMongoByMQ{LastSeq: lastSeq, ConversationID: conversationID, MsgData: messages}) + _, _, err := db.producerToMongo.SendMessage( + ctx, + key, + &pbMsg.MsgDataToMongoByMQ{LastSeq: lastSeq, ConversationID: conversationID, MsgData: messages}, + ) return err } return nil } -func (db *commonMsgDatabase) BatchInsertBlock(ctx context.Context, conversationID string, fields []any, key int8, firstSeq int64) error { +func (db *commonMsgDatabase) BatchInsertBlock( + ctx context.Context, + conversationID string, + fields []any, + key int8, + firstSeq int64, +) error { if len(fields) == 0 { return nil } @@ -256,7 +330,12 @@ func (db *commonMsgDatabase) BatchInsertBlock(ctx context.Context, conversationI return nil } -func (db *commonMsgDatabase) BatchInsertChat2DB(ctx context.Context, conversationID string, msgList []*sdkws.MsgData, currentMaxSeq int64) error { +func (db *commonMsgDatabase) BatchInsertChat2DB( + ctx context.Context, + conversationID string, + msgList []*sdkws.MsgData, + currentMaxSeq int64, +) error { if len(msgList) == 0 { return errs.ErrArgs.Wrap("msgList is empty") } @@ -302,11 +381,21 @@ func (db *commonMsgDatabase) BatchInsertChat2DB(ctx context.Context, conversatio return db.BatchInsertBlock(ctx, conversationID, msgs, updateKeyMsg, msgList[0].Seq) } -func (db *commonMsgDatabase) RevokeMsg(ctx context.Context, conversationID string, seq int64, revoke *unRelationTb.RevokeModel) error { +func (db *commonMsgDatabase) RevokeMsg( + ctx context.Context, + conversationID string, + seq int64, + revoke *unRelationTb.RevokeModel, +) error { return db.BatchInsertBlock(ctx, conversationID, []any{revoke}, updateKeyRevoke, seq) } -func (db *commonMsgDatabase) MarkSingleChatMsgsAsRead(ctx context.Context, userID string, conversationID string, totalSeqs []int64) error { +func (db *commonMsgDatabase) MarkSingleChatMsgsAsRead( + ctx context.Context, + userID string, + conversationID string, + totalSeqs []int64, +) error { for docID, seqs := range db.msg.GetDocIDSeqsMap(conversationID, totalSeqs) { var indexes []int64 for _, seq := range seqs { @@ -329,7 +418,11 @@ func (db *commonMsgDatabase) DelUserDeleteMsgsList(ctx context.Context, conversa db.cache.DelUserDeleteMsgsList(ctx, conversationID, seqs) } -func (db *commonMsgDatabase) BatchInsertChat2Cache(ctx context.Context, conversationID string, msgs []*sdkws.MsgData) (seq int64, isNew bool, err error) { +func (db *commonMsgDatabase) BatchInsertChat2Cache( + ctx context.Context, + conversationID string, + msgs []*sdkws.MsgData, +) (seq int64, isNew bool, err error) { currentMaxSeq, err := db.cache.GetMaxSeq(ctx, conversationID) if err != nil && errs.Unwrap(err) != redis.Nil { prome.Inc(prome.SeqGetFailedCounter) @@ -376,7 +469,11 @@ func (db *commonMsgDatabase) BatchInsertChat2Cache(ctx context.Context, conversa return lastMaxSeq, isNew, utils.Wrap(err, "") } -func (db *commonMsgDatabase) getMsgBySeqs(ctx context.Context, userID, conversationID string, seqs []int64) (totalMsgs []*sdkws.MsgData, err error) { +func (db *commonMsgDatabase) getMsgBySeqs( + ctx context.Context, + userID, conversationID string, + seqs []int64, +) (totalMsgs []*sdkws.MsgData, err error) { for docID, seqs := range db.msg.GetDocIDSeqsMap(conversationID, seqs) { //log.ZDebug(ctx, "getMsgBySeqs", "docID", docID, "seqs", seqs) msgs, err := db.findMsgInfoBySeq(ctx, userID, docID, seqs) @@ -390,7 +487,8 @@ func (db *commonMsgDatabase) getMsgBySeqs(ctx context.Context, userID, conversat return totalMsgs, nil } -// func (db *commonMsgDatabase) refetchDelSeqsMsgs(ctx context.Context, conversationID string, delNums, rangeBegin, begin int64) (seqMsgs []*unRelationTb.MsgDataModel, err error) { +// func (db *commonMsgDatabase) refetchDelSeqsMsgs(ctx context.Context, conversationID string, delNums, rangeBegin, +// begin int64) (seqMsgs []*unRelationTb.MsgDataModel, err error) { // var reFetchSeqs []int64 // if delNums > 0 { // newBeginSeq := rangeBegin - delNums @@ -428,7 +526,11 @@ func (db *commonMsgDatabase) getMsgBySeqs(ctx context.Context, userID, conversat // return seqMsgs, nil // } -func (db *commonMsgDatabase) findMsgInfoBySeq(ctx context.Context, userID, docID string, seqs []int64) (totalMsgs []*unRelationTb.MsgInfoModel, err error) { +func (db *commonMsgDatabase) findMsgInfoBySeq( + ctx context.Context, + userID, docID string, + seqs []int64, +) (totalMsgs []*unRelationTb.MsgInfoModel, err error) { msgs, err := db.msgDocDatabase.GetMsgBySeqIndexIn1Doc(ctx, userID, docID, seqs) for _, msg := range msgs { if msg.IsRead { @@ -438,8 +540,25 @@ func (db *commonMsgDatabase) findMsgInfoBySeq(ctx context.Context, userID, docID return msgs, err } -func (db *commonMsgDatabase) getMsgBySeqsRange(ctx context.Context, userID string, conversationID string, allSeqs []int64, begin, end int64) (seqMsgs []*sdkws.MsgData, err error) { - log.ZDebug(ctx, "getMsgBySeqsRange", "conversationID", conversationID, "allSeqs", allSeqs, "begin", begin, "end", end) +func (db *commonMsgDatabase) getMsgBySeqsRange( + ctx context.Context, + userID string, + conversationID string, + allSeqs []int64, + begin, end int64, +) (seqMsgs []*sdkws.MsgData, err error) { + log.ZDebug( + ctx, + "getMsgBySeqsRange", + "conversationID", + conversationID, + "allSeqs", + allSeqs, + "begin", + begin, + "end", + end, + ) for docID, seqs := range db.msg.GetDocIDSeqsMap(conversationID, allSeqs) { log.ZDebug(ctx, "getMsgBySeqsRange", "docID", docID, "seqs", seqs) msgs, err := db.findMsgInfoBySeq(ctx, userID, docID, seqs) @@ -456,7 +575,12 @@ func (db *commonMsgDatabase) getMsgBySeqsRange(ctx context.Context, userID strin return seqMsgs, nil } -func (db *commonMsgDatabase) GetMsgBySeqsRange(ctx context.Context, userID string, conversationID string, begin, end, num, userMaxSeq int64) (int64, int64, []*sdkws.MsgData, error) { +func (db *commonMsgDatabase) GetMsgBySeqsRange( + ctx context.Context, + userID string, + conversationID string, + begin, end, num, userMaxSeq int64, +) (int64, int64, []*sdkws.MsgData, error) { userMinSeq, err := db.cache.GetConversationUserMinSeq(ctx, conversationID, userID) if err != nil && errs.Unwrap(err) != redis.Nil { return 0, 0, nil, err @@ -476,7 +600,18 @@ func (db *commonMsgDatabase) GetMsgBySeqsRange(ctx context.Context, userID strin if err != nil && errs.Unwrap(err) != redis.Nil { return 0, 0, nil, err } - log.ZDebug(ctx, "GetMsgBySeqsRange", "userMinSeq", userMinSeq, "conMinSeq", minSeq, "conMaxSeq", maxSeq, "userMaxSeq", userMaxSeq) + log.ZDebug( + ctx, + "GetMsgBySeqsRange", + "userMinSeq", + userMinSeq, + "conMinSeq", + minSeq, + "conMaxSeq", + maxSeq, + "userMaxSeq", + userMaxSeq, + ) if userMaxSeq != 0 { if userMaxSeq < maxSeq { maxSeq = userMaxSeq @@ -526,7 +661,18 @@ func (db *commonMsgDatabase) GetMsgBySeqsRange(ctx context.Context, userID strin cacheDelNum += 1 } } - log.ZDebug(ctx, "get delSeqs from redis", "delSeqs", delSeqs, "userID", userID, "conversationID", conversationID, "cacheDelNum", cacheDelNum) + log.ZDebug( + ctx, + "get delSeqs from redis", + "delSeqs", + delSeqs, + "userID", + userID, + "conversationID", + conversationID, + "cacheDelNum", + cacheDelNum, + ) var reGetSeqsCache []int64 for i := 1; i <= cacheDelNum; { newSeq := newBegin - int64(i) @@ -546,7 +692,15 @@ func (db *commonMsgDatabase) GetMsgBySeqsRange(ctx context.Context, userID strin if err != nil { if err != redis.Nil { prome.Add(prome.MsgPullFromRedisFailedCounter, len(failedSeqs2)) - log.ZError(ctx, "get message from redis exception", err, "conversationID", conversationID, "seqs", reGetSeqsCache) + log.ZError( + ctx, + "get message from redis exception", + err, + "conversationID", + conversationID, + "seqs", + reGetSeqsCache, + ) } } failedSeqs = append(failedSeqs, failedSeqs2...) @@ -572,7 +726,12 @@ func (db *commonMsgDatabase) GetMsgBySeqsRange(ctx context.Context, userID strin return minSeq, maxSeq, successMsgs, nil } -func (db *commonMsgDatabase) GetMsgBySeqs(ctx context.Context, userID string, conversationID string, seqs []int64) (int64, int64, []*sdkws.MsgData, error) { +func (db *commonMsgDatabase) GetMsgBySeqs( + ctx context.Context, + userID string, + conversationID string, + seqs []int64, +) (int64, int64, []*sdkws.MsgData, error) { userMinSeq, err := db.cache.GetConversationUserMinSeq(ctx, conversationID, userID) if err != nil && errs.Unwrap(err) != redis.Nil { return 0, 0, nil, err @@ -598,10 +757,33 @@ func (db *commonMsgDatabase) GetMsgBySeqs(ctx context.Context, userID string, co if err != nil { if err != redis.Nil { prome.Add(prome.MsgPullFromRedisFailedCounter, len(failedSeqs)) - log.ZError(ctx, "get message from redis exception", err, "failedSeqs", failedSeqs, "conversationID", conversationID) + log.ZError( + ctx, + "get message from redis exception", + err, + "failedSeqs", + failedSeqs, + "conversationID", + conversationID, + ) } } - log.ZInfo(ctx, "db.cache.GetMessagesBySeq", "userID", userID, "conversationID", conversationID, "seqs", seqs, "successMsgs", len(successMsgs), "failedSeqs", failedSeqs, "conversationID", conversationID) + log.ZInfo( + ctx, + "db.cache.GetMessagesBySeq", + "userID", + userID, + "conversationID", + conversationID, + "seqs", + seqs, + "successMsgs", + len(successMsgs), + "failedSeqs", + failedSeqs, + "conversationID", + conversationID, + ) prome.Add(prome.MsgPullFromRedisSuccessCounter, len(successMsgs)) if len(failedSeqs) > 0 { mongoMsgs, err := db.getMsgBySeqs(ctx, userID, conversationID, failedSeqs) @@ -615,7 +797,11 @@ func (db *commonMsgDatabase) GetMsgBySeqs(ctx context.Context, userID string, co return minSeq, maxSeq, successMsgs, nil } -func (db *commonMsgDatabase) DeleteConversationMsgsAndSetMinSeq(ctx context.Context, conversationID string, remainTime int64) error { +func (db *commonMsgDatabase) DeleteConversationMsgsAndSetMinSeq( + ctx context.Context, + conversationID string, + remainTime int64, +) error { var delStruct delMsgRecursionStruct var skip int64 minSeq, err := db.deleteMsgRecursion(ctx, conversationID, skip, &delStruct, remainTime) @@ -649,13 +835,26 @@ func (d *delMsgRecursionStruct) getSetMinSeq() int64 { // seq 70 // set minSeq 21 // recursion 删除list并且返回设置的最小seq -func (db *commonMsgDatabase) deleteMsgRecursion(ctx context.Context, conversationID string, index int64, delStruct *delMsgRecursionStruct, remainTime int64) (int64, error) { +func (db *commonMsgDatabase) deleteMsgRecursion( + ctx context.Context, + conversationID string, + index int64, + delStruct *delMsgRecursionStruct, + remainTime int64, +) (int64, error) { // find from oldest list msgDocModel, err := db.msgDocDatabase.GetMsgDocModelByIndex(ctx, conversationID, index, 1) if err != nil || msgDocModel.DocID == "" { if err != nil { if err == unrelation.ErrMsgListNotExist { - log.ZDebug(ctx, "deleteMsgRecursion ErrMsgListNotExist", "conversationID", conversationID, "index:", index) + log.ZDebug( + ctx, + "deleteMsgRecursion ErrMsgListNotExist", + "conversationID", + conversationID, + "index:", + index, + ) } else { log.ZError(ctx, "deleteMsgRecursion GetUserMsgListByIndex failed", err, "conversationID", conversationID, "index", index) } @@ -667,11 +866,23 @@ func (db *commonMsgDatabase) deleteMsgRecursion(ctx context.Context, conversatio } return delStruct.getSetMinSeq() + 1, nil } - log.ZDebug(ctx, "doc info", "conversationID", conversationID, "index", index, "docID", msgDocModel.DocID, "len", len(msgDocModel.Msg)) + log.ZDebug( + ctx, + "doc info", + "conversationID", + conversationID, + "index", + index, + "docID", + msgDocModel.DocID, + "len", + len(msgDocModel.Msg), + ) if int64(len(msgDocModel.Msg)) > db.msg.GetSingleGocMsgNum() { log.ZWarn(ctx, "msgs too large", nil, "lenth", len(msgDocModel.Msg), "docID:", msgDocModel.DocID) } - if msgDocModel.IsFull() && msgDocModel.Msg[len(msgDocModel.Msg)-1].Msg.SendTime+(remainTime*1000) < utils.GetCurrentTimestampByMill() { + if msgDocModel.IsFull() && + msgDocModel.Msg[len(msgDocModel.Msg)-1].Msg.SendTime+(remainTime*1000) < utils.GetCurrentTimestampByMill() { log.ZDebug(ctx, "doc is full and all msg is expired", "docID", msgDocModel.DocID) delStruct.delDocIDs = append(delStruct.delDocIDs, msgDocModel.DocID) delStruct.minSeq = msgDocModel.Msg[len(msgDocModel.Msg)-1].Msg.Seq @@ -708,7 +919,11 @@ func (db *commonMsgDatabase) deleteMsgRecursion(ctx context.Context, conversatio return seq, err } -func (db *commonMsgDatabase) DeleteMsgsPhysicalBySeqs(ctx context.Context, conversationID string, allSeqs []int64) error { +func (db *commonMsgDatabase) DeleteMsgsPhysicalBySeqs( + ctx context.Context, + conversationID string, + allSeqs []int64, +) error { if err := db.cache.DeleteMessages(ctx, conversationID, allSeqs); err != nil { return err } @@ -724,7 +939,12 @@ func (db *commonMsgDatabase) DeleteMsgsPhysicalBySeqs(ctx context.Context, conve return nil } -func (db *commonMsgDatabase) DeleteUserMsgsBySeqs(ctx context.Context, userID string, conversationID string, seqs []int64) error { +func (db *commonMsgDatabase) DeleteUserMsgsBySeqs( + ctx context.Context, + userID string, + conversationID string, + seqs []int64, +) error { cachedMsgs, _, err := db.cache.GetMessagesBySeq(ctx, conversationID, seqs) if err != nil && errs.Unwrap(err) != redis.Nil { log.ZWarn(ctx, "DeleteUserMsgsBySeqs", err, "conversationID", conversationID, "seqs", seqs) @@ -793,31 +1013,70 @@ func (db *commonMsgDatabase) GetMinSeqs(ctx context.Context, conversationIDs []s func (db *commonMsgDatabase) GetMinSeq(ctx context.Context, conversationID string) (int64, error) { return db.cache.GetMinSeq(ctx, conversationID) } -func (db *commonMsgDatabase) GetConversationUserMinSeq(ctx context.Context, conversationID string, userID string) (int64, error) { + +func (db *commonMsgDatabase) GetConversationUserMinSeq( + ctx context.Context, + conversationID string, + userID string, +) (int64, error) { return db.cache.GetConversationUserMinSeq(ctx, conversationID, userID) } -func (db *commonMsgDatabase) GetConversationUserMinSeqs(ctx context.Context, conversationID string, userIDs []string) (map[string]int64, error) { + +func (db *commonMsgDatabase) GetConversationUserMinSeqs( + ctx context.Context, + conversationID string, + userIDs []string, +) (map[string]int64, error) { return db.cache.GetConversationUserMinSeqs(ctx, conversationID, userIDs) } -func (db *commonMsgDatabase) SetConversationUserMinSeq(ctx context.Context, conversationID string, userID string, minSeq int64) error { + +func (db *commonMsgDatabase) SetConversationUserMinSeq( + ctx context.Context, + conversationID string, + userID string, + minSeq int64, +) error { return db.cache.SetConversationUserMinSeq(ctx, conversationID, userID, minSeq) } -func (db *commonMsgDatabase) SetConversationUserMinSeqs(ctx context.Context, conversationID string, seqs map[string]int64) (err error) { + +func (db *commonMsgDatabase) SetConversationUserMinSeqs( + ctx context.Context, + conversationID string, + seqs map[string]int64, +) (err error) { return db.cache.SetConversationUserMinSeqs(ctx, conversationID, seqs) } -func (db *commonMsgDatabase) SetUserConversationsMinSeqs(ctx context.Context, userID string, seqs map[string]int64) error { +func (db *commonMsgDatabase) SetUserConversationsMinSeqs( + ctx context.Context, + userID string, + seqs map[string]int64, +) error { return db.cache.SetUserConversationsMinSeqs(ctx, userID, seqs) } -func (db *commonMsgDatabase) UserSetHasReadSeqs(ctx context.Context, userID string, hasReadSeqs map[string]int64) error { +func (db *commonMsgDatabase) UserSetHasReadSeqs( + ctx context.Context, + userID string, + hasReadSeqs map[string]int64, +) error { return db.cache.UserSetHasReadSeqs(ctx, userID, hasReadSeqs) } -func (db *commonMsgDatabase) SetHasReadSeq(ctx context.Context, userID string, conversationID string, hasReadSeq int64) error { +func (db *commonMsgDatabase) SetHasReadSeq( + ctx context.Context, + userID string, + conversationID string, + hasReadSeq int64, +) error { return db.cache.SetHasReadSeq(ctx, userID, conversationID, hasReadSeq) } -func (db *commonMsgDatabase) GetHasReadSeqs(ctx context.Context, userID string, conversationIDs []string) (map[string]int64, error) { + +func (db *commonMsgDatabase) GetHasReadSeqs( + ctx context.Context, + userID string, + conversationIDs []string, +) (map[string]int64, error) { return db.cache.GetHasReadSeqs(ctx, userID, conversationIDs) } @@ -833,7 +1092,10 @@ func (db *commonMsgDatabase) GetSendMsgStatus(ctx context.Context, id string) (i return db.cache.GetSendMsgStatus(ctx, id) } -func (db *commonMsgDatabase) GetConversationMinMaxSeqInMongoAndCache(ctx context.Context, conversationID string) (minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache int64, err error) { +func (db *commonMsgDatabase) GetConversationMinMaxSeqInMongoAndCache( + ctx context.Context, + conversationID string, +) (minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache int64, err error) { minSeqMongo, maxSeqMongo, err = db.GetMinMaxSeqMongo(ctx, conversationID) if err != nil { return @@ -849,11 +1111,17 @@ func (db *commonMsgDatabase) GetConversationMinMaxSeqInMongoAndCache(ctx context return } -func (db *commonMsgDatabase) GetMongoMaxAndMinSeq(ctx context.Context, conversationID string) (maxSeq, minSeq int64, err error) { +func (db *commonMsgDatabase) GetMongoMaxAndMinSeq( + ctx context.Context, + conversationID string, +) (maxSeq, minSeq int64, err error) { return db.GetMinMaxSeqMongo(ctx, conversationID) } -func (db *commonMsgDatabase) GetMinMaxSeqMongo(ctx context.Context, conversationID string) (minSeqMongo, maxSeqMongo int64, err error) { +func (db *commonMsgDatabase) GetMinMaxSeqMongo( + ctx context.Context, + conversationID string, +) (minSeqMongo, maxSeqMongo int64, err error) { oldestMsgMongo, err := db.msgDocDatabase.GetOldestMsg(ctx, conversationID) if err != nil { return @@ -867,35 +1135,83 @@ func (db *commonMsgDatabase) GetMinMaxSeqMongo(ctx context.Context, conversation return } -func (db *commonMsgDatabase) JudgeMessageReactionExist(ctx context.Context, clientMsgID string, sessionType int32) (bool, error) { +func (db *commonMsgDatabase) JudgeMessageReactionExist( + ctx context.Context, + clientMsgID string, + sessionType int32, +) (bool, error) { return db.cache.JudgeMessageReactionExist(ctx, clientMsgID, sessionType) } -func (db *commonMsgDatabase) SetMessageTypeKeyValue(ctx context.Context, clientMsgID string, sessionType int32, typeKey, value string) error { +func (db *commonMsgDatabase) SetMessageTypeKeyValue( + ctx context.Context, + clientMsgID string, + sessionType int32, + typeKey, value string, +) error { return db.cache.SetMessageTypeKeyValue(ctx, clientMsgID, sessionType, typeKey, value) } -func (db *commonMsgDatabase) SetMessageReactionExpire(ctx context.Context, clientMsgID string, sessionType int32, expiration time.Duration) (bool, error) { +func (db *commonMsgDatabase) SetMessageReactionExpire( + ctx context.Context, + clientMsgID string, + sessionType int32, + expiration time.Duration, +) (bool, error) { return db.cache.SetMessageReactionExpire(ctx, clientMsgID, sessionType, expiration) } -func (db *commonMsgDatabase) GetMessageTypeKeyValue(ctx context.Context, clientMsgID string, sessionType int32, typeKey string) (string, error) { +func (db *commonMsgDatabase) GetMessageTypeKeyValue( + ctx context.Context, + clientMsgID string, + sessionType int32, + typeKey string, +) (string, error) { return db.cache.GetMessageTypeKeyValue(ctx, clientMsgID, sessionType, typeKey) } -func (db *commonMsgDatabase) GetOneMessageAllReactionList(ctx context.Context, clientMsgID string, sessionType int32) (map[string]string, error) { +func (db *commonMsgDatabase) GetOneMessageAllReactionList( + ctx context.Context, + clientMsgID string, + sessionType int32, +) (map[string]string, error) { return db.cache.GetOneMessageAllReactionList(ctx, clientMsgID, sessionType) } -func (db *commonMsgDatabase) DeleteOneMessageKey(ctx context.Context, clientMsgID string, sessionType int32, subKey string) error { +func (db *commonMsgDatabase) DeleteOneMessageKey( + ctx context.Context, + clientMsgID string, + sessionType int32, + subKey string, +) error { return db.cache.DeleteOneMessageKey(ctx, clientMsgID, sessionType, subKey) } -func (db *commonMsgDatabase) InsertOrUpdateReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensions map[string]*sdkws.KeyValue) error { - return db.extendMsgDatabase.InsertOrUpdateReactionExtendMsgSet(ctx, conversationID, sessionType, clientMsgID, msgFirstModifyTime, db.extendMsgSetModel.Pb2Model(reactionExtensions)) +func (db *commonMsgDatabase) InsertOrUpdateReactionExtendMsgSet( + ctx context.Context, + conversationID string, + sessionType int32, + clientMsgID string, + msgFirstModifyTime int64, + reactionExtensions map[string]*sdkws.KeyValue, +) error { + return db.extendMsgDatabase.InsertOrUpdateReactionExtendMsgSet( + ctx, + conversationID, + sessionType, + clientMsgID, + msgFirstModifyTime, + db.extendMsgSetModel.Pb2Model(reactionExtensions), + ) } -func (db *commonMsgDatabase) GetExtendMsg(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, maxMsgUpdateTime int64) (*pbMsg.ExtendMsg, error) { +func (db *commonMsgDatabase) GetExtendMsg( + ctx context.Context, + conversationID string, + sessionType int32, + clientMsgID string, + maxMsgUpdateTime int64, +) (*pbMsg.ExtendMsg, error) { extendMsgSet, err := db.extendMsgDatabase.GetExtendMsgSet(ctx, conversationID, sessionType, maxMsgUpdateTime) if err != nil { return nil, err @@ -923,6 +1239,20 @@ func (db *commonMsgDatabase) GetExtendMsg(ctx context.Context, conversationID st }, nil } -func (db *commonMsgDatabase) DeleteReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensions map[string]*sdkws.KeyValue) error { - return db.extendMsgDatabase.DeleteReactionExtendMsgSet(ctx, conversationID, sessionType, clientMsgID, msgFirstModifyTime, db.extendMsgSetModel.Pb2Model(reactionExtensions)) +func (db *commonMsgDatabase) DeleteReactionExtendMsgSet( + ctx context.Context, + conversationID string, + sessionType int32, + clientMsgID string, + msgFirstModifyTime int64, + reactionExtensions map[string]*sdkws.KeyValue, +) error { + return db.extendMsgDatabase.DeleteReactionExtendMsgSet( + ctx, + conversationID, + sessionType, + clientMsgID, + msgFirstModifyTime, + db.extendMsgSetModel.Pb2Model(reactionExtensions), + ) } diff --git a/pkg/common/db/controller/msg_test.go b/pkg/common/db/controller/msg_test.go index 8310d9d6a..6771bdab4 100644 --- a/pkg/common/db/controller/msg_test.go +++ b/pkg/common/db/controller/msg_test.go @@ -11,10 +11,11 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" + "go.mongodb.org/mongo-driver/bson" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" unRelationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/unrelation" - "go.mongodb.org/mongo-driver/bson" ) func Test_BatchInsertChat2DB(t *testing.T) { @@ -193,7 +194,9 @@ func Test_FindBySeq(t *testing.T) { } db := GetDB() ctx := context.Background() - fmt.Println(db.msgDocDatabase.(*unrelation.MsgMongoDriver).GetMsgBySeqIndexIn1Doc(ctx, "100", "si_100_101:0", []int64{1})) + fmt.Println( + db.msgDocDatabase.(*unrelation.MsgMongoDriver).GetMsgBySeqIndexIn1Doc(ctx, "100", "si_100_101:0", []int64{1}), + ) //res, err := db.msgDocDatabase.GetMsgBySeqIndexIn1Doc(ctx, "123456", "test:0", []int64{1, 2, 3}) //if err != nil { // t.Fatal(err) diff --git a/pkg/common/db/controller/storage.go b/pkg/common/db/controller/storage.go index 6531c5127..22b2274f3 100644 --- a/pkg/common/db/controller/storage.go +++ b/pkg/common/db/controller/storage.go @@ -15,13 +15,14 @@ import ( "strconv" "time" + "github.com/google/uuid" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/obj" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/third" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "github.com/google/uuid" ) const ( @@ -40,7 +41,13 @@ type S3Database interface { CleanExpirationObject(ctx context.Context, t time.Time) } -func NewS3Database(obj obj.Interface, hash relation.ObjectHashModelInterface, info relation.ObjectInfoModelInterface, put relation.ObjectPutModelInterface, url *url.URL) S3Database { +func NewS3Database( + obj obj.Interface, + hash relation.ObjectHashModelInterface, + info relation.ObjectInfoModelInterface, + put relation.ObjectPutModelInterface, + url *url.URL, +) S3Database { return &s3Database{ url: url, obj: obj, @@ -207,7 +214,12 @@ func (c *s3Database) ApplyPut(ctx context.Context, req *third.ApplyPutReq) (*thi } t := md5.Sum(urlsJsonData) put.PutURLsHash = hex.EncodeToString(t[:]) - _, err = c.obj.PutObject(ctx, &obj.BucketObject{Bucket: c.obj.TempBucket(), Name: path.Join(put.Path, urlsName)}, bytes.NewReader(urlsJsonData), int64(len(urlsJsonData))) + _, err = c.obj.PutObject( + ctx, + &obj.BucketObject{Bucket: c.obj.TempBucket(), Name: path.Join(put.Path, urlsName)}, + bytes.NewReader(urlsJsonData), + int64(len(urlsJsonData)), + ) if err != nil { return nil, err } @@ -228,7 +240,10 @@ func (c *s3Database) GetPut(ctx context.Context, req *third.GetPutReq) (*third.G if err != nil { return nil, err } - reader, err := c.obj.GetObject(ctx, &obj.BucketObject{Bucket: c.obj.TempBucket(), Name: path.Join(up.Path, urlsName)}) + reader, err := c.obj.GetObject( + ctx, + &obj.BucketObject{Bucket: c.obj.TempBucket(), Name: path.Join(up.Path, urlsName)}, + ) if err != nil { return nil, err } diff --git a/pkg/common/db/controller/third.go b/pkg/common/db/controller/third.go index 20c8a9102..276084920 100644 --- a/pkg/common/db/controller/third.go +++ b/pkg/common/db/controller/third.go @@ -19,7 +19,13 @@ func NewThirdDatabase(cache cache.MsgModel) ThirdDatabase { return &thirdDatabase{cache: cache} } -func (t *thirdDatabase) FcmUpdateToken(ctx context.Context, account string, platformID int, fcmToken string, expireTime int64) error { +func (t *thirdDatabase) FcmUpdateToken( + ctx context.Context, + account string, + platformID int, + fcmToken string, + expireTime int64, +) error { return t.cache.SetFcmToken(ctx, account, platformID, fcmToken, expireTime) } diff --git a/pkg/common/db/controller/user.go b/pkg/common/db/controller/user.go index d1346d311..9a2e01b47 100644 --- a/pkg/common/db/controller/user.go +++ b/pkg/common/db/controller/user.go @@ -114,7 +114,10 @@ func (u *userDatabase) UpdateByMap(ctx context.Context, userID string, args map[ } // 获取,如果没找到,不返回错误 -func (u *userDatabase) Page(ctx context.Context, pageNumber, showNumber int32) (users []*relation.UserModel, count int64, err error) { +func (u *userDatabase) Page( + ctx context.Context, + pageNumber, showNumber int32, +) (users []*relation.UserModel, count int64, err error) { return u.userDB.Page(ctx, pageNumber, showNumber) } @@ -138,6 +141,10 @@ func (u *userDatabase) CountTotal(ctx context.Context) (count int64, err error) return u.userDB.CountTotal(ctx) } -func (u *userDatabase) CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) { +func (u *userDatabase) CountRangeEverydayTotal( + ctx context.Context, + start time.Time, + end time.Time, +) (map[string]int64, error) { return u.userDB.CountRangeEverydayTotal(ctx, start, end) } diff --git a/pkg/common/db/obj/minio.go b/pkg/common/db/obj/minio.go index 488775f27..2b39fff8b 100644 --- a/pkg/common/db/obj/minio.go +++ b/pkg/common/db/obj/minio.go @@ -9,11 +9,12 @@ import ( "net/url" "time" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" - "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" "github.com/minio/minio-go/v7/pkg/s3utils" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" + "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" ) func NewMinioInterface() (Interface, error) { @@ -94,7 +95,13 @@ func (m *minioImpl) DataBucket() string { return m.dataBucket } -func (m *minioImpl) PresignedGetURL(ctx context.Context, bucket string, name string, expires time.Duration, opt *HeaderOption) (string, error) { +func (m *minioImpl) PresignedGetURL( + ctx context.Context, + bucket string, + name string, + expires time.Duration, + opt *HeaderOption, +) (string, error) { var reqParams url.Values if opt != nil { reqParams = make(url.Values) @@ -204,7 +211,12 @@ func (m *minioImpl) IsNotFound(err error) bool { } } -func (m *minioImpl) PutObject(ctx context.Context, info *BucketObject, reader io.Reader, size int64) (*ObjectInfo, error) { +func (m *minioImpl) PutObject( + ctx context.Context, + info *BucketObject, + reader io.Reader, + size int64, +) (*ObjectInfo, error) { update, err := m.client.PutObject(ctx, info.Bucket, info.Name, reader, size, minio.PutObjectOptions{}) if err != nil { return nil, err diff --git a/pkg/common/db/obj/obj.go b/pkg/common/db/obj/obj.go index e32c2479b..00c8b86b1 100644 --- a/pkg/common/db/obj/obj.go +++ b/pkg/common/db/obj/obj.go @@ -68,7 +68,13 @@ type Interface interface { // DataBucket 永久存储的桶名 DataBucket() string // PresignedGetURL 通过桶名和对象名返回URL - PresignedGetURL(ctx context.Context, bucket string, name string, expires time.Duration, opt *HeaderOption) (string, error) + PresignedGetURL( + ctx context.Context, + bucket string, + name string, + expires time.Duration, + opt *HeaderOption, + ) (string, error) // PresignedPutURL 申请上传,返回PUT的上传地址 PresignedPutURL(ctx context.Context, args *ApplyPutArgs) (string, error) // GetObjectInfo 获取对象信息 diff --git a/pkg/common/db/ormutil/utils.go b/pkg/common/db/ormutil/utils.go index 9c4e19a5b..c92b091ef 100644 --- a/pkg/common/db/ormutil/utils.go +++ b/pkg/common/db/ormutil/utils.go @@ -4,8 +4,9 @@ import ( "fmt" "strings" - "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "gorm.io/gorm" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" ) func GormPage[E any](db *gorm.DB, pageNumber, showNumber int32) (uint32, []*E, error) { diff --git a/pkg/common/db/relation/black_model.go b/pkg/common/db/relation/black_model.go index 2faadda0c..736d9ed01 100644 --- a/pkg/common/db/relation/black_model.go +++ b/pkg/common/db/relation/black_model.go @@ -5,9 +5,10 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/ormutil" + "gorm.io/gorm" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "gorm.io/gorm" ) type BlackGorm struct { @@ -26,37 +27,64 @@ func (b *BlackGorm) Delete(ctx context.Context, blacks []*relation.BlackModel) ( return utils.Wrap(b.db(ctx).Delete(blacks).Error, "") } -func (b *BlackGorm) UpdateByMap(ctx context.Context, ownerUserID, blockUserID string, args map[string]interface{}) (err error) { - return utils.Wrap(b.db(ctx).Where("block_user_id = ? and block_user_id = ?", ownerUserID, blockUserID).Updates(args).Error, "") +func (b *BlackGorm) UpdateByMap( + ctx context.Context, + ownerUserID, blockUserID string, + args map[string]interface{}, +) (err error) { + return utils.Wrap( + b.db(ctx).Where("block_user_id = ? and block_user_id = ?", ownerUserID, blockUserID).Updates(args).Error, + "", + ) } func (b *BlackGorm) Update(ctx context.Context, blacks []*relation.BlackModel) (err error) { return utils.Wrap(b.db(ctx).Updates(&blacks).Error, "") } -func (b *BlackGorm) Find(ctx context.Context, blacks []*relation.BlackModel) (blackList []*relation.BlackModel, err error) { +func (b *BlackGorm) Find( + ctx context.Context, + blacks []*relation.BlackModel, +) (blackList []*relation.BlackModel, err error) { var where [][]interface{} for _, black := range blacks { where = append(where, []interface{}{black.OwnerUserID, black.BlockUserID}) } - return blackList, utils.Wrap(b.db(ctx).Where("(owner_user_id, block_user_id) in ?", where).Find(&blackList).Error, "") + return blackList, utils.Wrap( + b.db(ctx).Where("(owner_user_id, block_user_id) in ?", where).Find(&blackList).Error, + "", + ) } func (b *BlackGorm) Take(ctx context.Context, ownerUserID, blockUserID string) (black *relation.BlackModel, err error) { black = &relation.BlackModel{} - return black, utils.Wrap(b.db(ctx).Where("owner_user_id = ? and block_user_id = ?", ownerUserID, blockUserID).Take(black).Error, "") + return black, utils.Wrap( + b.db(ctx).Where("owner_user_id = ? and block_user_id = ?", ownerUserID, blockUserID).Take(black).Error, + "", + ) } -func (b *BlackGorm) FindOwnerBlacks(ctx context.Context, ownerUserID string, pageNumber, showNumber int32) (blacks []*relation.BlackModel, total int64, err error) { +func (b *BlackGorm) FindOwnerBlacks( + ctx context.Context, + ownerUserID string, + pageNumber, showNumber int32, +) (blacks []*relation.BlackModel, total int64, err error) { err = b.db(ctx).Count(&total).Error if err != nil { return nil, 0, utils.Wrap(err, "") } - totalUint32, blacks, err := ormutil.GormPage[relation.BlackModel](b.db(ctx).Where("owner_user_id = ?", ownerUserID), pageNumber, showNumber) + totalUint32, blacks, err := ormutil.GormPage[relation.BlackModel]( + b.db(ctx).Where("owner_user_id = ?", ownerUserID), + pageNumber, + showNumber, + ) total = int64(totalUint32) return } func (b *BlackGorm) FindBlackUserIDs(ctx context.Context, ownerUserID string) (blackUserIDs []string, err error) { - return blackUserIDs, utils.Wrap(b.db(ctx).Where("owner_user_id = ?", ownerUserID).Pluck("block_user_id", &blackUserIDs).Error, "") + return blackUserIDs, utils.Wrap( + b.db(ctx).Where("owner_user_id = ?", ownerUserID).Pluck("block_user_id", &blackUserIDs).Error, + "", + ) } diff --git a/pkg/common/db/relation/chat_log_model.go b/pkg/common/db/relation/chat_log_model.go index f164b146b..ef6dd2a8a 100644 --- a/pkg/common/db/relation/chat_log_model.go +++ b/pkg/common/db/relation/chat_log_model.go @@ -3,15 +3,16 @@ package relation import ( "fmt" + "github.com/golang/protobuf/jsonpb" + "github.com/jinzhu/copier" + "google.golang.org/protobuf/proto" + "gorm.io/gorm" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" pbMsg "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "github.com/golang/protobuf/jsonpb" - "github.com/jinzhu/copier" - "google.golang.org/protobuf/proto" - "gorm.io/gorm" ) type ChatLogGorm struct { @@ -48,7 +49,11 @@ func (c *ChatLogGorm) Create(msg *pbMsg.MsgDataToMQ) error { return c.DB.Create(chatLog).Error } -func (c *ChatLogGorm) GetChatLog(chatLog *relation.ChatLogModel, pageNumber, showNumber int32, contentTypeList []int32) (int64, []relation.ChatLogModel, error) { +func (c *ChatLogGorm) GetChatLog( + chatLog *relation.ChatLogModel, + pageNumber, showNumber int32, + contentTypeList []int32, +) (int64, []relation.ChatLogModel, error) { mdb := c.DB.Model(chatLog) if chatLog.SendTime.Unix() > 0 { mdb = mdb.Where("send_time > ? and send_time < ?", chatLog.SendTime, chatLog.SendTime.AddDate(0, 0, 1)) diff --git a/pkg/common/db/relation/conversation_model.go b/pkg/common/db/relation/conversation_model.go index f8522506a..cd67ad165 100644 --- a/pkg/common/db/relation/conversation_model.go +++ b/pkg/common/db/relation/conversation_model.go @@ -3,10 +3,11 @@ package relation import ( "context" + "gorm.io/gorm" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "gorm.io/gorm" ) type ConversationGorm struct { @@ -29,61 +30,160 @@ func (c *ConversationGorm) Delete(ctx context.Context, groupIDs []string) (err e return utils.Wrap(c.db(ctx).Where("group_id in (?)", groupIDs).Delete(&relation.ConversationModel{}).Error, "") } -func (c *ConversationGorm) UpdateByMap(ctx context.Context, userIDList []string, conversationID string, args map[string]interface{}) (rows int64, err error) { +func (c *ConversationGorm) UpdateByMap( + ctx context.Context, + userIDList []string, + conversationID string, + args map[string]interface{}, +) (rows int64, err error) { result := c.db(ctx).Where("owner_user_id IN (?) and conversation_id=?", userIDList, conversationID).Updates(args) return result.RowsAffected, utils.Wrap(result.Error, "") } func (c *ConversationGorm) Update(ctx context.Context, conversation *relation.ConversationModel) (err error) { - return utils.Wrap(c.db(ctx).Where("owner_user_id = ? and conversation_id = ?", conversation.OwnerUserID, conversation.ConversationID).Updates(conversation).Error, "") -} - -func (c *ConversationGorm) Find(ctx context.Context, ownerUserID string, conversationIDs []string) (conversations []*relation.ConversationModel, err error) { - err = utils.Wrap(c.db(ctx).Where("owner_user_id=? and conversation_id IN (?)", ownerUserID, conversationIDs).Find(&conversations).Error, "") + return utils.Wrap( + c.db(ctx). + Where("owner_user_id = ? and conversation_id = ?", conversation.OwnerUserID, conversation.ConversationID). + Updates(conversation). + Error, + "", + ) +} + +func (c *ConversationGorm) Find( + ctx context.Context, + ownerUserID string, + conversationIDs []string, +) (conversations []*relation.ConversationModel, err error) { + err = utils.Wrap( + c.db(ctx). + Where("owner_user_id=? and conversation_id IN (?)", ownerUserID, conversationIDs). + Find(&conversations). + Error, + "", + ) return conversations, err } -func (c *ConversationGorm) Take(ctx context.Context, userID, conversationID string) (conversation *relation.ConversationModel, err error) { +func (c *ConversationGorm) Take( + ctx context.Context, + userID, conversationID string, +) (conversation *relation.ConversationModel, err error) { cc := &relation.ConversationModel{} - return cc, utils.Wrap(c.db(ctx).Where("conversation_id = ? And owner_user_id = ?", conversationID, userID).Take(cc).Error, "") -} - -func (c *ConversationGorm) FindUserID(ctx context.Context, userIDs []string, conversationIDs []string) (existUserID []string, err error) { - return existUserID, utils.Wrap(c.db(ctx).Where(" owner_user_id IN (?) and conversation_id in (?)", userIDs, conversationIDs).Pluck("owner_user_id", &existUserID).Error, "") -} - -func (c *ConversationGorm) FindConversationID(ctx context.Context, userID string, conversationIDList []string) (existConversationID []string, err error) { - return existConversationID, utils.Wrap(c.db(ctx).Where(" conversation_id IN (?) and owner_user_id=?", conversationIDList, userID).Pluck("conversation_id", &existConversationID).Error, "") -} - -func (c *ConversationGorm) FindUserIDAllConversationID(ctx context.Context, userID string) (conversationIDList []string, err error) { - return conversationIDList, utils.Wrap(c.db(ctx).Where("owner_user_id=?", userID).Pluck("conversation_id", &conversationIDList).Error, "") -} - -func (c *ConversationGorm) FindUserIDAllConversations(ctx context.Context, userID string) (conversations []*relation.ConversationModel, err error) { + return cc, utils.Wrap( + c.db(ctx).Where("conversation_id = ? And owner_user_id = ?", conversationID, userID).Take(cc).Error, + "", + ) +} + +func (c *ConversationGorm) FindUserID( + ctx context.Context, + userIDs []string, + conversationIDs []string, +) (existUserID []string, err error) { + return existUserID, utils.Wrap( + c.db(ctx). + Where(" owner_user_id IN (?) and conversation_id in (?)", userIDs, conversationIDs). + Pluck("owner_user_id", &existUserID). + Error, + "", + ) +} + +func (c *ConversationGorm) FindConversationID( + ctx context.Context, + userID string, + conversationIDList []string, +) (existConversationID []string, err error) { + return existConversationID, utils.Wrap( + c.db(ctx). + Where(" conversation_id IN (?) and owner_user_id=?", conversationIDList, userID). + Pluck("conversation_id", &existConversationID). + Error, + "", + ) +} + +func (c *ConversationGorm) FindUserIDAllConversationID( + ctx context.Context, + userID string, +) (conversationIDList []string, err error) { + return conversationIDList, utils.Wrap( + c.db(ctx).Where("owner_user_id=?", userID).Pluck("conversation_id", &conversationIDList).Error, + "", + ) +} + +func (c *ConversationGorm) FindUserIDAllConversations( + ctx context.Context, + userID string, +) (conversations []*relation.ConversationModel, err error) { return conversations, utils.Wrap(c.db(ctx).Where("owner_user_id=?", userID).Find(&conversations).Error, "") } -func (c *ConversationGorm) FindRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) (userIDs []string, err error) { - return userIDs, utils.Wrap(c.db(ctx).Where("group_id = ? and recv_msg_opt = ?", groupID, constant.ReceiveNotNotifyMessage).Pluck("user_id", &userIDs).Error, "") -} - -func (c *ConversationGorm) FindSuperGroupRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) (userIDs []string, err error) { - return userIDs, utils.Wrap(c.db(ctx).Where("group_id = ? and recv_msg_opt = ? and conversation_type = ?", groupID, constant.ReceiveNotNotifyMessage, constant.SuperGroupChatType).Pluck("user_id", &userIDs).Error, "") -} - -func (c *ConversationGorm) GetUserRecvMsgOpt(ctx context.Context, ownerUserID, conversationID string) (opt int, err error) { +func (c *ConversationGorm) FindRecvMsgNotNotifyUserIDs( + ctx context.Context, + groupID string, +) (userIDs []string, err error) { + return userIDs, utils.Wrap( + c.db(ctx). + Where("group_id = ? and recv_msg_opt = ?", groupID, constant.ReceiveNotNotifyMessage). + Pluck("user_id", &userIDs). + Error, + "", + ) +} + +func (c *ConversationGorm) FindSuperGroupRecvMsgNotNotifyUserIDs( + ctx context.Context, + groupID string, +) (userIDs []string, err error) { + return userIDs, utils.Wrap( + c.db(ctx). + Where("group_id = ? and recv_msg_opt = ? and conversation_type = ?", groupID, constant.ReceiveNotNotifyMessage, constant.SuperGroupChatType). + Pluck("user_id", &userIDs). + Error, + "", + ) +} + +func (c *ConversationGorm) GetUserRecvMsgOpt( + ctx context.Context, + ownerUserID, conversationID string, +) (opt int, err error) { var conversation relation.ConversationModel - return int(conversation.RecvMsgOpt), utils.Wrap(c.db(ctx).Where("conversation_id = ? And owner_user_id = ?", conversationID, ownerUserID).Select("recv_msg_opt").Find(&conversation).Error, "") + return int( + conversation.RecvMsgOpt, + ), utils.Wrap( + c.db(ctx). + Where("conversation_id = ? And owner_user_id = ?", conversationID, ownerUserID). + Select("recv_msg_opt"). + Find(&conversation). + Error, + "", + ) } func (c *ConversationGorm) GetAllConversationIDs(ctx context.Context) (conversationIDs []string, err error) { - return conversationIDs, utils.Wrap(c.db(ctx).Distinct("conversation_id").Pluck("conversation_id", &conversationIDs).Error, "") + return conversationIDs, utils.Wrap( + c.db(ctx).Distinct("conversation_id").Pluck("conversation_id", &conversationIDs).Error, + "", + ) } -func (c *ConversationGorm) GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (hasReadSeqs map[string]int64, err error) { +func (c *ConversationGorm) GetUserAllHasReadSeqs( + ctx context.Context, + ownerUserID string, +) (hasReadSeqs map[string]int64, err error) { var conversations []*relation.ConversationModel - err = utils.Wrap(c.db(ctx).Where("owner_user_id = ?", ownerUserID).Select("conversation_id", "has_read_seq").Find(&conversations).Error, "") + err = utils.Wrap( + c.db(ctx). + Where("owner_user_id = ?", ownerUserID). + Select("conversation_id", "has_read_seq"). + Find(&conversations). + Error, + "", + ) hasReadSeqs = make(map[string]int64, len(conversations)) // for _, conversation := range conversations { // hasReadSeqs[conversation.ConversationID] = conversation.HasReadSeq @@ -91,6 +191,12 @@ func (c *ConversationGorm) GetUserAllHasReadSeqs(ctx context.Context, ownerUserI return hasReadSeqs, err } -func (c *ConversationGorm) GetConversationsByConversationID(ctx context.Context, conversationIDs []string) (conversations []*relation.ConversationModel, err error) { - return conversations, utils.Wrap(c.db(ctx).Where("conversation_id IN (?)", conversationIDs).Find(&conversations).Error, "") +func (c *ConversationGorm) GetConversationsByConversationID( + ctx context.Context, + conversationIDs []string, +) (conversations []*relation.ConversationModel, err error) { + return conversations, utils.Wrap( + c.db(ctx).Where("conversation_id IN (?)", conversationIDs).Find(&conversations).Error, + "", + ) } diff --git a/pkg/common/db/relation/friend_model.go b/pkg/common/db/relation/friend_model.go index f06bd945a..3baaa50cc 100644 --- a/pkg/common/db/relation/friend_model.go +++ b/pkg/common/db/relation/friend_model.go @@ -3,9 +3,10 @@ package relation import ( "context" + "gorm.io/gorm" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "gorm.io/gorm" ) type FriendGorm struct { @@ -27,13 +28,27 @@ func (f *FriendGorm) Create(ctx context.Context, friends []*relation.FriendModel // 删除ownerUserID指定的好友 func (f *FriendGorm) Delete(ctx context.Context, ownerUserID string, friendUserIDs []string) (err error) { - err = utils.Wrap(f.db(ctx).Where("owner_user_id = ? AND friend_user_id in ( ?)", ownerUserID, friendUserIDs).Delete(&relation.FriendModel{}).Error, "") + err = utils.Wrap( + f.db(ctx). + Where("owner_user_id = ? AND friend_user_id in ( ?)", ownerUserID, friendUserIDs). + Delete(&relation.FriendModel{}). + Error, + "", + ) return err } // 更新ownerUserID单个好友信息 更新零值 -func (f *FriendGorm) UpdateByMap(ctx context.Context, ownerUserID string, friendUserID string, args map[string]interface{}) (err error) { - return utils.Wrap(f.db(ctx).Where("owner_user_id = ? AND friend_user_id = ? ", ownerUserID, friendUserID).Updates(args).Error, "") +func (f *FriendGorm) UpdateByMap( + ctx context.Context, + ownerUserID string, + friendUserID string, + args map[string]interface{}, +) (err error) { + return utils.Wrap( + f.db(ctx).Where("owner_user_id = ? AND friend_user_id = ? ", ownerUserID, friendUserID).Updates(args).Error, + "", + ) } // 更新好友信息的非零值 @@ -44,7 +59,13 @@ func (f *FriendGorm) Update(ctx context.Context, friends []*relation.FriendModel // 更新好友备注(也支持零值 ) func (f *FriendGorm) UpdateRemark(ctx context.Context, ownerUserID, friendUserID, remark string) (err error) { if remark != "" { - return utils.Wrap(f.db(ctx).Where("owner_user_id = ? and friend_user_id = ?", ownerUserID, friendUserID).Update("remark", remark).Error, "") + return utils.Wrap( + f.db(ctx). + Where("owner_user_id = ? and friend_user_id = ?", ownerUserID, friendUserID). + Update("remark", remark). + Error, + "", + ) } m := make(map[string]interface{}, 1) m["remark"] = "" @@ -52,46 +73,106 @@ func (f *FriendGorm) UpdateRemark(ctx context.Context, ownerUserID, friendUserID } // 获取单个好友信息,如没找到 返回错误 -func (f *FriendGorm) Take(ctx context.Context, ownerUserID, friendUserID string) (friend *relation.FriendModel, err error) { +func (f *FriendGorm) Take( + ctx context.Context, + ownerUserID, friendUserID string, +) (friend *relation.FriendModel, err error) { friend = &relation.FriendModel{} - return friend, utils.Wrap(f.db(ctx).Where("owner_user_id = ? and friend_user_id", ownerUserID, friendUserID).Take(friend).Error, "") + return friend, utils.Wrap( + f.db(ctx).Where("owner_user_id = ? and friend_user_id", ownerUserID, friendUserID).Take(friend).Error, + "", + ) } // 查找好友关系,如果是双向关系,则都返回 -func (f *FriendGorm) FindUserState(ctx context.Context, userID1, userID2 string) (friends []*relation.FriendModel, err error) { - return friends, utils.Wrap(f.db(ctx).Where("(owner_user_id = ? and friend_user_id = ?) or (owner_user_id = ? and friend_user_id = ?)", userID1, userID2, userID2, userID1).Find(&friends).Error, "") +func (f *FriendGorm) FindUserState( + ctx context.Context, + userID1, userID2 string, +) (friends []*relation.FriendModel, err error) { + return friends, utils.Wrap( + f.db(ctx). + Where("(owner_user_id = ? and friend_user_id = ?) or (owner_user_id = ? and friend_user_id = ?)", userID1, userID2, userID2, userID1). + Find(&friends). + Error, + "", + ) } // 获取 owner指定的好友列表 如果有friendUserIDs不存在,也不返回错误 -func (f *FriendGorm) FindFriends(ctx context.Context, ownerUserID string, friendUserIDs []string) (friends []*relation.FriendModel, err error) { - return friends, utils.Wrap(f.db(ctx).Where("owner_user_id = ? AND friend_user_id in (?)", ownerUserID, friendUserIDs).Find(&friends).Error, "") +func (f *FriendGorm) FindFriends( + ctx context.Context, + ownerUserID string, + friendUserIDs []string, +) (friends []*relation.FriendModel, err error) { + return friends, utils.Wrap( + f.db(ctx).Where("owner_user_id = ? AND friend_user_id in (?)", ownerUserID, friendUserIDs).Find(&friends).Error, + "", + ) } // 获取哪些人添加了friendUserID 如果有ownerUserIDs不存在,也不返回错误 -func (f *FriendGorm) FindReversalFriends(ctx context.Context, friendUserID string, ownerUserIDs []string) (friends []*relation.FriendModel, err error) { - return friends, utils.Wrap(f.db(ctx).Where("friend_user_id = ? AND owner_user_id in (?)", friendUserID, ownerUserIDs).Find(&friends).Error, "") +func (f *FriendGorm) FindReversalFriends( + ctx context.Context, + friendUserID string, + ownerUserIDs []string, +) (friends []*relation.FriendModel, err error) { + return friends, utils.Wrap( + f.db(ctx).Where("friend_user_id = ? AND owner_user_id in (?)", friendUserID, ownerUserIDs).Find(&friends).Error, + "", + ) } // 获取ownerUserID好友列表 支持翻页 -func (f *FriendGorm) FindOwnerFriends(ctx context.Context, ownerUserID string, pageNumber, showNumber int32) (friends []*relation.FriendModel, total int64, err error) { +func (f *FriendGorm) FindOwnerFriends( + ctx context.Context, + ownerUserID string, + pageNumber, showNumber int32, +) (friends []*relation.FriendModel, total int64, err error) { err = f.DB.Model(&relation.FriendModel{}).Where("owner_user_id = ? ", ownerUserID).Count(&total).Error if err != nil { return nil, 0, utils.Wrap(err, "") } - err = utils.Wrap(f.db(ctx).Where("owner_user_id = ? ", ownerUserID).Limit(int(showNumber)).Offset(int((pageNumber-1)*showNumber)).Find(&friends).Error, "") + err = utils.Wrap( + f.db(ctx). + Where("owner_user_id = ? ", ownerUserID). + Limit(int(showNumber)). + Offset(int((pageNumber-1)*showNumber)). + Find(&friends). + Error, + "", + ) return } // 获取哪些人添加了friendUserID 支持翻页 -func (f *FriendGorm) FindInWhoseFriends(ctx context.Context, friendUserID string, pageNumber, showNumber int32) (friends []*relation.FriendModel, total int64, err error) { +func (f *FriendGorm) FindInWhoseFriends( + ctx context.Context, + friendUserID string, + pageNumber, showNumber int32, +) (friends []*relation.FriendModel, total int64, err error) { err = f.DB.Model(&relation.FriendModel{}).Where("friend_user_id = ? ", friendUserID).Count(&total).Error if err != nil { return nil, 0, utils.Wrap(err, "") } - err = utils.Wrap(f.db(ctx).Where("friend_user_id = ? ", friendUserID).Limit(int(showNumber)).Offset(int((pageNumber-1)*showNumber)).Find(&friends).Error, "") + err = utils.Wrap( + f.db(ctx). + Where("friend_user_id = ? ", friendUserID). + Limit(int(showNumber)). + Offset(int((pageNumber-1)*showNumber)). + Find(&friends). + Error, + "", + ) return } func (f *FriendGorm) FindFriendUserIDs(ctx context.Context, ownerUserID string) (friendUserIDs []string, err error) { - return friendUserIDs, utils.Wrap(f.db(ctx).Model(&relation.FriendModel{}).Where("owner_user_id = ? ", ownerUserID).Pluck("friend_user_id", &friendUserIDs).Error, "") + return friendUserIDs, utils.Wrap( + f.db(ctx). + Model(&relation.FriendModel{}). + Where("owner_user_id = ? ", ownerUserID). + Pluck("friend_user_id", &friendUserIDs). + Error, + "", + ) } diff --git a/pkg/common/db/relation/friend_request_model.go b/pkg/common/db/relation/friend_request_model.go index 956f733a7..1673d6dcf 100644 --- a/pkg/common/db/relation/friend_request_model.go +++ b/pkg/common/db/relation/friend_request_model.go @@ -3,9 +3,10 @@ package relation import ( "context" + "gorm.io/gorm" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "gorm.io/gorm" ) type FriendRequestGorm struct { @@ -27,48 +28,108 @@ func (f *FriendRequestGorm) Create(ctx context.Context, friendRequests []*relati // 删除记录 func (f *FriendRequestGorm) Delete(ctx context.Context, fromUserID, toUserID string) (err error) { - return utils.Wrap(f.db(ctx).Where("from_user_id = ? AND to_user_id = ?", fromUserID, toUserID).Delete(&relation.FriendRequestModel{}).Error, "") + return utils.Wrap( + f.db(ctx). + Where("from_user_id = ? AND to_user_id = ?", fromUserID, toUserID). + Delete(&relation.FriendRequestModel{}). + Error, + "", + ) } // 更新零值 -func (f *FriendRequestGorm) UpdateByMap(ctx context.Context, fromUserID string, toUserID string, args map[string]interface{}) (err error) { - return utils.Wrap(f.db(ctx).Model(&relation.FriendRequestModel{}).Where("from_user_id = ? AND to_user_id =?", fromUserID, toUserID).Updates(args).Error, "") +func (f *FriendRequestGorm) UpdateByMap( + ctx context.Context, + fromUserID string, + toUserID string, + args map[string]interface{}, +) (err error) { + return utils.Wrap( + f.db(ctx). + Model(&relation.FriendRequestModel{}). + Where("from_user_id = ? AND to_user_id =?", fromUserID, toUserID). + Updates(args). + Error, + "", + ) } // 更新记录 (非零值) func (f *FriendRequestGorm) Update(ctx context.Context, friendRequest *relation.FriendRequestModel) (err error) { - return utils.Wrap(f.db(ctx).Where("from_user_id = ? AND to_user_id =?", friendRequest.FromUserID, friendRequest.ToUserID).Updates(friendRequest).Error, "") + return utils.Wrap( + f.db(ctx). + Where("from_user_id = ? AND to_user_id =?", friendRequest.FromUserID, friendRequest.ToUserID). + Updates(friendRequest). + Error, + "", + ) } // 获取来指定用户的好友申请 未找到 不返回错误 -func (f *FriendRequestGorm) Find(ctx context.Context, fromUserID, toUserID string) (friendRequest *relation.FriendRequestModel, err error) { +func (f *FriendRequestGorm) Find( + ctx context.Context, + fromUserID, toUserID string, +) (friendRequest *relation.FriendRequestModel, err error) { friendRequest = &relation.FriendRequestModel{} - err = utils.Wrap(f.db(ctx).Where("from_user_id = ? and to_user_id = ?", fromUserID, toUserID).Find(friendRequest).Error, "") + err = utils.Wrap( + f.db(ctx).Where("from_user_id = ? and to_user_id = ?", fromUserID, toUserID).Find(friendRequest).Error, + "", + ) return friendRequest, err } -func (f *FriendRequestGorm) Take(ctx context.Context, fromUserID, toUserID string) (friendRequest *relation.FriendRequestModel, err error) { +func (f *FriendRequestGorm) Take( + ctx context.Context, + fromUserID, toUserID string, +) (friendRequest *relation.FriendRequestModel, err error) { friendRequest = &relation.FriendRequestModel{} - err = utils.Wrap(f.db(ctx).Where("from_user_id = ? and to_user_id = ?", fromUserID, toUserID).Take(friendRequest).Error, "") + err = utils.Wrap( + f.db(ctx).Where("from_user_id = ? and to_user_id = ?", fromUserID, toUserID).Take(friendRequest).Error, + "", + ) return friendRequest, err } // 获取toUserID收到的好友申请列表 -func (f *FriendRequestGorm) FindToUserID(ctx context.Context, toUserID string, pageNumber, showNumber int32) (friendRequests []*relation.FriendRequestModel, total int64, err error) { +func (f *FriendRequestGorm) FindToUserID( + ctx context.Context, + toUserID string, + pageNumber, showNumber int32, +) (friendRequests []*relation.FriendRequestModel, total int64, err error) { err = f.db(ctx).Model(&relation.FriendRequestModel{}).Where("to_user_id = ? ", toUserID).Count(&total).Error if err != nil { return nil, 0, utils.Wrap(err, "") } - err = utils.Wrap(f.db(ctx).Where("to_user_id = ? ", toUserID).Limit(int(showNumber)).Offset(int(pageNumber-1)*int(showNumber)).Find(&friendRequests).Error, "") + err = utils.Wrap( + f.db(ctx). + Where("to_user_id = ? ", toUserID). + Limit(int(showNumber)). + Offset(int(pageNumber-1)*int(showNumber)). + Find(&friendRequests). + Error, + "", + ) return } // 获取fromUserID发出去的好友申请列表 -func (f *FriendRequestGorm) FindFromUserID(ctx context.Context, fromUserID string, pageNumber, showNumber int32) (friendRequests []*relation.FriendRequestModel, total int64, err error) { +func (f *FriendRequestGorm) FindFromUserID( + ctx context.Context, + fromUserID string, + pageNumber, showNumber int32, +) (friendRequests []*relation.FriendRequestModel, total int64, err error) { err = f.db(ctx).Model(&relation.FriendRequestModel{}).Where("from_user_id = ? ", fromUserID).Count(&total).Error if err != nil { return nil, 0, utils.Wrap(err, "") } - err = utils.Wrap(f.db(ctx).Where("from_user_id = ? ", fromUserID).Limit(int(showNumber)).Offset(int(pageNumber-1)*int(showNumber)).Find(&friendRequests).Error, "") + err = utils.Wrap( + f.db(ctx). + Where("from_user_id = ? ", fromUserID). + Limit(int(showNumber)). + Offset(int(pageNumber-1)*int(showNumber)). + Find(&friendRequests). + Error, + "", + ) return } diff --git a/pkg/common/db/relation/group_member_model.go b/pkg/common/db/relation/group_member_model.go index 5cbbdf2b3..78ab4357c 100644 --- a/pkg/common/db/relation/group_member_model.go +++ b/pkg/common/db/relation/group_member_model.go @@ -3,11 +3,12 @@ package relation import ( "context" + "gorm.io/gorm" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/ormutil" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "gorm.io/gorm" ) var _ relation.GroupMemberModelInterface = (*GroupMemberGorm)(nil) @@ -29,7 +30,10 @@ func (g *GroupMemberGorm) Create(ctx context.Context, groupMemberList []*relatio } func (g *GroupMemberGorm) Delete(ctx context.Context, groupID string, userIDs []string) (err error) { - return utils.Wrap(g.db(ctx).Where("group_id = ? and user_id in (?)", groupID, userIDs).Delete(&relation.GroupMemberModel{}).Error, "") + return utils.Wrap( + g.db(ctx).Where("group_id = ? and user_id in (?)", groupID, userIDs).Delete(&relation.GroupMemberModel{}).Error, + "", + ) } func (g *GroupMemberGorm) DeleteGroup(ctx context.Context, groupIDs []string) (err error) { @@ -40,14 +44,24 @@ func (g *GroupMemberGorm) Update(ctx context.Context, groupID string, userID str return utils.Wrap(g.db(ctx).Where("group_id = ? and user_id = ?", groupID, userID).Updates(data).Error, "") } -func (g *GroupMemberGorm) UpdateRoleLevel(ctx context.Context, groupID string, userID string, roleLevel int32) (rowsAffected int64, err error) { +func (g *GroupMemberGorm) UpdateRoleLevel( + ctx context.Context, + groupID string, + userID string, + roleLevel int32, +) (rowsAffected int64, err error) { db := g.db(ctx).Where("group_id = ? and user_id = ?", groupID, userID).Updates(map[string]any{ "role_level": roleLevel, }) return db.RowsAffected, utils.Wrap(db.Error, "") } -func (g *GroupMemberGorm) Find(ctx context.Context, groupIDs []string, userIDs []string, roleLevels []int32) (groupMembers []*relation.GroupMemberModel, err error) { +func (g *GroupMemberGorm) Find( + ctx context.Context, + groupIDs []string, + userIDs []string, + roleLevels []int32, +) (groupMembers []*relation.GroupMemberModel, err error) { db := g.db(ctx) if len(groupIDs) > 0 { db = db.Where("group_id in (?)", groupIDs) @@ -61,17 +75,37 @@ func (g *GroupMemberGorm) Find(ctx context.Context, groupIDs []string, userIDs [ return groupMembers, utils.Wrap(db.Find(&groupMembers).Error, "") } -func (g *GroupMemberGorm) Take(ctx context.Context, groupID string, userID string) (groupMember *relation.GroupMemberModel, err error) { +func (g *GroupMemberGorm) Take( + ctx context.Context, + groupID string, + userID string, +) (groupMember *relation.GroupMemberModel, err error) { groupMember = &relation.GroupMemberModel{} - return groupMember, utils.Wrap(g.db(ctx).Where("group_id = ? and user_id = ?", groupID, userID).Take(groupMember).Error, "") + return groupMember, utils.Wrap( + g.db(ctx).Where("group_id = ? and user_id = ?", groupID, userID).Take(groupMember).Error, + "", + ) } -func (g *GroupMemberGorm) TakeOwner(ctx context.Context, groupID string) (groupMember *relation.GroupMemberModel, err error) { +func (g *GroupMemberGorm) TakeOwner( + ctx context.Context, + groupID string, +) (groupMember *relation.GroupMemberModel, err error) { groupMember = &relation.GroupMemberModel{} - return groupMember, utils.Wrap(g.db(ctx).Where("group_id = ? and role_level = ?", groupID, constant.GroupOwner).Take(groupMember).Error, "") -} - -func (g *GroupMemberGorm) SearchMember(ctx context.Context, keyword string, groupIDs []string, userIDs []string, roleLevels []int32, pageNumber, showNumber int32) (total uint32, groupList []*relation.GroupMemberModel, err error) { + return groupMember, utils.Wrap( + g.db(ctx).Where("group_id = ? and role_level = ?", groupID, constant.GroupOwner).Take(groupMember).Error, + "", + ) +} + +func (g *GroupMemberGorm) SearchMember( + ctx context.Context, + keyword string, + groupIDs []string, + userIDs []string, + roleLevels []int32, + pageNumber, showNumber int32, +) (total uint32, groupList []*relation.GroupMemberModel, err error) { db := g.db(ctx) ormutil.GormIn(&db, "group_id", groupIDs) ormutil.GormIn(&db, "user_id", userIDs) @@ -79,11 +113,17 @@ func (g *GroupMemberGorm) SearchMember(ctx context.Context, keyword string, grou return ormutil.GormSearch[relation.GroupMemberModel](db, []string{"nickname"}, keyword, pageNumber, showNumber) } -func (g *GroupMemberGorm) MapGroupMemberNum(ctx context.Context, groupIDs []string) (count map[string]uint32, err error) { +func (g *GroupMemberGorm) MapGroupMemberNum( + ctx context.Context, + groupIDs []string, +) (count map[string]uint32, err error) { return ormutil.MapCount(g.db(ctx).Where("group_id in (?)", groupIDs), "group_id") } -func (g *GroupMemberGorm) FindJoinUserID(ctx context.Context, groupIDs []string) (groupUsers map[string][]string, err error) { +func (g *GroupMemberGorm) FindJoinUserID( + ctx context.Context, + groupIDs []string, +) (groupUsers map[string][]string, err error) { var groupMembers []*relation.GroupMemberModel if err := g.db(ctx).Select("group_id, user_id").Where("group_id in (?)", groupIDs).Find(&groupMembers).Error; err != nil { return nil, utils.Wrap(err, "") @@ -131,5 +171,12 @@ func (g *GroupMemberGorm) FindUsersJoinedGroupID(ctx context.Context, userIDs [] } func (g *GroupMemberGorm) FindUserManagedGroupID(ctx context.Context, userID string) (groupIDs []string, err error) { - return groupIDs, utils.Wrap(g.db(ctx).Model(&relation.GroupMemberModel{}).Where("user_id = ? and (role_level = ? or role_level = ?)", userID, constant.GroupOwner, constant.GroupAdmin).Pluck("group_id", &groupIDs).Error, "") + return groupIDs, utils.Wrap( + g.db(ctx). + Model(&relation.GroupMemberModel{}). + Where("user_id = ? and (role_level = ? or role_level = ?)", userID, constant.GroupOwner, constant.GroupAdmin). + Pluck("group_id", &groupIDs). + Error, + "", + ) } diff --git a/pkg/common/db/relation/group_model.go b/pkg/common/db/relation/group_model.go index dd9f59bea..fbcb5763b 100644 --- a/pkg/common/db/relation/group_model.go +++ b/pkg/common/db/relation/group_model.go @@ -3,10 +3,11 @@ package relation import ( "context" + "gorm.io/gorm" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/ormutil" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "gorm.io/gorm" ) var _ relation.GroupModelInterface = (*GroupGorm)(nil) @@ -32,7 +33,13 @@ func (g *GroupGorm) UpdateMap(ctx context.Context, groupID string, args map[stri } func (g *GroupGorm) UpdateStatus(ctx context.Context, groupID string, status int32) (err error) { - return utils.Wrap(g.DB.Where("group_id = ?", groupID).Model(&relation.GroupModel{}).Updates(map[string]any{"status": status}).Error, "") + return utils.Wrap( + g.DB.Where("group_id = ?", groupID). + Model(&relation.GroupModel{}). + Updates(map[string]any{"status": status}). + Error, + "", + ) } func (g *GroupGorm) Find(ctx context.Context, groupIDs []string) (groups []*relation.GroupModel, err error) { @@ -44,10 +51,17 @@ func (g *GroupGorm) Take(ctx context.Context, groupID string) (group *relation.G return group, utils.Wrap(g.DB.Where("group_id = ?", groupID).Take(group).Error, "") } -func (g *GroupGorm) Search(ctx context.Context, keyword string, pageNumber, showNumber int32) (total uint32, groups []*relation.GroupModel, err error) { +func (g *GroupGorm) Search( + ctx context.Context, + keyword string, + pageNumber, showNumber int32, +) (total uint32, groups []*relation.GroupModel, err error) { return ormutil.GormSearch[relation.GroupModel](g.DB, []string{"name"}, keyword, pageNumber, showNumber) } func (g *GroupGorm) GetGroupIDsByGroupType(ctx context.Context, groupType int) (groupIDs []string, err error) { - return groupIDs, utils.Wrap(g.DB.Model(&relation.GroupModel{}).Where("group_type = ? ", groupType).Pluck("group_id", &groupIDs).Error, "") + return groupIDs, utils.Wrap( + g.DB.Model(&relation.GroupModel{}).Where("group_type = ? ", groupType).Pluck("group_id", &groupIDs).Error, + "", + ) } diff --git a/pkg/common/db/relation/group_request_model.go b/pkg/common/db/relation/group_request_model.go index 407c2b136..9d12005ae 100644 --- a/pkg/common/db/relation/group_request_model.go +++ b/pkg/common/db/relation/group_request_model.go @@ -5,9 +5,10 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/ormutil" + "gorm.io/gorm" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "gorm.io/gorm" ) type GroupRequestGorm struct { @@ -29,25 +30,69 @@ func (g *GroupRequestGorm) Create(ctx context.Context, groupRequests []*relation } func (g *GroupRequestGorm) Delete(ctx context.Context, groupID string, userID string) (err error) { - return utils.Wrap(g.DB.WithContext(ctx).Where("group_id = ? and user_id = ? ", groupID, userID).Delete(&relation.GroupRequestModel{}).Error, utils.GetSelfFuncName()) + return utils.Wrap( + g.DB.WithContext(ctx). + Where("group_id = ? and user_id = ? ", groupID, userID). + Delete(&relation.GroupRequestModel{}). + Error, + utils.GetSelfFuncName(), + ) } -func (g *GroupRequestGorm) UpdateHandler(ctx context.Context, groupID string, userID string, handledMsg string, handleResult int32) (err error) { - return utils.Wrap(g.DB.WithContext(ctx).Model(&relation.GroupRequestModel{}).Where("group_id = ? and user_id = ? ", groupID, userID).Updates(map[string]any{ - "handle_msg": handledMsg, - "handle_result": handleResult, - }).Error, utils.GetSelfFuncName()) +func (g *GroupRequestGorm) UpdateHandler( + ctx context.Context, + groupID string, + userID string, + handledMsg string, + handleResult int32, +) (err error) { + return utils.Wrap( + g.DB.WithContext(ctx). + Model(&relation.GroupRequestModel{}). + Where("group_id = ? and user_id = ? ", groupID, userID). + Updates(map[string]any{ + "handle_msg": handledMsg, + "handle_result": handleResult, + }). + Error, + utils.GetSelfFuncName(), + ) } -func (g *GroupRequestGorm) Take(ctx context.Context, groupID string, userID string) (groupRequest *relation.GroupRequestModel, err error) { +func (g *GroupRequestGorm) Take( + ctx context.Context, + groupID string, + userID string, +) (groupRequest *relation.GroupRequestModel, err error) { groupRequest = &relation.GroupRequestModel{} - return groupRequest, utils.Wrap(g.DB.WithContext(ctx).Where("group_id = ? and user_id = ? ", groupID, userID).Take(groupRequest).Error, utils.GetSelfFuncName()) + return groupRequest, utils.Wrap( + g.DB.WithContext(ctx).Where("group_id = ? and user_id = ? ", groupID, userID).Take(groupRequest).Error, + utils.GetSelfFuncName(), + ) } -func (g *GroupRequestGorm) Page(ctx context.Context, userID string, pageNumber, showNumber int32) (total uint32, groups []*relation.GroupRequestModel, err error) { - return ormutil.GormSearch[relation.GroupRequestModel](g.DB.WithContext(ctx).Where("user_id = ?", userID), nil, "", pageNumber, showNumber) +func (g *GroupRequestGorm) Page( + ctx context.Context, + userID string, + pageNumber, showNumber int32, +) (total uint32, groups []*relation.GroupRequestModel, err error) { + return ormutil.GormSearch[relation.GroupRequestModel]( + g.DB.WithContext(ctx).Where("user_id = ?", userID), + nil, + "", + pageNumber, + showNumber, + ) } -func (g *GroupRequestGorm) PageGroup(ctx context.Context, groupIDs []string, pageNumber, showNumber int32) (total uint32, groups []*relation.GroupRequestModel, err error) { - return ormutil.GormPage[relation.GroupRequestModel](g.DB.WithContext(ctx).Where("group_id in ?", groupIDs), pageNumber, showNumber) +func (g *GroupRequestGorm) PageGroup( + ctx context.Context, + groupIDs []string, + pageNumber, showNumber int32, +) (total uint32, groups []*relation.GroupRequestModel, err error) { + return ormutil.GormPage[relation.GroupRequestModel]( + g.DB.WithContext(ctx).Where("group_id in ?", groupIDs), + pageNumber, + showNumber, + ) } diff --git a/pkg/common/db/relation/mysql_init.go b/pkg/common/db/relation/mysql_init.go index 432af214f..89ee8545e 100644 --- a/pkg/common/db/relation/mysql_init.go +++ b/pkg/common/db/relation/mysql_init.go @@ -4,12 +4,13 @@ import ( "fmt" "time" + mysqlDriver "github.com/go-sql-driver/mysql" + "gorm.io/driver/mysql" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mw/specialerror" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" - mysqlDriver "github.com/go-sql-driver/mysql" - "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" @@ -31,14 +32,26 @@ func newMysqlGormDB() (*gorm.DB, error) { return nil, err } defer sqlDB.Close() - sql := fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s default charset utf8mb4 COLLATE utf8mb4_unicode_ci;", config.Config.Mysql.Database) + sql := fmt.Sprintf( + "CREATE DATABASE IF NOT EXISTS %s default charset utf8mb4 COLLATE utf8mb4_unicode_ci;", + config.Config.Mysql.Database, + ) err = db.Exec(sql).Error if err != nil { return nil, fmt.Errorf("init db %w", err) } - dsn = fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=true&loc=Local", - config.Config.Mysql.Username, config.Config.Mysql.Password, config.Config.Mysql.Address[0], config.Config.Mysql.Database) - sqlLogger := log.NewSqlLogger(logger.LogLevel(config.Config.Mysql.LogLevel), true, time.Duration(config.Config.Mysql.SlowThreshold)*time.Millisecond) + dsn = fmt.Sprintf( + "%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=true&loc=Local", + config.Config.Mysql.Username, + config.Config.Mysql.Password, + config.Config.Mysql.Address[0], + config.Config.Mysql.Database, + ) + sqlLogger := log.NewSqlLogger( + logger.LogLevel(config.Config.Mysql.LogLevel), + true, + time.Duration(config.Config.Mysql.SlowThreshold)*time.Millisecond, + ) db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{ Logger: sqlLogger, }) diff --git a/pkg/common/db/relation/object_hash_model.go b/pkg/common/db/relation/object_hash_model.go index 275d09a4e..789929c5f 100644 --- a/pkg/common/db/relation/object_hash_model.go +++ b/pkg/common/db/relation/object_hash_model.go @@ -3,9 +3,10 @@ package relation import ( "context" + "gorm.io/gorm" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "gorm.io/gorm" ) type ObjectHashGorm struct { @@ -24,7 +25,11 @@ func (o *ObjectHashGorm) NewTx(tx any) relation.ObjectHashModelInterface { } } -func (o *ObjectHashGorm) Take(ctx context.Context, hash string, engine string) (oh *relation.ObjectHashModel, err error) { +func (o *ObjectHashGorm) Take( + ctx context.Context, + hash string, + engine string, +) (oh *relation.ObjectHashModel, err error) { oh = &relation.ObjectHashModel{} return oh, utils.Wrap1(o.DB.Where("hash = ? and engine = ?", hash, engine).Take(oh).Error) } @@ -33,7 +38,11 @@ func (o *ObjectHashGorm) Create(ctx context.Context, h []*relation.ObjectHashMod return utils.Wrap1(o.DB.Create(h).Error) } -func (o *ObjectHashGorm) DeleteNoCitation(ctx context.Context, engine string, num int) (list []*relation.ObjectHashModel, err error) { +func (o *ObjectHashGorm) DeleteNoCitation( + ctx context.Context, + engine string, + num int, +) (list []*relation.ObjectHashModel, err error) { err = o.DB.Table(relation.ObjectHashModelTableName, "as h").Select("h.*"). Joins("LEFT JOIN "+relation.ObjectInfoModelTableName+" as i ON h.hash = i.hash"). Where("h.engine = ? AND i.hash IS NULL", engine). diff --git a/pkg/common/db/relation/object_info_model.go b/pkg/common/db/relation/object_info_model.go index f5b34755f..aa48ae491 100644 --- a/pkg/common/db/relation/object_info_model.go +++ b/pkg/common/db/relation/object_info_model.go @@ -4,10 +4,11 @@ import ( "context" "time" + "gorm.io/gorm" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "gorm.io/gorm" ) type ObjectInfoGorm struct { @@ -45,5 +46,10 @@ func (o *ObjectInfoGorm) Take(ctx context.Context, name string) (info *relation. } func (o *ObjectInfoGorm) DeleteExpiration(ctx context.Context, expiration time.Time) (err error) { - return utils.Wrap1(o.DB.WithContext(ctx).Where("expiration_time IS NOT NULL AND expiration_time <= ?", expiration).Delete(&relation.ObjectInfoModel{}).Error) + return utils.Wrap1( + o.DB.WithContext(ctx). + Where("expiration_time IS NOT NULL AND expiration_time <= ?", expiration). + Delete(&relation.ObjectInfoModel{}). + Error, + ) } diff --git a/pkg/common/db/relation/object_put_model.go b/pkg/common/db/relation/object_put_model.go index 06a9883af..12808c215 100644 --- a/pkg/common/db/relation/object_put_model.go +++ b/pkg/common/db/relation/object_put_model.go @@ -4,9 +4,10 @@ import ( "context" "time" + "gorm.io/gorm" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "gorm.io/gorm" ) type ObjectPutGorm struct { @@ -38,7 +39,11 @@ func (o *ObjectPutGorm) SetCompleted(ctx context.Context, putID string) (err err return utils.Wrap1(o.DB.Model(&relation.ObjectPutModel{}).Where("put_id = ?", putID).Update("complete", true).Error) } -func (o *ObjectPutGorm) FindExpirationPut(ctx context.Context, expirationTime time.Time, num int) (list []*relation.ObjectPutModel, err error) { +func (o *ObjectPutGorm) FindExpirationPut( + ctx context.Context, + expirationTime time.Time, + num int, +) (list []*relation.ObjectPutModel, err error) { err = o.DB.Where("effective_time <= ?", expirationTime).Limit(num).Find(&list).Error return list, utils.Wrap1(err) } diff --git a/pkg/common/db/relation/user_model.go b/pkg/common/db/relation/user_model.go index 481393ac0..013666859 100644 --- a/pkg/common/db/relation/user_model.go +++ b/pkg/common/db/relation/user_model.go @@ -6,9 +6,10 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" + "gorm.io/gorm" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "gorm.io/gorm" ) type UserGorm struct { @@ -48,12 +49,23 @@ func (u *UserGorm) Take(ctx context.Context, userID string) (user *relation.User } // 获取用户信息 不存在,不返回错误 -func (u *UserGorm) Page(ctx context.Context, pageNumber, showNumber int32) (users []*relation.UserModel, count int64, err error) { +func (u *UserGorm) Page( + ctx context.Context, + pageNumber, showNumber int32, +) (users []*relation.UserModel, count int64, err error) { err = utils.Wrap(u.db(ctx).Count(&count).Error, "") if err != nil { return } - err = utils.Wrap(u.db(ctx).Limit(int(showNumber)).Offset(int((pageNumber-1)*showNumber)).Find(&users).Order("create_time DESC").Error, "") + err = utils.Wrap( + u.db(ctx). + Limit(int(showNumber)). + Offset(int((pageNumber-1)*showNumber)). + Find(&users). + Order("create_time DESC"). + Error, + "", + ) return } @@ -73,12 +85,22 @@ func (u *UserGorm) CountTotal(ctx context.Context) (count int64, err error) { return count, errs.Wrap(err) } -func (u *UserGorm) CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) { +func (u *UserGorm) CountRangeEverydayTotal( + ctx context.Context, + start time.Time, + end time.Time, +) (map[string]int64, error) { var res []struct { Date time.Time `gorm:"column:date"` Count int64 `gorm:"column:count"` } - err := u.db(ctx).Model(&relation.UserModel{}).Select("DATE(create_time) AS date, count(1) AS count").Where("create_time >= ? and create_time < ?", start, end).Group("date").Find(&res).Error + err := u.db(ctx). + Model(&relation.UserModel{}). + Select("DATE(create_time) AS date, count(1) AS count"). + Where("create_time >= ? and create_time < ?", start, end). + Group("date"). + Find(&res). + Error if err != nil { return nil, errs.Wrap(err) } diff --git a/pkg/common/db/table/relation/black.go b/pkg/common/db/table/relation/black.go index fd25cff7f..bcfffc6db 100644 --- a/pkg/common/db/table/relation/black.go +++ b/pkg/common/db/table/relation/black.go @@ -29,6 +29,10 @@ type BlackModelInterface interface { Update(ctx context.Context, blacks []*BlackModel) (err error) Find(ctx context.Context, blacks []*BlackModel) (blackList []*BlackModel, err error) Take(ctx context.Context, ownerUserID, blockUserID string) (black *BlackModel, err error) - FindOwnerBlacks(ctx context.Context, ownerUserID string, pageNumber, showNumber int32) (blacks []*BlackModel, total int64, err error) + FindOwnerBlacks( + ctx context.Context, + ownerUserID string, + pageNumber, showNumber int32, + ) (blacks []*BlackModel, total int64, err error) FindBlackUserIDs(ctx context.Context, ownerUserID string) (blackUserIDs []string, err error) } diff --git a/pkg/common/db/table/relation/chatlog.go b/pkg/common/db/table/relation/chatlog.go index 8f34f2854..f43d8a43a 100644 --- a/pkg/common/db/table/relation/chatlog.go +++ b/pkg/common/db/table/relation/chatlog.go @@ -11,21 +11,21 @@ const ( ) type ChatLogModel struct { - ServerMsgID string `gorm:"column:server_msg_id;primary_key;type:char(64)" json:"serverMsgID"` - ClientMsgID string `gorm:"column:client_msg_id;type:char(64)" json:"clientMsgID"` - SendID string `gorm:"column:send_id;type:char(64);index:send_id,priority:2" json:"sendID"` - RecvID string `gorm:"column:recv_id;type:char(64);index:recv_id,priority:2" json:"recvID"` - SenderPlatformID int32 `gorm:"column:sender_platform_id" json:"senderPlatformID"` - SenderNickname string `gorm:"column:sender_nick_name;type:varchar(255)" json:"senderNickname"` - SenderFaceURL string `gorm:"column:sender_face_url;type:varchar(255);" json:"senderFaceURL"` - SessionType int32 `gorm:"column:session_type;index:session_type,priority:2;index:session_type_alone" json:"sessionType"` - MsgFrom int32 `gorm:"column:msg_from" json:"msgFrom"` - ContentType int32 `gorm:"column:content_type;index:content_type,priority:2;index:content_type_alone" json:"contentType"` - Content string `gorm:"column:content;type:varchar(3000)" json:"content"` - Status int32 `gorm:"column:status" json:"status"` + ServerMsgID string `gorm:"column:server_msg_id;primary_key;type:char(64)" json:"serverMsgID"` + ClientMsgID string `gorm:"column:client_msg_id;type:char(64)" json:"clientMsgID"` + SendID string `gorm:"column:send_id;type:char(64);index:send_id,priority:2" json:"sendID"` + RecvID string `gorm:"column:recv_id;type:char(64);index:recv_id,priority:2" json:"recvID"` + SenderPlatformID int32 `gorm:"column:sender_platform_id" json:"senderPlatformID"` + SenderNickname string `gorm:"column:sender_nick_name;type:varchar(255)" json:"senderNickname"` + SenderFaceURL string `gorm:"column:sender_face_url;type:varchar(255);" json:"senderFaceURL"` + SessionType int32 `gorm:"column:session_type;index:session_type,priority:2;index:session_type_alone" json:"sessionType"` + MsgFrom int32 `gorm:"column:msg_from" json:"msgFrom"` + ContentType int32 `gorm:"column:content_type;index:content_type,priority:2;index:content_type_alone" json:"contentType"` + Content string `gorm:"column:content;type:varchar(3000)" json:"content"` + Status int32 `gorm:"column:status" json:"status"` SendTime time.Time `gorm:"column:send_time;index:sendTime;index:content_type,priority:1;index:session_type,priority:1;index:recv_id,priority:1;index:send_id,priority:1" json:"sendTime"` - CreateTime time.Time `gorm:"column:create_time" json:"createTime"` - Ex string `gorm:"column:ex;type:varchar(1024)" json:"ex"` + CreateTime time.Time `gorm:"column:create_time" json:"createTime"` + Ex string `gorm:"column:ex;type:varchar(1024)" json:"ex"` } func (ChatLogModel) TableName() string { diff --git a/pkg/common/db/table/relation/conversation.go b/pkg/common/db/table/relation/conversation.go index ae009a1a1..f08ea90e3 100644 --- a/pkg/common/db/table/relation/conversation.go +++ b/pkg/common/db/table/relation/conversation.go @@ -7,20 +7,20 @@ const ( ) type ConversationModel struct { - OwnerUserID string `gorm:"column:owner_user_id;primary_key;type:char(128)" json:"OwnerUserID"` + OwnerUserID string `gorm:"column:owner_user_id;primary_key;type:char(128)" json:"OwnerUserID"` ConversationID string `gorm:"column:conversation_id;primary_key;type:char(128)" json:"conversationID"` - ConversationType int32 `gorm:"column:conversation_type" json:"conversationType"` - UserID string `gorm:"column:user_id;type:char(64)" json:"userID"` - GroupID string `gorm:"column:group_id;type:char(128)" json:"groupID"` - RecvMsgOpt int32 `gorm:"column:recv_msg_opt" json:"recvMsgOpt"` - IsPinned bool `gorm:"column:is_pinned" json:"isPinned"` - IsPrivateChat bool `gorm:"column:is_private_chat" json:"isPrivateChat"` - BurnDuration int32 `gorm:"column:burn_duration;default:30" json:"burnDuration"` - GroupAtType int32 `gorm:"column:group_at_type" json:"groupAtType"` - AttachedInfo string `gorm:"column:attached_info;type:varchar(1024)" json:"attachedInfo"` - Ex string `gorm:"column:ex;type:varchar(1024)" json:"ex"` - MaxSeq int64 `gorm:"column:max_seq" json:"maxSeq"` - MinSeq int64 `gorm:"column:min_seq" json:"minSeq"` + ConversationType int32 `gorm:"column:conversation_type" json:"conversationType"` + UserID string `gorm:"column:user_id;type:char(64)" json:"userID"` + GroupID string `gorm:"column:group_id;type:char(128)" json:"groupID"` + RecvMsgOpt int32 `gorm:"column:recv_msg_opt" json:"recvMsgOpt"` + IsPinned bool `gorm:"column:is_pinned" json:"isPinned"` + IsPrivateChat bool `gorm:"column:is_private_chat" json:"isPrivateChat"` + BurnDuration int32 `gorm:"column:burn_duration;default:30" json:"burnDuration"` + GroupAtType int32 `gorm:"column:group_at_type" json:"groupAtType"` + AttachedInfo string `gorm:"column:attached_info;type:varchar(1024)" json:"attachedInfo"` + Ex string `gorm:"column:ex;type:varchar(1024)" json:"ex"` + MaxSeq int64 `gorm:"column:max_seq" json:"maxSeq"` + MinSeq int64 `gorm:"column:min_seq" json:"minSeq"` } func (ConversationModel) TableName() string { @@ -30,13 +30,26 @@ func (ConversationModel) TableName() string { type ConversationModelInterface interface { Create(ctx context.Context, conversations []*ConversationModel) (err error) Delete(ctx context.Context, groupIDs []string) (err error) - UpdateByMap(ctx context.Context, userIDs []string, conversationID string, args map[string]interface{}) (rows int64, err error) + UpdateByMap( + ctx context.Context, + userIDs []string, + conversationID string, + args map[string]interface{}, + ) (rows int64, err error) Update(ctx context.Context, conversation *ConversationModel) (err error) - Find(ctx context.Context, ownerUserID string, conversationIDs []string) (conversations []*ConversationModel, err error) + Find( + ctx context.Context, + ownerUserID string, + conversationIDs []string, + ) (conversations []*ConversationModel, err error) FindUserID(ctx context.Context, userIDs []string, conversationIDs []string) ([]string, error) FindUserIDAllConversationID(ctx context.Context, userID string) ([]string, error) Take(ctx context.Context, userID, conversationID string) (conversation *ConversationModel, err error) - FindConversationID(ctx context.Context, userID string, conversationIDs []string) (existConversationID []string, err error) + FindConversationID( + ctx context.Context, + userID string, + conversationIDs []string, + ) (existConversationID []string, err error) FindUserIDAllConversations(ctx context.Context, userID string) (conversations []*ConversationModel, err error) FindRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) ([]string, error) GetUserRecvMsgOpt(ctx context.Context, ownerUserID, conversationID string) (opt int, err error) diff --git a/pkg/common/db/table/relation/friend.go b/pkg/common/db/table/relation/friend.go index af85d55d8..43724141f 100644 --- a/pkg/common/db/table/relation/friend.go +++ b/pkg/common/db/table/relation/friend.go @@ -41,11 +41,23 @@ type FriendModelInterface interface { // 获取 owner指定的好友列表 如果有friendUserIDs不存在,也不返回错误 FindFriends(ctx context.Context, ownerUserID string, friendUserIDs []string) (friends []*FriendModel, err error) // 获取哪些人添加了friendUserID 如果有ownerUserIDs不存在,也不返回错误 - FindReversalFriends(ctx context.Context, friendUserID string, ownerUserIDs []string) (friends []*FriendModel, err error) + FindReversalFriends( + ctx context.Context, + friendUserID string, + ownerUserIDs []string, + ) (friends []*FriendModel, err error) // 获取ownerUserID好友列表 支持翻页 - FindOwnerFriends(ctx context.Context, ownerUserID string, pageNumber, showNumber int32) (friends []*FriendModel, total int64, err error) + FindOwnerFriends( + ctx context.Context, + ownerUserID string, + pageNumber, showNumber int32, + ) (friends []*FriendModel, total int64, err error) // 获取哪些人添加了friendUserID 支持翻页 - FindInWhoseFriends(ctx context.Context, friendUserID string, pageNumber, showNumber int32) (friends []*FriendModel, total int64, err error) + FindInWhoseFriends( + ctx context.Context, + friendUserID string, + pageNumber, showNumber int32, + ) (friends []*FriendModel, total int64, err error) // 获取好友UserID列表 FindFriendUserIDs(ctx context.Context, ownerUserID string) (friendUserIDs []string, err error) NewTx(tx any) FriendModelInterface diff --git a/pkg/common/db/table/relation/friend_request.go b/pkg/common/db/table/relation/friend_request.go index b14ecb2c2..59160c819 100644 --- a/pkg/common/db/table/relation/friend_request.go +++ b/pkg/common/db/table/relation/friend_request.go @@ -36,9 +36,17 @@ type FriendRequestModelInterface interface { Find(ctx context.Context, fromUserID, toUserID string) (friendRequest *FriendRequestModel, err error) Take(ctx context.Context, fromUserID, toUserID string) (friendRequest *FriendRequestModel, err error) // 获取toUserID收到的好友申请列表 - FindToUserID(ctx context.Context, toUserID string, pageNumber, showNumber int32) (friendRequests []*FriendRequestModel, total int64, err error) + FindToUserID( + ctx context.Context, + toUserID string, + pageNumber, showNumber int32, + ) (friendRequests []*FriendRequestModel, total int64, err error) // 获取fromUserID发出去的好友申请列表 - FindFromUserID(ctx context.Context, fromUserID string, pageNumber, showNumber int32) (friendRequests []*FriendRequestModel, total int64, err error) + FindFromUserID( + ctx context.Context, + fromUserID string, + pageNumber, showNumber int32, + ) (friendRequests []*FriendRequestModel, total int64, err error) NewTx(tx any) FriendRequestModelInterface } diff --git a/pkg/common/db/table/relation/group.go b/pkg/common/db/table/relation/group.go index c011699ae..70ccad9b4 100644 --- a/pkg/common/db/table/relation/group.go +++ b/pkg/common/db/table/relation/group.go @@ -10,19 +10,19 @@ const ( ) type GroupModel struct { - GroupID string `gorm:"column:group_id;primary_key;size:64" json:"groupID" binding:"required"` - GroupName string `gorm:"column:name;size:255" json:"groupName"` - Notification string `gorm:"column:notification;size:255" json:"notification"` - Introduction string `gorm:"column:introduction;size:255" json:"introduction"` - FaceURL string `gorm:"column:face_url;size:255" json:"faceURL"` + GroupID string `gorm:"column:group_id;primary_key;size:64" json:"groupID" binding:"required"` + GroupName string `gorm:"column:name;size:255" json:"groupName"` + Notification string `gorm:"column:notification;size:255" json:"notification"` + Introduction string `gorm:"column:introduction;size:255" json:"introduction"` + FaceURL string `gorm:"column:face_url;size:255" json:"faceURL"` CreateTime time.Time `gorm:"column:create_time;index:create_time;autoCreateTime"` - Ex string `gorm:"column:ex" json:"ex;size:1024" json:"ex"` + Ex string `gorm:"column:ex" json:"ex;size:1024"` Status int32 `gorm:"column:status"` CreatorUserID string `gorm:"column:creator_user_id;size:64"` GroupType int32 `gorm:"column:group_type"` NeedVerification int32 `gorm:"column:need_verification"` - LookMemberInfo int32 `gorm:"column:look_member_info" json:"lookMemberInfo"` - ApplyMemberFriend int32 `gorm:"column:apply_member_friend" json:"applyMemberFriend"` + LookMemberInfo int32 `gorm:"column:look_member_info" json:"lookMemberInfo"` + ApplyMemberFriend int32 `gorm:"column:apply_member_friend" json:"applyMemberFriend"` NotificationUpdateTime time.Time `gorm:"column:notification_update_time"` NotificationUserID string `gorm:"column:notification_user_id;size:64"` } @@ -38,6 +38,10 @@ type GroupModelInterface interface { UpdateStatus(ctx context.Context, groupID string, status int32) (err error) Find(ctx context.Context, groupIDs []string) (groups []*GroupModel, err error) Take(ctx context.Context, groupID string) (group *GroupModel, err error) - Search(ctx context.Context, keyword string, pageNumber, showNumber int32) (total uint32, groups []*GroupModel, err error) + Search( + ctx context.Context, + keyword string, + pageNumber, showNumber int32, + ) (total uint32, groups []*GroupModel, err error) GetGroupIDsByGroupType(ctx context.Context, groupType int) (groupIDs []string, err error) } diff --git a/pkg/common/db/table/relation/group_member.go b/pkg/common/db/table/relation/group_member.go index b430575fe..6640a9402 100644 --- a/pkg/common/db/table/relation/group_member.go +++ b/pkg/common/db/table/relation/group_member.go @@ -34,11 +34,23 @@ type GroupMemberModelInterface interface { DeleteGroup(ctx context.Context, groupIDs []string) (err error) Update(ctx context.Context, groupID string, userID string, data map[string]any) (err error) UpdateRoleLevel(ctx context.Context, groupID string, userID string, roleLevel int32) (rowsAffected int64, err error) - Find(ctx context.Context, groupIDs []string, userIDs []string, roleLevels []int32) (groupMembers []*GroupMemberModel, err error) + Find( + ctx context.Context, + groupIDs []string, + userIDs []string, + roleLevels []int32, + ) (groupMembers []*GroupMemberModel, err error) FindMemberUserID(ctx context.Context, groupID string) (userIDs []string, err error) Take(ctx context.Context, groupID string, userID string) (groupMember *GroupMemberModel, err error) TakeOwner(ctx context.Context, groupID string) (groupMember *GroupMemberModel, err error) - SearchMember(ctx context.Context, keyword string, groupIDs []string, userIDs []string, roleLevels []int32, pageNumber, showNumber int32) (total uint32, groupList []*GroupMemberModel, err error) + SearchMember( + ctx context.Context, + keyword string, + groupIDs []string, + userIDs []string, + roleLevels []int32, + pageNumber, showNumber int32, + ) (total uint32, groupList []*GroupMemberModel, err error) MapGroupMemberNum(ctx context.Context, groupIDs []string) (count map[string]uint32, err error) FindJoinUserID(ctx context.Context, groupIDs []string) (groupUsers map[string][]string, err error) FindUserJoinedGroupID(ctx context.Context, userID string) (groupIDs []string, err error) diff --git a/pkg/common/db/table/relation/group_request.go b/pkg/common/db/table/relation/group_request.go index 62b2a1081..816d3ad35 100644 --- a/pkg/common/db/table/relation/group_request.go +++ b/pkg/common/db/table/relation/group_request.go @@ -33,6 +33,14 @@ type GroupRequestModelInterface interface { Delete(ctx context.Context, groupID string, userID string) (err error) UpdateHandler(ctx context.Context, groupID string, userID string, handledMsg string, handleResult int32) (err error) Take(ctx context.Context, groupID string, userID string) (groupRequest *GroupRequestModel, err error) - Page(ctx context.Context, userID string, pageNumber, showNumber int32) (total uint32, groups []*GroupRequestModel, err error) - PageGroup(ctx context.Context, groupIDs []string, pageNumber, showNumber int32) (total uint32, groups []*GroupRequestModel, err error) + Page( + ctx context.Context, + userID string, + pageNumber, showNumber int32, + ) (total uint32, groups []*GroupRequestModel, err error) + PageGroup( + ctx context.Context, + groupIDs []string, + pageNumber, showNumber int32, + ) (total uint32, groups []*GroupRequestModel, err error) } diff --git a/pkg/common/db/table/relation/utils.go b/pkg/common/db/table/relation/utils.go index 329c4648b..d3bd8d312 100644 --- a/pkg/common/db/table/relation/utils.go +++ b/pkg/common/db/table/relation/utils.go @@ -1,8 +1,9 @@ package relation import ( - "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" "gorm.io/gorm" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" ) type BatchUpdateGroupMember struct { diff --git a/pkg/common/db/table/unrelation/extend_msg_set.go b/pkg/common/db/table/unrelation/extend_msg_set.go index fcf33af9f..76588e679 100644 --- a/pkg/common/db/table/unrelation/extend_msg_set.go +++ b/pkg/common/db/table/unrelation/extend_msg_set.go @@ -15,36 +15,65 @@ const ( ) type ExtendMsgSetModel struct { - ConversationID string `bson:"source_id" json:"conversationID"` - SessionType int32 `bson:"session_type" json:"sessionType"` - ExtendMsgs map[string]ExtendMsgModel `bson:"extend_msgs" json:"extendMsgs"` - ExtendMsgNum int32 `bson:"extend_msg_num" json:"extendMsgNum"` - CreateTime int64 `bson:"create_time" json:"createTime"` // this block's create time + ConversationID string `bson:"source_id" json:"conversationID"` + SessionType int32 `bson:"session_type" json:"sessionType"` + ExtendMsgs map[string]ExtendMsgModel `bson:"extend_msgs" json:"extendMsgs"` + ExtendMsgNum int32 `bson:"extend_msg_num" json:"extendMsgNum"` + CreateTime int64 `bson:"create_time" json:"createTime"` // this block's create time MaxMsgUpdateTime int64 `bson:"max_msg_update_time" json:"maxMsgUpdateTime"` // index find msg } type KeyValueModel struct { - TypeKey string `bson:"type_key" json:"typeKey"` - Value string `bson:"value" json:"value"` + TypeKey string `bson:"type_key" json:"typeKey"` + Value string `bson:"value" json:"value"` LatestUpdateTime int64 `bson:"latest_update_time" json:"latestUpdateTime"` } type ExtendMsgModel struct { ReactionExtensionList map[string]KeyValueModel `bson:"reaction_extension_list" json:"reactionExtensionList"` - ClientMsgID string `bson:"client_msg_id" json:"clientMsgID"` - MsgFirstModifyTime int64 `bson:"msg_first_modify_time" json:"msgFirstModifyTime"` // this extendMsg create time - AttachedInfo string `bson:"attached_info" json:"attachedInfo"` - Ex string `bson:"ex" json:"ex"` + ClientMsgID string `bson:"client_msg_id" json:"clientMsgID"` + MsgFirstModifyTime int64 `bson:"msg_first_modify_time" json:"msgFirstModifyTime"` // this extendMsg create time + AttachedInfo string `bson:"attached_info" json:"attachedInfo"` + Ex string `bson:"ex" json:"ex"` } type ExtendMsgSetModelInterface interface { CreateExtendMsgSet(ctx context.Context, set *ExtendMsgSetModel) error - GetAllExtendMsgSet(ctx context.Context, conversationID string, opts *GetAllExtendMsgSetOpts) (sets []*ExtendMsgSetModel, err error) - GetExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, maxMsgUpdateTime int64) (*ExtendMsgSetModel, error) + GetAllExtendMsgSet( + ctx context.Context, + conversationID string, + opts *GetAllExtendMsgSetOpts, + ) (sets []*ExtendMsgSetModel, err error) + GetExtendMsgSet( + ctx context.Context, + conversationID string, + sessionType int32, + maxMsgUpdateTime int64, + ) (*ExtendMsgSetModel, error) InsertExtendMsg(ctx context.Context, conversationID string, sessionType int32, msg *ExtendMsgModel) error - InsertOrUpdateReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensionList map[string]*KeyValueModel) error - DeleteReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensionList map[string]*KeyValueModel) error - TakeExtendMsg(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, maxMsgUpdateTime int64) (extendMsg *ExtendMsgModel, err error) + InsertOrUpdateReactionExtendMsgSet( + ctx context.Context, + conversationID string, + sessionType int32, + clientMsgID string, + msgFirstModifyTime int64, + reactionExtensionList map[string]*KeyValueModel, + ) error + DeleteReactionExtendMsgSet( + ctx context.Context, + conversationID string, + sessionType int32, + clientMsgID string, + msgFirstModifyTime int64, + reactionExtensionList map[string]*KeyValueModel, + ) error + TakeExtendMsg( + ctx context.Context, + conversationID string, + sessionType int32, + clientMsgID string, + maxMsgUpdateTime int64, + ) (extendMsg *ExtendMsgModel, err error) } func (ExtendMsgSetModel) TableName() string { diff --git a/pkg/common/db/table/unrelation/msg.go b/pkg/common/db/table/unrelation/msg.go index 44fde3318..950a60689 100644 --- a/pkg/common/db/table/unrelation/msg.go +++ b/pkg/common/db/table/unrelation/msg.go @@ -4,8 +4,9 @@ import ( "context" "strconv" - "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" "go.mongodb.org/mongo-driver/mongo" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" ) const ( diff --git a/pkg/common/db/table/unrelation/super_group.go b/pkg/common/db/table/unrelation/super_group.go index 0c3a0c51c..4875c490d 100644 --- a/pkg/common/db/table/unrelation/super_group.go +++ b/pkg/common/db/table/unrelation/super_group.go @@ -10,7 +10,7 @@ const ( ) type SuperGroupModel struct { - GroupID string `bson:"group_id" json:"groupID"` + GroupID string `bson:"group_id" json:"groupID"` MemberIDs []string `bson:"member_id_list" json:"memberIDList"` } @@ -19,7 +19,7 @@ func (SuperGroupModel) TableName() string { } type UserToSuperGroupModel struct { - UserID string `bson:"user_id" json:"userID"` + UserID string `bson:"user_id" json:"userID"` GroupIDs []string `bson:"group_id_list" json:"groupIDList"` } diff --git a/pkg/common/db/tx/mongo.go b/pkg/common/db/tx/mongo.go index c8c4817ac..cd7d24607 100644 --- a/pkg/common/db/tx/mongo.go +++ b/pkg/common/db/tx/mongo.go @@ -3,8 +3,9 @@ package tx import ( "context" - "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" "go.mongodb.org/mongo-driver/mongo" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" ) func NewMongo(client *mongo.Client) CtxTx { diff --git a/pkg/common/db/unrelation/extend_msg.go b/pkg/common/db/unrelation/extend_msg.go index ab2b8b2b3..2abe1d0bd 100644 --- a/pkg/common/db/unrelation/extend_msg.go +++ b/pkg/common/db/unrelation/extend_msg.go @@ -5,12 +5,13 @@ import ( "errors" "fmt" - unRelationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" - "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" + + unRelationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" + "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" ) type ExtendMsgSetMongoDriver struct { @@ -27,7 +28,11 @@ func (e *ExtendMsgSetMongoDriver) CreateExtendMsgSet(ctx context.Context, set *u return err } -func (e *ExtendMsgSetMongoDriver) GetAllExtendMsgSet(ctx context.Context, ID string, opts *unRelationTb.GetAllExtendMsgSetOpts) (sets []*unRelationTb.ExtendMsgSetModel, err error) { +func (e *ExtendMsgSetMongoDriver) GetAllExtendMsgSet( + ctx context.Context, + ID string, + opts *unRelationTb.GetAllExtendMsgSetOpts, +) (sets []*unRelationTb.ExtendMsgSetModel, err error) { regex := fmt.Sprintf("^%s", ID) var findOpts *options.FindOptions if opts != nil { @@ -47,11 +52,23 @@ func (e *ExtendMsgSetMongoDriver) GetAllExtendMsgSet(ctx context.Context, ID str return sets, nil } -func (e *ExtendMsgSetMongoDriver) GetExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, maxMsgUpdateTime int64) (*unRelationTb.ExtendMsgSetModel, error) { +func (e *ExtendMsgSetMongoDriver) GetExtendMsgSet( + ctx context.Context, + conversationID string, + sessionType int32, + maxMsgUpdateTime int64, +) (*unRelationTb.ExtendMsgSetModel, error) { var err error - findOpts := options.Find().SetLimit(1).SetSkip(0).SetSort(bson.M{"source_id": -1}).SetProjection(bson.M{"extend_msgs": 0}) + findOpts := options.Find(). + SetLimit(1). + SetSkip(0). + SetSort(bson.M{"source_id": -1}). + SetProjection(bson.M{"extend_msgs": 0}) // update newest - find := bson.M{"source_id": primitive.Regex{Pattern: fmt.Sprintf("^%s", conversationID)}, "session_type": sessionType} + find := bson.M{ + "source_id": primitive.Regex{Pattern: fmt.Sprintf("^%s", conversationID)}, + "session_type": sessionType, + } if maxMsgUpdateTime > 0 { find["max_msg_update_time"] = maxMsgUpdateTime } @@ -70,7 +87,12 @@ func (e *ExtendMsgSetMongoDriver) GetExtendMsgSet(ctx context.Context, conversat } // first modify msg -func (e *ExtendMsgSetMongoDriver) InsertExtendMsg(ctx context.Context, conversationID string, sessionType int32, msg *unRelationTb.ExtendMsgModel) error { +func (e *ExtendMsgSetMongoDriver) InsertExtendMsg( + ctx context.Context, + conversationID string, + sessionType int32, + msg *unRelationTb.ExtendMsgModel, +) error { set, err := e.GetExtendMsgSet(ctx, conversationID, sessionType, 0) if err != nil { return utils.Wrap(err, "") @@ -95,7 +117,14 @@ func (e *ExtendMsgSetMongoDriver) InsertExtendMsg(ctx context.Context, conversat } // insert or update -func (e *ExtendMsgSetMongoDriver) InsertOrUpdateReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensionList map[string]*unRelationTb.KeyValueModel) error { +func (e *ExtendMsgSetMongoDriver) InsertOrUpdateReactionExtendMsgSet( + ctx context.Context, + conversationID string, + sessionType int32, + clientMsgID string, + msgFirstModifyTime int64, + reactionExtensionList map[string]*unRelationTb.KeyValueModel, +) error { var updateBson = bson.M{} for _, v := range reactionExtensionList { updateBson[fmt.Sprintf("extend_msgs.%s.%s", clientMsgID, v.TypeKey)] = v @@ -111,12 +140,24 @@ func (e *ExtendMsgSetMongoDriver) InsertOrUpdateReactionExtendMsgSet(ctx context if set == nil { return errors.New(fmt.Sprintf("conversationID %s has no set", conversationID)) } - _, err = e.ExtendMsgSetCollection.UpdateOne(ctx, bson.M{"source_id": set.ConversationID, "session_type": sessionType}, bson.M{"$set": updateBson}, opt) + _, err = e.ExtendMsgSetCollection.UpdateOne( + ctx, + bson.M{"source_id": set.ConversationID, "session_type": sessionType}, + bson.M{"$set": updateBson}, + opt, + ) return utils.Wrap(err, "") } // delete TypeKey -func (e *ExtendMsgSetMongoDriver) DeleteReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensionList map[string]*unRelationTb.KeyValueModel) error { +func (e *ExtendMsgSetMongoDriver) DeleteReactionExtendMsgSet( + ctx context.Context, + conversationID string, + sessionType int32, + clientMsgID string, + msgFirstModifyTime int64, + reactionExtensionList map[string]*unRelationTb.KeyValueModel, +) error { var updateBson = bson.M{} for _, v := range reactionExtensionList { updateBson[fmt.Sprintf("extend_msgs.%s.%s", clientMsgID, v.TypeKey)] = "" @@ -128,14 +169,36 @@ func (e *ExtendMsgSetMongoDriver) DeleteReactionExtendMsgSet(ctx context.Context if set == nil { return errors.New(fmt.Sprintf("conversationID %s has no set", conversationID)) } - _, err = e.ExtendMsgSetCollection.UpdateOne(ctx, bson.M{"source_id": set.ConversationID, "session_type": sessionType}, bson.M{"$unset": updateBson}) + _, err = e.ExtendMsgSetCollection.UpdateOne( + ctx, + bson.M{"source_id": set.ConversationID, "session_type": sessionType}, + bson.M{"$unset": updateBson}, + ) return err } -func (e *ExtendMsgSetMongoDriver) TakeExtendMsg(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, maxMsgUpdateTime int64) (extendMsg *unRelationTb.ExtendMsgModel, err error) { - findOpts := options.Find().SetLimit(1).SetSkip(0).SetSort(bson.M{"source_id": -1}).SetProjection(bson.M{fmt.Sprintf("extend_msgs.%s", clientMsgID): 1}) +func (e *ExtendMsgSetMongoDriver) TakeExtendMsg( + ctx context.Context, + conversationID string, + sessionType int32, + clientMsgID string, + maxMsgUpdateTime int64, +) (extendMsg *unRelationTb.ExtendMsgModel, err error) { + findOpts := options.Find(). + SetLimit(1). + SetSkip(0). + SetSort(bson.M{"source_id": -1}). + SetProjection(bson.M{fmt.Sprintf("extend_msgs.%s", clientMsgID): 1}) regex := fmt.Sprintf("^%s", conversationID) - result, err := e.ExtendMsgSetCollection.Find(ctx, bson.M{"source_id": primitive.Regex{Pattern: regex}, "session_type": sessionType, "max_msg_update_time": bson.M{"$lte": maxMsgUpdateTime}}, findOpts) + result, err := e.ExtendMsgSetCollection.Find( + ctx, + bson.M{ + "source_id": primitive.Regex{Pattern: regex}, + "session_type": sessionType, + "max_msg_update_time": bson.M{"$lte": maxMsgUpdateTime}, + }, + findOpts, + ) if err != nil { return nil, utils.Wrap(err, "") } diff --git a/pkg/common/db/unrelation/mongo.go b/pkg/common/db/unrelation/mongo.go index 6ca460ef8..8325abff9 100644 --- a/pkg/common/db/unrelation/mongo.go +++ b/pkg/common/db/unrelation/mongo.go @@ -6,14 +6,15 @@ import ( "strings" "time" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/x/bsonx" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mw/specialerror" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" - "go.mongodb.org/mongo-driver/x/bsonx" ) type Mongo struct { @@ -24,7 +25,8 @@ func NewMongo() (*Mongo, error) { specialerror.AddReplace(mongo.ErrNoDocuments, errs.ErrRecordNotFound) uri := "mongodb://sample.host:27017/?maxPoolSize=20&w=majority" if config.Config.Mongo.Uri != "" { - // example: mongodb://$user:$password@mongo1.mongo:27017,mongo2.mongo:27017,mongo3.mongo:27017/$DBDatabase/?replicaSet=rs0&readPreference=secondary&authSource=admin&maxPoolSize=$DBMaxPoolSize + // example: + // mongodb://$user:$password@mongo1.mongo:27017,mongo2.mongo:27017,mongo3.mongo:27017/$DBDatabase/?replicaSet=rs0&readPreference=secondary&authSource=admin&maxPoolSize=$DBMaxPoolSize uri = config.Config.Mongo.Uri } else { //mongodb://mongodb1.example.com:27317,mongodb2.example.com:27017/?replicaSet=mySet&authSource=authDB diff --git a/pkg/common/db/unrelation/msg.go b/pkg/common/db/unrelation/msg.go index 16275a142..4c48f6eb3 100644 --- a/pkg/common/db/unrelation/msg.go +++ b/pkg/common/db/unrelation/msg.go @@ -8,15 +8,16 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" - table "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" - "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" - "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" - "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "google.golang.org/protobuf/proto" + + table "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" + "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" + "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" + "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" ) var ErrMsgListNotExist = errors.New("user not have msg in mongoDB") @@ -32,7 +33,8 @@ func NewMsgMongoDriver(database *mongo.Database) table.MsgDocModelInterface { } func (m *MsgMongoDriver) PushMsgsToDoc(ctx context.Context, docID string, msgsToMongo []table.MsgInfoModel) error { - return m.MsgCollection.FindOneAndUpdate(ctx, bson.M{"doc_id": docID}, bson.M{"$push": bson.M{"msgs": bson.M{"$each": msgsToMongo}}}).Err() + return m.MsgCollection.FindOneAndUpdate(ctx, bson.M{"doc_id": docID}, bson.M{"$push": bson.M{"msgs": bson.M{"$each": msgsToMongo}}}). + Err() } func (m *MsgMongoDriver) Create(ctx context.Context, model *table.MsgDocModel) error { @@ -40,7 +42,13 @@ func (m *MsgMongoDriver) Create(ctx context.Context, model *table.MsgDocModel) e return err } -func (m *MsgMongoDriver) UpdateMsg(ctx context.Context, docID string, index int64, key string, value any) (*mongo.UpdateResult, error) { +func (m *MsgMongoDriver) UpdateMsg( + ctx context.Context, + docID string, + index int64, + key string, + value any, +) (*mongo.UpdateResult, error) { var field string if key == "" { field = fmt.Sprintf("msgs.%d", index) @@ -57,7 +65,13 @@ func (m *MsgMongoDriver) UpdateMsg(ctx context.Context, docID string, index int6 } // PushUnique value must slice -func (m *MsgMongoDriver) PushUnique(ctx context.Context, docID string, index int64, key string, value any) (*mongo.UpdateResult, error) { +func (m *MsgMongoDriver) PushUnique( + ctx context.Context, + docID string, + index int64, + key string, + value any, +) (*mongo.UpdateResult, error) { var field string if key == "" { field = fmt.Sprintf("msgs.%d", index) @@ -78,20 +92,34 @@ func (m *MsgMongoDriver) PushUnique(ctx context.Context, docID string, index int } func (m *MsgMongoDriver) UpdateMsgContent(ctx context.Context, docID string, index int64, msg []byte) error { - _, err := m.MsgCollection.UpdateOne(ctx, bson.M{"doc_id": docID}, bson.M{"$set": bson.M{fmt.Sprintf("msgs.%d.msg", index): msg}}) + _, err := m.MsgCollection.UpdateOne( + ctx, + bson.M{"doc_id": docID}, + bson.M{"$set": bson.M{fmt.Sprintf("msgs.%d.msg", index): msg}}, + ) if err != nil { return utils.Wrap(err, "") } return nil } -func (m *MsgMongoDriver) UpdateMsgStatusByIndexInOneDoc(ctx context.Context, docID string, msg *sdkws.MsgData, seqIndex int, status int32) error { +func (m *MsgMongoDriver) UpdateMsgStatusByIndexInOneDoc( + ctx context.Context, + docID string, + msg *sdkws.MsgData, + seqIndex int, + status int32, +) error { msg.Status = status bytes, err := proto.Marshal(msg) if err != nil { return utils.Wrap(err, "") } - _, err = m.MsgCollection.UpdateOne(ctx, bson.M{"doc_id": docID}, bson.M{"$set": bson.M{fmt.Sprintf("msgs.%d.msg", seqIndex): bytes}}) + _, err = m.MsgCollection.UpdateOne( + ctx, + bson.M{"doc_id": docID}, + bson.M{"$set": bson.M{fmt.Sprintf("msgs.%d.msg", seqIndex): bytes}}, + ) if err != nil { return utils.Wrap(err, "") } @@ -104,12 +132,20 @@ func (m *MsgMongoDriver) FindOneByDocID(ctx context.Context, docID string) (*tab return doc, err } -func (m *MsgMongoDriver) GetMsgDocModelByIndex(ctx context.Context, conversationID string, index, sort int64) (*table.MsgDocModel, error) { +func (m *MsgMongoDriver) GetMsgDocModelByIndex( + ctx context.Context, + conversationID string, + index, sort int64, +) (*table.MsgDocModel, error) { if sort != 1 && sort != -1 { return nil, errs.ErrArgs.Wrap("mongo sort must be 1 or -1") } findOpts := options.Find().SetLimit(1).SetSkip(index).SetSort(bson.M{"doc_id": sort}) - cursor, err := m.MsgCollection.Find(ctx, bson.M{"doc_id": primitive.Regex{Pattern: fmt.Sprintf("^%s:", conversationID)}}, findOpts) + cursor, err := m.MsgCollection.Find( + ctx, + bson.M{"doc_id": primitive.Regex{Pattern: fmt.Sprintf("^%s:", conversationID)}}, + findOpts, + ) if err != nil { return nil, utils.Wrap(err, "") } @@ -180,7 +216,12 @@ func (m *MsgMongoDriver) DeleteDocs(ctx context.Context, docIDs []string) error return err } -func (m *MsgMongoDriver) GetMsgBySeqIndexIn1Doc(ctx context.Context, userID string, docID string, seqs []int64) (msgs []*table.MsgInfoModel, err error) { +func (m *MsgMongoDriver) GetMsgBySeqIndexIn1Doc( + ctx context.Context, + userID string, + docID string, + seqs []int64, +) (msgs []*table.MsgInfoModel, err error) { indexs := make([]int64, 0, len(seqs)) for _, seq := range seqs { indexs = append(indexs, m.model.GetMsgIndex(seq)) @@ -286,7 +327,12 @@ func (m *MsgMongoDriver) IsExistDocID(ctx context.Context, docID string) (bool, return count > 0, nil } -func (m *MsgMongoDriver) MarkSingleChatMsgsAsRead(ctx context.Context, userID string, docID string, indexes []int64) error { +func (m *MsgMongoDriver) MarkSingleChatMsgsAsRead( + ctx context.Context, + userID string, + docID string, + indexes []int64, +) error { updates := []mongo.WriteModel{} for _, index := range indexes { filter := bson.M{ diff --git a/pkg/common/db/unrelation/super_group.go b/pkg/common/db/unrelation/super_group.go index 54de697e4..3dc9f61bd 100644 --- a/pkg/common/db/unrelation/super_group.go +++ b/pkg/common/db/unrelation/super_group.go @@ -3,15 +3,19 @@ package unrelation import ( "context" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" - "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" + "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" ) func NewSuperGroupMongoDriver(database *mongo.Database) unrelation.SuperGroupModelInterface { - return &SuperGroupMongoDriver{superGroupCollection: database.Collection(unrelation.CSuperGroup), userToSuperGroupCollection: database.Collection(unrelation.CUserToSuperGroup)} + return &SuperGroupMongoDriver{ + superGroupCollection: database.Collection(unrelation.CSuperGroup), + userToSuperGroupCollection: database.Collection(unrelation.CUserToSuperGroup), + } } type SuperGroupMongoDriver struct { @@ -28,9 +32,14 @@ func (s *SuperGroupMongoDriver) CreateSuperGroup(ctx context.Context, groupID st return err } for _, userID := range initMemberIDs { - _, err = s.userToSuperGroupCollection.UpdateOne(ctx, bson.M{"user_id": userID}, bson.M{"$addToSet": bson.M{"group_id_list": groupID}}, &options.UpdateOptions{ - Upsert: utils.ToPtr(true), - }) + _, err = s.userToSuperGroupCollection.UpdateOne( + ctx, + bson.M{"user_id": userID}, + bson.M{"$addToSet": bson.M{"group_id_list": groupID}}, + &options.UpdateOptions{ + Upsert: utils.ToPtr(true), + }, + ) if err != nil { return err } @@ -38,14 +47,20 @@ func (s *SuperGroupMongoDriver) CreateSuperGroup(ctx context.Context, groupID st return nil } -func (s *SuperGroupMongoDriver) TakeSuperGroup(ctx context.Context, groupID string) (group *unrelation.SuperGroupModel, err error) { +func (s *SuperGroupMongoDriver) TakeSuperGroup( + ctx context.Context, + groupID string, +) (group *unrelation.SuperGroupModel, err error) { if err := s.superGroupCollection.FindOne(ctx, bson.M{"group_id": groupID}).Decode(&group); err != nil { return nil, utils.Wrap(err, "") } return group, nil } -func (s *SuperGroupMongoDriver) FindSuperGroup(ctx context.Context, groupIDs []string) (groups []*unrelation.SuperGroupModel, err error) { +func (s *SuperGroupMongoDriver) FindSuperGroup( + ctx context.Context, + groupIDs []string, +) (groups []*unrelation.SuperGroupModel, err error) { cursor, err := s.superGroupCollection.Find(ctx, bson.M{"group_id": bson.M{ "$in": groupIDs, }}) @@ -60,7 +75,11 @@ func (s *SuperGroupMongoDriver) FindSuperGroup(ctx context.Context, groupIDs []s } func (s *SuperGroupMongoDriver) AddUserToSuperGroup(ctx context.Context, groupID string, userIDs []string) error { - _, err := s.superGroupCollection.UpdateOne(ctx, bson.M{"group_id": groupID}, bson.M{"$addToSet": bson.M{"member_id_list": bson.M{"$each": userIDs}}}) + _, err := s.superGroupCollection.UpdateOne( + ctx, + bson.M{"group_id": groupID}, + bson.M{"$addToSet": bson.M{"member_id_list": bson.M{"$each": userIDs}}}, + ) if err != nil { return err } @@ -69,7 +88,12 @@ func (s *SuperGroupMongoDriver) AddUserToSuperGroup(ctx context.Context, groupID Upsert: &upsert, } for _, userID := range userIDs { - _, err = s.userToSuperGroupCollection.UpdateOne(ctx, bson.M{"user_id": userID}, bson.M{"$addToSet": bson.M{"group_id_list": groupID}}, opts) + _, err = s.userToSuperGroupCollection.UpdateOne( + ctx, + bson.M{"user_id": userID}, + bson.M{"$addToSet": bson.M{"group_id_list": groupID}}, + opts, + ) if err != nil { return utils.Wrap(err, "transaction failed") } @@ -78,7 +102,11 @@ func (s *SuperGroupMongoDriver) AddUserToSuperGroup(ctx context.Context, groupID } func (s *SuperGroupMongoDriver) RemoverUserFromSuperGroup(ctx context.Context, groupID string, userIDs []string) error { - _, err := s.superGroupCollection.UpdateOne(ctx, bson.M{"group_id": groupID}, bson.M{"$pull": bson.M{"member_id_list": bson.M{"$in": userIDs}}}) + _, err := s.superGroupCollection.UpdateOne( + ctx, + bson.M{"group_id": groupID}, + bson.M{"$pull": bson.M{"member_id_list": bson.M{"$in": userIDs}}}, + ) if err != nil { return err } @@ -89,7 +117,10 @@ func (s *SuperGroupMongoDriver) RemoverUserFromSuperGroup(ctx context.Context, g return nil } -func (s *SuperGroupMongoDriver) GetSuperGroupByUserID(ctx context.Context, userID string) (*unrelation.UserToSuperGroupModel, error) { +func (s *SuperGroupMongoDriver) GetSuperGroupByUserID( + ctx context.Context, + userID string, +) (*unrelation.UserToSuperGroupModel, error) { var user unrelation.UserToSuperGroupModel err := s.userToSuperGroupCollection.FindOne(ctx, bson.M{"user_id": userID}).Decode(&user) return &user, utils.Wrap(err, "") @@ -107,6 +138,10 @@ func (s *SuperGroupMongoDriver) DeleteSuperGroup(ctx context.Context, groupID st } func (s *SuperGroupMongoDriver) RemoveGroupFromUser(ctx context.Context, groupID string, userIDs []string) error { - _, err := s.userToSuperGroupCollection.UpdateOne(ctx, bson.M{"user_id": bson.M{"$in": userIDs}}, bson.M{"$pull": bson.M{"group_id_list": groupID}}) + _, err := s.userToSuperGroupCollection.UpdateOne( + ctx, + bson.M{"user_id": bson.M{"$in": userIDs}}, + bson.M{"$pull": bson.M{"group_id_list": groupID}}, + ) return utils.Wrap(err, "") } diff --git a/pkg/common/http/http_client.go b/pkg/common/http/http_client.go index 2227a3438..72e3fae62 100644 --- a/pkg/common/http/http_client.go +++ b/pkg/common/http/http_client.go @@ -3,8 +3,7 @@ ** copyright('open-im,www.open-im.io'). ** author("fg,Gordon@tuoyun.net"). ** time(2021/5/27 10:31). - */ -package http + */package http import ( "bytes" @@ -39,7 +38,13 @@ func Get(url string) (response []byte, err error) { return body, nil } -func Post(ctx context.Context, url string, header map[string]string, data interface{}, timeout int) (content []byte, err error) { +func Post( + ctx context.Context, + url string, + header map[string]string, + data interface{}, + timeout int, +) (content []byte, err error) { if timeout > 0 { var cancel func() ctx, cancel = context.WithTimeout(ctx, time.Second*time.Duration(timeout)) @@ -72,7 +77,13 @@ func Post(ctx context.Context, url string, header map[string]string, data interf return result, nil } -func PostReturn(ctx context.Context, url string, header map[string]string, input, output interface{}, timeOutSecond int) error { +func PostReturn( + ctx context.Context, + url string, + header map[string]string, + input, output interface{}, + timeOutSecond int, +) error { b, err := Post(ctx, url, header, input, timeOutSecond) if err != nil { return err @@ -81,7 +92,13 @@ func PostReturn(ctx context.Context, url string, header map[string]string, input return err } -func callBackPostReturn(ctx context.Context, url, command string, input interface{}, output callbackstruct.CallbackResp, callbackConfig config.CallBackConfig) error { +func callBackPostReturn( + ctx context.Context, + url, command string, + input interface{}, + output callbackstruct.CallbackResp, + callbackConfig config.CallBackConfig, +) error { defer log.ZDebug(ctx, "callback", "url", url, "command", command, "input", input, "callbackConfig", callbackConfig) v := urlLib.Values{} v.Set(constant.CallbackCommand, command) @@ -104,6 +121,12 @@ func callBackPostReturn(ctx context.Context, url, command string, input interfac return output.Parse() } -func CallBackPostReturn(ctx context.Context, url string, req callbackstruct.CallbackReq, resp callbackstruct.CallbackResp, callbackConfig config.CallBackConfig) error { +func CallBackPostReturn( + ctx context.Context, + url string, + req callbackstruct.CallbackReq, + resp callbackstruct.CallbackResp, + callbackConfig config.CallBackConfig, +) error { return callBackPostReturn(ctx, url, req.GetCallbackCommand(), req, resp, callbackConfig) } diff --git a/pkg/common/log/sql_logger.go b/pkg/common/log/sql_logger.go index 30194ac96..1785b211c 100644 --- a/pkg/common/log/sql_logger.go +++ b/pkg/common/log/sql_logger.go @@ -17,7 +17,11 @@ type SqlLogger struct { SlowThreshold time.Duration } -func NewSqlLogger(logLevel gormLogger.LogLevel, ignoreRecordNotFoundError bool, slowThreshold time.Duration) *SqlLogger { +func NewSqlLogger( + logLevel gormLogger.LogLevel, + ignoreRecordNotFoundError bool, + slowThreshold time.Duration, +) *SqlLogger { return &SqlLogger{ LogLevel: logLevel, IgnoreRecordNotFoundError: ignoreRecordNotFoundError, @@ -52,7 +56,17 @@ func (l *SqlLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql s case err != nil && l.LogLevel >= gormLogger.Error && (!errors.Is(err, gorm.ErrRecordNotFound) || !l.IgnoreRecordNotFoundError): sql, rows := fc() if rows == -1 { - ZError(ctx, "sql exec detail", err, "gorm", gormUtils.FileWithLineNum(), "elapsed time", fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), "sql", sql) + ZError( + ctx, + "sql exec detail", + err, + "gorm", + gormUtils.FileWithLineNum(), + "elapsed time", + fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), + "sql", + sql, + ) } else { ZError(ctx, "sql exec detail", err, "gorm", gormUtils.FileWithLineNum(), "elapsed time", fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), "rows", rows, "sql", sql) } @@ -60,14 +74,36 @@ func (l *SqlLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql s sql, rows := fc() slowLog := fmt.Sprintf("SLOW SQL >= %v", l.SlowThreshold) if rows == -1 { - ZWarn(ctx, "sql exec detail", nil, "gorm", gormUtils.FileWithLineNum(), nil, "slow sql", slowLog, "elapsed time", fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), "sql", sql) + ZWarn( + ctx, + "sql exec detail", + nil, + "gorm", + gormUtils.FileWithLineNum(), + nil, + "slow sql", + slowLog, + "elapsed time", + fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), + "sql", + sql, + ) } else { ZWarn(ctx, "sql exec detail", nil, "gorm", gormUtils.FileWithLineNum(), nil, "slow sql", slowLog, "elapsed time", fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), "rows", rows, "sql", sql) } case l.LogLevel == gormLogger.Info: sql, rows := fc() if rows == -1 { - ZDebug(ctx, "sql exec detail", "gorm", gormUtils.FileWithLineNum(), "elapsed time", fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), "sql", sql) + ZDebug( + ctx, + "sql exec detail", + "gorm", + gormUtils.FileWithLineNum(), + "elapsed time", + fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), + "sql", + sql, + ) } else { ZDebug(ctx, "sql exec detail", "gorm", gormUtils.FileWithLineNum(), "elapsed time", fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), "rows", rows, "sql", sql) } diff --git a/pkg/common/log/zap.go b/pkg/common/log/zap.go index 8ea3e2dfc..a173ac055 100644 --- a/pkg/common/log/zap.go +++ b/pkg/common/log/zap.go @@ -7,10 +7,11 @@ import ( "path/filepath" "time" + rotatelogs "github.com/lestrrat-go/file-rotatelogs" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" - rotatelogs "github.com/lestrrat-go/file-rotatelogs" "go.uber.org/zap" "go.uber.org/zap/zapcore" @@ -31,7 +32,14 @@ var ( ) // InitFromConfig initializes a Zap-based logger -func InitFromConfig(loggerPrefixName, moduleName string, logLevel int, isStdout bool, isJson bool, logLocation string, rotateCount uint) error { +func InitFromConfig( + loggerPrefixName, moduleName string, + logLevel int, + isStdout bool, + isJson bool, + logLocation string, + rotateCount uint, +) error { l, err := NewZapLogger(loggerPrefixName, moduleName, logLevel, isStdout, isJson, logLocation, rotateCount) if err != nil { return err @@ -78,7 +86,14 @@ type ZapLogger struct { loggerPrefixName string } -func NewZapLogger(loggerPrefixName, loggerName string, logLevel int, isStdout bool, isJson bool, logLocation string, rotateCount uint) (*ZapLogger, error) { +func NewZapLogger( + loggerPrefixName, loggerName string, + logLevel int, + isStdout bool, + isJson bool, + logLocation string, + rotateCount uint, +) (*ZapLogger, error) { zapConfig := zap.Config{ Level: zap.NewAtomicLevelAt(logLevelMap[logLevel]), // EncoderConfig: zap.NewProductionEncoderConfig(), diff --git a/pkg/common/mw/gin.go b/pkg/common/mw/gin.go index 10dd4eed9..42798d060 100644 --- a/pkg/common/mw/gin.go +++ b/pkg/common/mw/gin.go @@ -4,6 +4,9 @@ import ( "errors" "net/http" + "github.com/gin-gonic/gin" + "github.com/redis/go-redis/v9" + "github.com/OpenIMSDK/Open-IM-Server/pkg/apiresp" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" @@ -12,8 +15,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/tokenverify" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" - "github.com/gin-gonic/gin" - "github.com/redis/go-redis/v9" ) func CorsHandler() gin.HandlerFunc { @@ -21,10 +22,22 @@ func CorsHandler() gin.HandlerFunc { c.Writer.Header().Set("Access-Control-Allow-Origin", "*") c.Header("Access-Control-Allow-Methods", "*") c.Header("Access-Control-Allow-Headers", "*") - c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") // 跨域关键设置 让浏览器可以解析 - c.Header("Access-Control-Max-Age", "172800") // 缓存请求信息 单位为秒 - c.Header("Access-Control-Allow-Credentials", "false") // 跨域请求是否需要带cookie信息 默认设置为true - c.Header("content-type", "application/json") // 设置返回格式是json + c.Header( + "Access-Control-Expose-Headers", + "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar", + ) // 跨域关键设置 让浏览器可以解析 + c.Header( + "Access-Control-Max-Age", + "172800", + ) // 缓存请求信息 单位为秒 + c.Header( + "Access-Control-Allow-Credentials", + "false", + ) // 跨域请求是否需要带cookie信息 默认设置为true + c.Header( + "content-type", + "application/json", + ) // 设置返回格式是json //Release all option pre-requests if c.Request.Method == http.MethodOptions { c.JSON(http.StatusOK, "Options Request!") @@ -52,7 +65,11 @@ func GinParseOperationID() gin.HandlerFunc { } func GinParseToken(rdb redis.UniversalClient) gin.HandlerFunc { - dataBase := controller.NewAuthDatabase(cache.NewMsgCacheModel(rdb), config.Config.Secret, config.Config.TokenPolicy.Expire) + dataBase := controller.NewAuthDatabase( + cache.NewMsgCacheModel(rdb), + config.Config.Secret, + config.Config.TokenPolicy.Expire, + ) return func(c *gin.Context) { switch c.Request.Method { case http.MethodPost: diff --git a/pkg/common/mw/rpc_client_interceptor.go b/pkg/common/mw/rpc_client_interceptor.go index e52366ec0..530847280 100644 --- a/pkg/common/mw/rpc_client_interceptor.go +++ b/pkg/common/mw/rpc_client_interceptor.go @@ -6,20 +6,28 @@ import ( "fmt" "strings" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/errinfo" - "google.golang.org/grpc" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" ) func GrpcClient() grpc.DialOption { return grpc.WithUnaryInterceptor(RpcClientInterceptor) } -func RpcClientInterceptor(ctx context.Context, method string, req, resp interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) (err error) { +func RpcClientInterceptor( + ctx context.Context, + method string, + req, resp interface{}, + cc *grpc.ClientConn, + invoker grpc.UnaryInvoker, + opts ...grpc.CallOption, +) (err error) { if ctx == nil { return errs.ErrInternalServer.Wrap("call rpc request context is nil") } diff --git a/pkg/common/mw/rpc_server_interceptor.go b/pkg/common/mw/rpc_server_interceptor.go index be37fc0fb..4dcd4e582 100644 --- a/pkg/common/mw/rpc_server_interceptor.go +++ b/pkg/common/mw/rpc_server_interceptor.go @@ -11,16 +11,17 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mw/specialerror" - "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" - "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/errinfo" "github.com/pkg/errors" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mw/specialerror" + "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" + "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/errinfo" ) func rpcString(v interface{}) string { @@ -30,12 +31,17 @@ func rpcString(v interface{}) string { return fmt.Sprintf("%+v", v) } -func RpcServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { +func RpcServerInterceptor( + ctx context.Context, + req interface{}, + info *grpc.UnaryServerInfo, + handler grpc.UnaryHandler, +) (resp interface{}, err error) { log.ZDebug(ctx, "rpc server req", "req", rpcString(req)) //defer func() { // if r := recover(); r != nil { - // log.ZError(ctx, "rpc panic", nil, "FullMethod", info.FullMethod, "type:", fmt.Sprintf("%T", r), "panic:", r) + // log.ZError(ctx, "rpc panic", nil, "FullMethod", info.FullMethod, "type:", fmt.Sprintf("%T", r), "panic:", r) // fmt.Printf("panic: %+v\nstack info: %s\n", r, string(debug.Stack())) // pc, file, line, ok := runtime.Caller(4) // if !ok { @@ -48,7 +54,8 @@ func RpcServerInterceptor(ctx context.Context, req interface{}, info *grpc.Unary // Cause: fmt.Sprintf("%s", r), // Warp: nil, // } - // sta, err_ := status.New(codes.Code(errs.ErrInternalServer.Code()), errs.ErrInternalServer.Msg()).WithDetails(errInfo) + // sta, err_ := status.New(codes.Code(errs.ErrInternalServer.Code()), + // errs.ErrInternalServer.Msg()).WithDetails(errInfo) // if err_ != nil { // panic(err_) // } @@ -122,7 +129,17 @@ func RpcServerInterceptor(ctx context.Context, req interface{}, info *grpc.Unary if unwrap != err { sti, ok := err.(interface{ StackTrace() errors.StackTrace }) if ok { - log.ZWarn(ctx, "rpc server resp", err, "funcName", funcName, "unwrap", unwrap.Error(), "stack", fmt.Sprintf("%+v", err)) + log.ZWarn( + ctx, + "rpc server resp", + err, + "funcName", + funcName, + "unwrap", + unwrap.Error(), + "stack", + fmt.Sprintf("%+v", err), + ) if fs := sti.StackTrace(); len(fs) > 0 { pc := uintptr(fs[0]) fn := runtime.FuncForPC(pc) diff --git a/pkg/common/network/ip.go b/pkg/common/network/ip.go index bbe16e265..2d5a5b09c 100644 --- a/pkg/common/network/ip.go +++ b/pkg/common/network/ip.go @@ -1,8 +1,9 @@ package network import ( - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" utils "github.com/OpenIMSDK/open_utils" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" ) func GetRpcRegisterIP(configIP string) (string, error) { diff --git a/pkg/common/tokenverify/jwt_token.go b/pkg/common/tokenverify/jwt_token.go index 582a7f68e..9f4088eff 100644 --- a/pkg/common/tokenverify/jwt_token.go +++ b/pkg/common/tokenverify/jwt_token.go @@ -5,11 +5,12 @@ import ( "fmt" "time" + "github.com/golang-jwt/jwt/v4" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "github.com/golang-jwt/jwt/v4" ) type Claims struct { diff --git a/pkg/common/tokenverify/jwt_token_test.go b/pkg/common/tokenverify/jwt_token_test.go index ffbac4bca..b8390b45d 100644 --- a/pkg/common/tokenverify/jwt_token_test.go +++ b/pkg/common/tokenverify/jwt_token_test.go @@ -3,9 +3,10 @@ package tokenverify import ( "testing" + "github.com/golang-jwt/jwt/v4" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" - "github.com/golang-jwt/jwt/v4" ) func Test_ParseToken(t *testing.T) { diff --git a/pkg/discoveryregistry/zookeeper/discover.go b/pkg/discoveryregistry/zookeeper/discover.go index 65dacd397..912c001d9 100644 --- a/pkg/discoveryregistry/zookeeper/discover.go +++ b/pkg/discoveryregistry/zookeeper/discover.go @@ -6,9 +6,10 @@ import ( "io" "strings" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/pkg/errors" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" + "github.com/go-zookeeper/zk" "google.golang.org/grpc" "google.golang.org/grpc/resolver" @@ -68,7 +69,11 @@ func (s *ZkClient) GetConnsRemote(serviceName string) (conns []resolver.Address, return conns, nil } -func (s *ZkClient) GetConns(ctx context.Context, serviceName string, opts ...grpc.DialOption) ([]grpc.ClientConnInterface, error) { +func (s *ZkClient) GetConns( + ctx context.Context, + serviceName string, + opts ...grpc.DialOption, +) ([]grpc.ClientConnInterface, error) { s.logger.Printf("get conns from client, serviceName: %s", serviceName) s.lock.Lock() opts = append(s.options, opts...) @@ -82,7 +87,13 @@ func (s *ZkClient) GetConns(ctx context.Context, serviceName string, opts ...grp return nil, err } if len(conns) == 0 { - return nil, fmt.Errorf("no conn for service %s, grpc server may not exist, local conn is %v, please check zookeeper server %v, path: %s", serviceName, s.localConns, s.zkServers, s.zkRoot) + return nil, fmt.Errorf( + "no conn for service %s, grpc server may not exist, local conn is %v, please check zookeeper server %v, path: %s", + serviceName, + s.localConns, + s.zkServers, + s.zkRoot, + ) } s.localConns[serviceName] = conns } @@ -100,8 +111,15 @@ func (s *ZkClient) GetConns(ctx context.Context, serviceName string, opts ...grp return ret, nil } -func (s *ZkClient) GetConn(ctx context.Context, serviceName string, opts ...grpc.DialOption) (grpc.ClientConnInterface, error) { - newOpts := append(s.options, grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, s.balancerName))) +func (s *ZkClient) GetConn( + ctx context.Context, + serviceName string, + opts ...grpc.DialOption, +) (grpc.ClientConnInterface, error) { + newOpts := append( + s.options, + grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, s.balancerName)), + ) s.logger.Printf("get conn from client, serviceName: %s", serviceName) return grpc.DialContext(ctx, fmt.Sprintf("%s:///%s", s.scheme, serviceName), append(newOpts, opts...)...) } diff --git a/pkg/discoveryregistry/zookeeper/register.go b/pkg/discoveryregistry/zookeeper/register.go index 8f38f6d8d..ab75f488b 100644 --- a/pkg/discoveryregistry/zookeeper/register.go +++ b/pkg/discoveryregistry/zookeeper/register.go @@ -26,7 +26,11 @@ func (s *ZkClient) Register(rpcRegisterName, host string, port int, opts ...grpc if err != nil { return err } - node, err := s.conn.CreateProtectedEphemeralSequential(s.getPath(rpcRegisterName)+"/"+addr+"_", []byte(addr), zk.WorldACL(zk.PermAll)) + node, err := s.conn.CreateProtectedEphemeralSequential( + s.getPath(rpcRegisterName)+"/"+addr+"_", + []byte(addr), + zk.WorldACL(zk.PermAll), + ) if err != nil { return err } diff --git a/pkg/discoveryregistry/zookeeper/resolver.go b/pkg/discoveryregistry/zookeeper/resolver.go index ee2a449cb..74ed04455 100644 --- a/pkg/discoveryregistry/zookeeper/resolver.go +++ b/pkg/discoveryregistry/zookeeper/resolver.go @@ -18,7 +18,16 @@ type Resolver struct { } func (r *Resolver) ResolveNowZK(o resolver.ResolveNowOptions) { - log.ZDebug(context.Background(), "start resolve now", "target", r.target, "cc", r.cc.UpdateState, "serviceName", strings.TrimLeft(r.target.URL.Path, "/")) + log.ZDebug( + context.Background(), + "start resolve now", + "target", + r.target, + "cc", + r.cc.UpdateState, + "serviceName", + strings.TrimLeft(r.target.URL.Path, "/"), + ) newConns, err := r.getConnsRemote(strings.TrimLeft(r.target.URL.Path, "/")) if err != nil { log.ZError(context.Background(), "resolve now error", err, "target", r.target) @@ -26,7 +35,15 @@ func (r *Resolver) ResolveNowZK(o resolver.ResolveNowOptions) { } r.addrs = newConns if err := r.cc.UpdateState(resolver.State{Addresses: newConns}); err != nil { - log.ZError(context.Background(), "UpdateState error, conns is nil from svr", err, "conns", newConns, "zk path", r.target.URL.Path) + log.ZError( + context.Background(), + "UpdateState error, conns is nil from svr", + err, + "conns", + newConns, + "zk path", + r.target.URL.Path, + ) return } log.ZDebug(context.Background(), "resolve now finished", "target", r.target, "conns", r.addrs) @@ -36,7 +53,11 @@ func (r *Resolver) ResolveNow(o resolver.ResolveNowOptions) {} func (s *Resolver) Close() {} -func (s *ZkClient) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) { +func (s *ZkClient) Build( + target resolver.Target, + cc resolver.ClientConn, + opts resolver.BuildOptions, +) (resolver.Resolver, error) { s.logger.Printf("build resolver: %+v, cc: %+v", target, cc.UpdateState) // log.ZDebug(context.Background(), "build resolver start", "target", target, "cc", cc.UpdateState) r := &Resolver{} @@ -49,7 +70,8 @@ func (s *ZkClient) Build(target resolver.Target, cc resolver.ClientConn, opts re serviceName := strings.TrimLeft(target.URL.Path, "/") s.resolvers[serviceName] = r s.logger.Printf("build resolver finished: %+v, cc: %+v, key: %s", target, cc.UpdateState, serviceName) - // log.ZDebug(context.Background(), "build resolver finished", "target", target, "cc", cc.UpdateState, "serviceName", serviceName) + // log.ZDebug(context.Background(), "build resolver finished", "target", target, "cc", cc.UpdateState, + // "serviceName", serviceName) return r, nil } diff --git a/pkg/discoveryregistry/zookeeper/zk.go b/pkg/discoveryregistry/zookeeper/zk.go index be0fb5bb0..d6287e47b 100644 --- a/pkg/discoveryregistry/zookeeper/zk.go +++ b/pkg/discoveryregistry/zookeeper/zk.go @@ -97,7 +97,12 @@ func NewClient(zkServers []string, zkRoot string, options ...ZkOption) (*ZkClien for _, option := range options { option(client) } - conn, eventChan, err := zk.Connect(zkServers, time.Duration(client.timeout)*time.Second, zk.WithLogInfo(true), zk.WithLogger(client.logger)) + conn, eventChan, err := zk.Connect( + zkServers, + time.Duration(client.timeout)*time.Second, + zk.WithLogInfo(true), + zk.WithLogger(client.logger), + ) if err != nil { return nil, err } diff --git a/pkg/proto/conversation/conversation.pb.go b/pkg/proto/conversation/conversation.pb.go index 8c7451a3d..debced9b9 100644 --- a/pkg/proto/conversation/conversation.pb.go +++ b/pkg/proto/conversation/conversation.pb.go @@ -11,12 +11,13 @@ import ( reflect "reflect" sync "sync" - wrapperspb "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/wrapperspb" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + + wrapperspb "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/wrapperspb" ) const ( diff --git a/pkg/proto/friend/friend.pb.go b/pkg/proto/friend/friend.pb.go index 515ac8128..c49a13999 100644 --- a/pkg/proto/friend/friend.pb.go +++ b/pkg/proto/friend/friend.pb.go @@ -11,12 +11,13 @@ import ( reflect "reflect" sync "sync" - sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + + sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" ) const ( diff --git a/pkg/proto/group/group.pb.go b/pkg/proto/group/group.pb.go index 5024365cc..f54e54a9e 100644 --- a/pkg/proto/group/group.pb.go +++ b/pkg/proto/group/group.pb.go @@ -11,13 +11,14 @@ import ( reflect "reflect" sync "sync" - sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" - wrapperspb "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/wrapperspb" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + + sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" + wrapperspb "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/wrapperspb" ) const ( diff --git a/pkg/proto/msg/msg.pb.go b/pkg/proto/msg/msg.pb.go index 4a35ebe4f..f52af2b63 100644 --- a/pkg/proto/msg/msg.pb.go +++ b/pkg/proto/msg/msg.pb.go @@ -11,13 +11,14 @@ import ( reflect "reflect" sync "sync" - sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" - wrapperspb "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/wrapperspb" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + + sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" + wrapperspb "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/wrapperspb" ) const ( diff --git a/pkg/proto/msggateway/msggateway.pb.go b/pkg/proto/msggateway/msggateway.pb.go index 9b247c65a..51f186c8c 100644 --- a/pkg/proto/msggateway/msggateway.pb.go +++ b/pkg/proto/msggateway/msggateway.pb.go @@ -11,12 +11,13 @@ import ( reflect "reflect" sync "sync" - sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + + sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" ) const ( diff --git a/pkg/proto/push/push.pb.go b/pkg/proto/push/push.pb.go index 1e6a895f1..f9ef4a08a 100644 --- a/pkg/proto/push/push.pb.go +++ b/pkg/proto/push/push.pb.go @@ -11,12 +11,13 @@ import ( reflect "reflect" sync "sync" - sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + + sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" ) const ( diff --git a/pkg/proto/sdkws/sdkws.pb.go b/pkg/proto/sdkws/sdkws.pb.go index 13637d3c9..9a5e4d630 100644 --- a/pkg/proto/sdkws/sdkws.pb.go +++ b/pkg/proto/sdkws/sdkws.pb.go @@ -10,9 +10,10 @@ import ( reflect "reflect" sync "sync" - wrapperspb "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/wrapperspb" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + + wrapperspb "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/wrapperspb" ) const ( diff --git a/pkg/proto/user/user.pb.go b/pkg/proto/user/user.pb.go index b55abd968..86113ec05 100644 --- a/pkg/proto/user/user.pb.go +++ b/pkg/proto/user/user.pb.go @@ -11,13 +11,14 @@ import ( reflect "reflect" sync "sync" - conversation "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/conversation" - sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + + conversation "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/conversation" + sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" ) const ( diff --git a/pkg/rpcclient/auth.go b/pkg/rpcclient/auth.go index 580e6bcee..fc435aa24 100644 --- a/pkg/rpcclient/auth.go +++ b/pkg/rpcclient/auth.go @@ -3,10 +3,11 @@ package rpcclient import ( "context" + "google.golang.org/grpc" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/auth" - "google.golang.org/grpc" ) func NewAuth(discov discoveryregistry.SvcDiscoveryRegistry) *Auth { diff --git a/pkg/rpcclient/conversation.go b/pkg/rpcclient/conversation.go index 400a7b651..cc781e449 100644 --- a/pkg/rpcclient/conversation.go +++ b/pkg/rpcclient/conversation.go @@ -4,11 +4,12 @@ import ( "context" "fmt" + "google.golang.org/grpc" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" pbConversation "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/conversation" - "google.golang.org/grpc" ) type Conversation struct { @@ -32,12 +33,18 @@ func NewConversationRpcClient(discov discoveryregistry.SvcDiscoveryRegistry) Con return ConversationRpcClient(*NewConversation(discov)) } -func (c *ConversationRpcClient) ModifyConversationField(ctx context.Context, req *pbConversation.ModifyConversationFieldReq) error { +func (c *ConversationRpcClient) ModifyConversationField( + ctx context.Context, + req *pbConversation.ModifyConversationFieldReq, +) error { _, err := c.Client.ModifyConversationField(ctx, req) return err } -func (c *ConversationRpcClient) GetSingleConversationRecvMsgOpt(ctx context.Context, userID, conversationID string) (int32, error) { +func (c *ConversationRpcClient) GetSingleConversationRecvMsgOpt( + ctx context.Context, + userID, conversationID string, +) (int32, error) { var req pbConversation.GetConversationReq req.OwnerUserID = userID req.ConversationID = conversationID @@ -49,21 +56,51 @@ func (c *ConversationRpcClient) GetSingleConversationRecvMsgOpt(ctx context.Cont } func (c *ConversationRpcClient) SingleChatFirstCreateConversation(ctx context.Context, recvID, sendID string) error { - _, err := c.Client.CreateSingleChatConversations(ctx, &pbConversation.CreateSingleChatConversationsReq{RecvID: recvID, SendID: sendID}) + _, err := c.Client.CreateSingleChatConversations( + ctx, + &pbConversation.CreateSingleChatConversationsReq{RecvID: recvID, SendID: sendID}, + ) return err } -func (c *ConversationRpcClient) GroupChatFirstCreateConversation(ctx context.Context, groupID string, userIDs []string) error { - _, err := c.Client.CreateGroupChatConversations(ctx, &pbConversation.CreateGroupChatConversationsReq{UserIDs: userIDs, GroupID: groupID}) +func (c *ConversationRpcClient) GroupChatFirstCreateConversation( + ctx context.Context, + groupID string, + userIDs []string, +) error { + _, err := c.Client.CreateGroupChatConversations( + ctx, + &pbConversation.CreateGroupChatConversationsReq{UserIDs: userIDs, GroupID: groupID}, + ) return err } -func (c *ConversationRpcClient) SetConversationMaxSeq(ctx context.Context, ownerUserIDs []string, conversationID string, maxSeq int64) error { - _, err := c.Client.SetConversationMaxSeq(ctx, &pbConversation.SetConversationMaxSeqReq{OwnerUserID: ownerUserIDs, ConversationID: conversationID, MaxSeq: maxSeq}) +func (c *ConversationRpcClient) SetConversationMaxSeq( + ctx context.Context, + ownerUserIDs []string, + conversationID string, + maxSeq int64, +) error { + _, err := c.Client.SetConversationMaxSeq( + ctx, + &pbConversation.SetConversationMaxSeqReq{ + OwnerUserID: ownerUserIDs, + ConversationID: conversationID, + MaxSeq: maxSeq, + }, + ) return err } -func (c *ConversationRpcClient) SetConversations(ctx context.Context, userIDs []string, conversation *pbConversation.ConversationReq) error { - _, err := c.Client.SetConversations(ctx, &pbConversation.SetConversationsReq{UserIDs: userIDs, Conversation: conversation}) + +func (c *ConversationRpcClient) SetConversations( + ctx context.Context, + userIDs []string, + conversation *pbConversation.ConversationReq, +) error { + _, err := c.Client.SetConversations( + ctx, + &pbConversation.SetConversationsReq{UserIDs: userIDs, Conversation: conversation}, + ) return err } @@ -75,16 +112,28 @@ func (c *ConversationRpcClient) GetConversationIDs(ctx context.Context, ownerUse return resp.ConversationIDs, nil } -func (c *ConversationRpcClient) GetConversation(ctx context.Context, ownerUserID, conversationID string) (*pbConversation.Conversation, error) { - resp, err := c.Client.GetConversation(ctx, &pbConversation.GetConversationReq{OwnerUserID: ownerUserID, ConversationID: conversationID}) +func (c *ConversationRpcClient) GetConversation( + ctx context.Context, + ownerUserID, conversationID string, +) (*pbConversation.Conversation, error) { + resp, err := c.Client.GetConversation( + ctx, + &pbConversation.GetConversationReq{OwnerUserID: ownerUserID, ConversationID: conversationID}, + ) if err != nil { return nil, err } return resp.Conversation, nil } -func (c *ConversationRpcClient) GetConversationsByConversationID(ctx context.Context, conversationIDs []string) ([]*pbConversation.Conversation, error) { - resp, err := c.Client.GetConversationsByConversationID(ctx, &pbConversation.GetConversationsByConversationIDReq{ConversationIDs: conversationIDs}) +func (c *ConversationRpcClient) GetConversationsByConversationID( + ctx context.Context, + conversationIDs []string, +) ([]*pbConversation.Conversation, error) { + resp, err := c.Client.GetConversationsByConversationID( + ctx, + &pbConversation.GetConversationsByConversationIDReq{ConversationIDs: conversationIDs}, + ) if err != nil { return nil, err } @@ -94,8 +143,15 @@ func (c *ConversationRpcClient) GetConversationsByConversationID(ctx context.Con return resp.Conversations, nil } -func (c *ConversationRpcClient) GetConversations(ctx context.Context, ownerUserID string, conversationIDs []string) ([]*pbConversation.Conversation, error) { - resp, err := c.Client.GetConversations(ctx, &pbConversation.GetConversationsReq{OwnerUserID: ownerUserID, ConversationIDs: conversationIDs}) +func (c *ConversationRpcClient) GetConversations( + ctx context.Context, + ownerUserID string, + conversationIDs []string, +) ([]*pbConversation.Conversation, error) { + resp, err := c.Client.GetConversations( + ctx, + &pbConversation.GetConversationsReq{OwnerUserID: ownerUserID, ConversationIDs: conversationIDs}, + ) if err != nil { return nil, err } diff --git a/pkg/rpcclient/friend.go b/pkg/rpcclient/friend.go index aa7ba0344..56875dc83 100644 --- a/pkg/rpcclient/friend.go +++ b/pkg/rpcclient/friend.go @@ -3,11 +3,12 @@ package rpcclient import ( "context" + "google.golang.org/grpc" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/friend" sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" - "google.golang.org/grpc" ) type Friend struct { @@ -31,8 +32,14 @@ func NewFriendRpcClient(discov discoveryregistry.SvcDiscoveryRegistry) FriendRpc return FriendRpcClient(*NewFriend(discov)) } -func (f *FriendRpcClient) GetFriendsInfo(ctx context.Context, ownerUserID, friendUserID string) (resp *sdkws.FriendInfo, err error) { - r, err := f.Client.GetDesignatedFriends(ctx, &friend.GetDesignatedFriendsReq{OwnerUserID: ownerUserID, FriendUserIDs: []string{friendUserID}}) +func (f *FriendRpcClient) GetFriendsInfo( + ctx context.Context, + ownerUserID, friendUserID string, +) (resp *sdkws.FriendInfo, err error) { + r, err := f.Client.GetDesignatedFriends( + ctx, + &friend.GetDesignatedFriendsReq{OwnerUserID: ownerUserID, FriendUserIDs: []string{friendUserID}}, + ) if err != nil { return nil, err } diff --git a/pkg/rpcclient/group.go b/pkg/rpcclient/group.go index 911887dda..399be6a77 100644 --- a/pkg/rpcclient/group.go +++ b/pkg/rpcclient/group.go @@ -4,6 +4,8 @@ import ( "context" "strings" + "google.golang.org/grpc" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" @@ -11,7 +13,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/group" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "google.golang.org/grpc" ) type Group struct { @@ -35,7 +36,11 @@ func NewGroupRpcClient(discov discoveryregistry.SvcDiscoveryRegistry) GroupRpcCl return GroupRpcClient(*NewGroup(discov)) } -func (g *GroupRpcClient) GetGroupInfos(ctx context.Context, groupIDs []string, complete bool) ([]*sdkws.GroupInfo, error) { +func (g *GroupRpcClient) GetGroupInfos( + ctx context.Context, + groupIDs []string, + complete bool, +) ([]*sdkws.GroupInfo, error) { resp, err := g.Client.GetGroupsInfo(ctx, &group.GetGroupsInfoReq{ GroupIDs: groupIDs, }) @@ -60,7 +65,11 @@ func (g *GroupRpcClient) GetGroupInfo(ctx context.Context, groupID string) (*sdk return groups[0], nil } -func (g *GroupRpcClient) GetGroupInfoMap(ctx context.Context, groupIDs []string, complete bool) (map[string]*sdkws.GroupInfo, error) { +func (g *GroupRpcClient) GetGroupInfoMap( + ctx context.Context, + groupIDs []string, + complete bool, +) (map[string]*sdkws.GroupInfo, error) { groups, err := g.GetGroupInfos(ctx, groupIDs, complete) if err != nil { return nil, err @@ -70,7 +79,12 @@ func (g *GroupRpcClient) GetGroupInfoMap(ctx context.Context, groupIDs []string, }), nil } -func (g *GroupRpcClient) GetGroupMemberInfos(ctx context.Context, groupID string, userIDs []string, complete bool) ([]*sdkws.GroupMemberFullInfo, error) { +func (g *GroupRpcClient) GetGroupMemberInfos( + ctx context.Context, + groupID string, + userIDs []string, + complete bool, +) ([]*sdkws.GroupMemberFullInfo, error) { resp, err := g.Client.GetGroupMembersInfo(ctx, &group.GetGroupMembersInfoReq{ GroupID: groupID, UserIDs: userIDs, @@ -88,7 +102,11 @@ func (g *GroupRpcClient) GetGroupMemberInfos(ctx context.Context, groupID string return resp.Members, nil } -func (g *GroupRpcClient) GetGroupMemberInfo(ctx context.Context, groupID string, userID string) (*sdkws.GroupMemberFullInfo, error) { +func (g *GroupRpcClient) GetGroupMemberInfo( + ctx context.Context, + groupID string, + userID string, +) (*sdkws.GroupMemberFullInfo, error) { members, err := g.GetGroupMemberInfos(ctx, groupID, []string{userID}, true) if err != nil { return nil, err @@ -96,7 +114,12 @@ func (g *GroupRpcClient) GetGroupMemberInfo(ctx context.Context, groupID string, return members[0], nil } -func (g *GroupRpcClient) GetGroupMemberInfoMap(ctx context.Context, groupID string, userIDs []string, complete bool) (map[string]*sdkws.GroupMemberFullInfo, error) { +func (g *GroupRpcClient) GetGroupMemberInfoMap( + ctx context.Context, + groupID string, + userIDs []string, + complete bool, +) (map[string]*sdkws.GroupMemberFullInfo, error) { members, err := g.GetGroupMemberInfos(ctx, groupID, userIDs, true) if err != nil { return nil, err @@ -106,7 +129,10 @@ func (g *GroupRpcClient) GetGroupMemberInfoMap(ctx context.Context, groupID stri }), nil } -func (g *GroupRpcClient) GetOwnerAndAdminInfos(ctx context.Context, groupID string) ([]*sdkws.GroupMemberFullInfo, error) { +func (g *GroupRpcClient) GetOwnerAndAdminInfos( + ctx context.Context, + groupID string, +) ([]*sdkws.GroupMemberFullInfo, error) { resp, err := g.Client.GetGroupMemberRoleLevel(ctx, &group.GetGroupMemberRoleLevelReq{ GroupID: groupID, RoleLevels: []int32{constant.GroupOwner, constant.GroupAdmin}, @@ -145,7 +171,11 @@ func (g *GroupRpcClient) GetGroupInfoCache(ctx context.Context, groupID string) return resp.GroupInfo, nil } -func (g *GroupRpcClient) GetGroupMemberCache(ctx context.Context, groupID string, groupMemberID string) (*sdkws.GroupMemberFullInfo, error) { +func (g *GroupRpcClient) GetGroupMemberCache( + ctx context.Context, + groupID string, + groupMemberID string, +) (*sdkws.GroupMemberFullInfo, error) { resp, err := g.Client.GetGroupMemberCache(ctx, &group.GetGroupMemberCacheReq{ GroupID: groupID, GroupMemberID: groupMemberID, diff --git a/pkg/rpcclient/msg.go b/pkg/rpcclient/msg.go index 8c08d0db4..43f30361a 100644 --- a/pkg/rpcclient/msg.go +++ b/pkg/rpcclient/msg.go @@ -4,6 +4,9 @@ import ( "context" "encoding/json" + "google.golang.org/grpc" + "google.golang.org/protobuf/proto" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" @@ -11,8 +14,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "google.golang.org/grpc" - "google.golang.org/protobuf/proto" // "google.golang.org/protobuf/proto" ) @@ -134,7 +135,10 @@ func (m *MessageRpcClient) GetMaxSeq(ctx context.Context, req *sdkws.GetMaxSeqRe return resp, err } -func (m *MessageRpcClient) PullMessageBySeqList(ctx context.Context, req *sdkws.PullMessageBySeqsReq) (*sdkws.PullMessageBySeqsResp, error) { +func (m *MessageRpcClient) PullMessageBySeqList( + ctx context.Context, + req *sdkws.PullMessageBySeqsReq, +) (*sdkws.PullMessageBySeqsResp, error) { resp, err := m.Client.PullMessageBySeqs(ctx, req) return resp, err } @@ -155,7 +159,9 @@ type NotificationSender struct { type NewNotificationSenderOptions func(*NotificationSender) -func WithLocalSendMsg(sendMsg func(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error)) NewNotificationSenderOptions { +func WithLocalSendMsg( + sendMsg func(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error), +) NewNotificationSenderOptions { return func(s *NotificationSender) { s.sendMsg = sendMsg } @@ -168,18 +174,39 @@ func WithRpcClient(msgRpcClient *MessageRpcClient) NewNotificationSenderOptions } func NewNotificationSender(opts ...NewNotificationSenderOptions) *NotificationSender { - notificationSender := &NotificationSender{contentTypeConf: newContentTypeConf(), sessionTypeConf: newSessionTypeConf()} + notificationSender := &NotificationSender{ + contentTypeConf: newContentTypeConf(), + sessionTypeConf: newSessionTypeConf(), + } for _, opt := range opts { opt(notificationSender) } return notificationSender } -func (s *NotificationSender) NotificationWithSesstionType(ctx context.Context, sendID, recvID string, contentType, sesstionType int32, m proto.Message, opts ...utils.OptionsOpt) (err error) { +func (s *NotificationSender) NotificationWithSesstionType( + ctx context.Context, + sendID, recvID string, + contentType, sesstionType int32, + m proto.Message, + opts ...utils.OptionsOpt, +) (err error) { n := sdkws.NotificationElem{Detail: utils.StructToJsonString(m)} content, err := json.Marshal(&n) if err != nil { - log.ZError(ctx, "MsgClient Notification json.Marshal failed", err, "sendID", sendID, "recvID", recvID, "contentType", contentType, "msg", m) + log.ZError( + ctx, + "MsgClient Notification json.Marshal failed", + err, + "sendID", + sendID, + "recvID", + recvID, + "contentType", + contentType, + "msg", + m, + ) return err } var req msg.SendMsgReq @@ -214,6 +241,12 @@ func (s *NotificationSender) NotificationWithSesstionType(ctx context.Context, s return err } -func (s *NotificationSender) Notification(ctx context.Context, sendID, recvID string, contentType int32, m proto.Message, opts ...utils.OptionsOpt) error { +func (s *NotificationSender) Notification( + ctx context.Context, + sendID, recvID string, + contentType int32, + m proto.Message, + opts ...utils.OptionsOpt, +) error { return s.NotificationWithSesstionType(ctx, sendID, recvID, contentType, s.sessionTypeConf[contentType], m, opts...) } diff --git a/pkg/rpcclient/notification/conevrsation.go b/pkg/rpcclient/notification/conevrsation.go index cc30f46f3..8fb11ba97 100644 --- a/pkg/rpcclient/notification/conevrsation.go +++ b/pkg/rpcclient/notification/conevrsation.go @@ -17,7 +17,11 @@ func NewConversationNotificationSender(msgRpcClient *rpcclient.MessageRpcClient) } // SetPrivate调用 -func (c *ConversationNotificationSender) ConversationSetPrivateNotification(ctx context.Context, sendID, recvID string, isPrivateChat bool) error { +func (c *ConversationNotificationSender) ConversationSetPrivateNotification( + ctx context.Context, + sendID, recvID string, + isPrivateChat bool, +) error { tips := &sdkws.ConversationSetPrivateTips{ RecvID: recvID, SendID: sendID, @@ -35,7 +39,11 @@ func (c *ConversationNotificationSender) ConversationChangeNotification(ctx cont } // 会话未读数同步 -func (c *ConversationNotificationSender) ConversationUnreadChangeNotification(ctx context.Context, userID, conversationID string, unreadCountTime, hasReadSeq int64) error { +func (c *ConversationNotificationSender) ConversationUnreadChangeNotification( + ctx context.Context, + userID, conversationID string, + unreadCountTime, hasReadSeq int64, +) error { tips := &sdkws.ConversationHasReadTips{ UserID: userID, ConversationID: conversationID, diff --git a/pkg/rpcclient/notification/extend_msg.go b/pkg/rpcclient/notification/extend_msg.go index e71ada3e1..1bef5c9b4 100644 --- a/pkg/rpcclient/notification/extend_msg.go +++ b/pkg/rpcclient/notification/extend_msg.go @@ -4,6 +4,8 @@ import ( "context" "encoding/json" + "google.golang.org/protobuf/proto" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" @@ -12,7 +14,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "google.golang.org/protobuf/proto" ) type ExtendMsgNotificationSender struct { @@ -23,8 +24,16 @@ func NewExtendMsgNotificationSender(client discoveryregistry.SvcDiscoveryRegistr return &ExtendMsgNotificationSender{} } -func (e *ExtendMsgNotificationSender) ExtendMessageUpdatedNotification(ctx context.Context, sendID string, conversationID string, sessionType int32, - req *msg.SetMessageReactionExtensionsReq, resp *msg.SetMessageReactionExtensionsResp, isHistory bool, isReactionFromCache bool) { +func (e *ExtendMsgNotificationSender) ExtendMessageUpdatedNotification( + ctx context.Context, + sendID string, + conversationID string, + sessionType int32, + req *msg.SetMessageReactionExtensionsReq, + resp *msg.SetMessageReactionExtensionsResp, + isHistory bool, + isReactionFromCache bool, +) { var content sdkws.ReactionMessageModifierNotification content.ConversationID = req.ConversationID content.OpUserID = mcontext.GetOpUserID(ctx) @@ -43,10 +52,28 @@ func (e *ExtendMsgNotificationSender) ExtendMessageUpdatedNotification(ctx conte content.IsReact = resp.IsReact content.IsExternalExtensions = req.IsExternalExtensions content.MsgFirstModifyTime = resp.MsgFirstModifyTime - e.messageReactionSender(ctx, sendID, conversationID, sessionType, constant.ReactionMessageModifier, &content, isHistory, isReactionFromCache) + e.messageReactionSender( + ctx, + sendID, + conversationID, + sessionType, + constant.ReactionMessageModifier, + &content, + isHistory, + isReactionFromCache, + ) } -func (e *ExtendMsgNotificationSender) ExtendMessageDeleteNotification(ctx context.Context, sendID string, conversationID string, sessionType int32, - req *msg.DeleteMessagesReactionExtensionsReq, resp *msg.DeleteMessagesReactionExtensionsResp, isHistory bool, isReactionFromCache bool) { + +func (e *ExtendMsgNotificationSender) ExtendMessageDeleteNotification( + ctx context.Context, + sendID string, + conversationID string, + sessionType int32, + req *msg.DeleteMessagesReactionExtensionsReq, + resp *msg.DeleteMessagesReactionExtensionsResp, + isHistory bool, + isReactionFromCache bool, +) { var content sdkws.ReactionMessageDeleteNotification content.ConversationID = req.ConversationID content.OpUserID = req.OpUserID @@ -63,9 +90,27 @@ func (e *ExtendMsgNotificationSender) ExtendMessageDeleteNotification(ctx contex content.SuccessReactionExtensions = keyMap content.ClientMsgID = req.ClientMsgID content.MsgFirstModifyTime = req.MsgFirstModifyTime - e.messageReactionSender(ctx, sendID, conversationID, sessionType, constant.ReactionMessageDeleter, &content, isHistory, isReactionFromCache) + e.messageReactionSender( + ctx, + sendID, + conversationID, + sessionType, + constant.ReactionMessageDeleter, + &content, + isHistory, + isReactionFromCache, + ) } -func (e *ExtendMsgNotificationSender) messageReactionSender(ctx context.Context, sendID string, conversationID string, sessionType, contentType int32, m proto.Message, isHistory bool, isReactionFromCache bool) error { + +func (e *ExtendMsgNotificationSender) messageReactionSender( + ctx context.Context, + sendID string, + conversationID string, + sessionType, contentType int32, + m proto.Message, + isHistory bool, + isReactionFromCache bool, +) error { options := make(map[string]bool, 5) utils.SetSwitchFromOptions(options, constant.IsOfflinePush, false) utils.SetSwitchFromOptions(options, constant.IsConversationUpdate, false) diff --git a/pkg/rpcclient/notification/friend.go b/pkg/rpcclient/notification/friend.go index 18f72203f..495238b03 100644 --- a/pkg/rpcclient/notification/friend.go +++ b/pkg/rpcclient/notification/friend.go @@ -30,7 +30,9 @@ func WithFriendDB(db controller.FriendDatabase) friendNotificationSenderOptions } } -func WithDBFunc(fn func(ctx context.Context, userIDs []string) (users []*relationTb.UserModel, err error)) friendNotificationSenderOptions { +func WithDBFunc( + fn func(ctx context.Context, userIDs []string) (users []*relationTb.UserModel, err error), +) friendNotificationSenderOptions { return func(s *FriendNotificationSender) { f := func(ctx context.Context, userIDs []string) (result []CommonUser, err error) { users, err := fn(ctx, userIDs) @@ -46,7 +48,9 @@ func WithDBFunc(fn func(ctx context.Context, userIDs []string) (users []*relatio } } -func WithRpcFunc(fn func(ctx context.Context, userIDs []string) ([]*sdkws.UserInfo, error)) friendNotificationSenderOptions { +func WithRpcFunc( + fn func(ctx context.Context, userIDs []string) ([]*sdkws.UserInfo, error), +) friendNotificationSenderOptions { return func(s *FriendNotificationSender) { f := func(ctx context.Context, userIDs []string) (result []CommonUser, err error) { users, err := fn(ctx, userIDs) @@ -62,7 +66,10 @@ func WithRpcFunc(fn func(ctx context.Context, userIDs []string) ([]*sdkws.UserIn } } -func NewFriendNotificationSender(msgRpcClient *rpcclient.MessageRpcClient, opts ...friendNotificationSenderOptions) *FriendNotificationSender { +func NewFriendNotificationSender( + msgRpcClient *rpcclient.MessageRpcClient, + opts ...friendNotificationSenderOptions, +) *FriendNotificationSender { f := &FriendNotificationSender{ NotificationSender: rpcclient.NewNotificationSender(rpcclient.WithRpcClient(msgRpcClient)), } @@ -72,7 +79,10 @@ func NewFriendNotificationSender(msgRpcClient *rpcclient.MessageRpcClient, opts return f } -func (f *FriendNotificationSender) getUsersInfoMap(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error) { +func (f *FriendNotificationSender) getUsersInfoMap( + ctx context.Context, + userIDs []string, +) (map[string]*sdkws.UserInfo, error) { users, err := f.getUsersInfo(ctx, userIDs) if err != nil { return nil, err @@ -84,7 +94,10 @@ func (f *FriendNotificationSender) getUsersInfoMap(ctx context.Context, userIDs return result, nil } -func (f *FriendNotificationSender) getFromToUserNickname(ctx context.Context, fromUserID, toUserID string) (string, string, error) { +func (f *FriendNotificationSender) getFromToUserNickname( + ctx context.Context, + fromUserID, toUserID string, +) (string, string, error) { users, err := f.getUsersInfoMap(ctx, []string{fromUserID, toUserID}) if err != nil { return "", "", nil @@ -97,7 +110,10 @@ func (f *FriendNotificationSender) UserInfoUpdatedNotification(ctx context.Conte return f.Notification(ctx, mcontext.GetOpUserID(ctx), changedUserID, constant.UserInfoUpdatedNotification, &tips) } -func (f *FriendNotificationSender) FriendApplicationAddNotification(ctx context.Context, req *pbFriend.ApplyToAddFriendReq) error { +func (f *FriendNotificationSender) FriendApplicationAddNotification( + ctx context.Context, + req *pbFriend.ApplyToAddFriendReq, +) error { tips := sdkws.FriendApplicationTips{FromToUserID: &sdkws.FromToUserID{ FromUserID: req.FromUserID, ToUserID: req.ToUserID, @@ -105,7 +121,10 @@ func (f *FriendNotificationSender) FriendApplicationAddNotification(ctx context. return f.Notification(ctx, req.FromUserID, req.ToUserID, constant.FriendApplicationNotification, &tips) } -func (c *FriendNotificationSender) FriendApplicationAgreedNotification(ctx context.Context, req *pbFriend.RespondFriendApplyReq) error { +func (c *FriendNotificationSender) FriendApplicationAgreedNotification( + ctx context.Context, + req *pbFriend.RespondFriendApplyReq, +) error { tips := sdkws.FriendApplicationApprovedTips{FromToUserID: &sdkws.FromToUserID{ FromUserID: req.FromUserID, ToUserID: req.ToUserID, @@ -113,7 +132,10 @@ func (c *FriendNotificationSender) FriendApplicationAgreedNotification(ctx conte return c.Notification(ctx, req.ToUserID, req.FromUserID, constant.FriendApplicationApprovedNotification, &tips) } -func (c *FriendNotificationSender) FriendApplicationRefusedNotification(ctx context.Context, req *pbFriend.RespondFriendApplyReq) error { +func (c *FriendNotificationSender) FriendApplicationRefusedNotification( + ctx context.Context, + req *pbFriend.RespondFriendApplyReq, +) error { tips := sdkws.FriendApplicationApprovedTips{FromToUserID: &sdkws.FromToUserID{ FromUserID: req.FromUserID, ToUserID: req.ToUserID, @@ -121,7 +143,10 @@ func (c *FriendNotificationSender) FriendApplicationRefusedNotification(ctx cont return c.Notification(ctx, req.ToUserID, req.FromUserID, constant.FriendApplicationRejectedNotification, &tips) } -func (c *FriendNotificationSender) FriendAddedNotification(ctx context.Context, operationID, opUserID, fromUserID, toUserID string) error { +func (c *FriendNotificationSender) FriendAddedNotification( + ctx context.Context, + operationID, opUserID, fromUserID, toUserID string, +) error { tips := sdkws.FriendAddedTips{Friend: &sdkws.FriendInfo{}, OpUser: &sdkws.PublicUserInfo{}} user, err := c.getUsersInfo(ctx, []string{opUserID}) if err != nil { @@ -172,7 +197,11 @@ func (c *FriendNotificationSender) BlackDeletedNotification(ctx context.Context, c.Notification(ctx, req.OwnerUserID, req.BlackUserID, constant.BlackDeletedNotification, &blackDeletedTips) } -func (c *FriendNotificationSender) FriendInfoUpdatedNotification(ctx context.Context, changedUserID string, needNotifiedUserID string) { +func (c *FriendNotificationSender) FriendInfoUpdatedNotification( + ctx context.Context, + changedUserID string, + needNotifiedUserID string, +) { tips := sdkws.UserInfoUpdatedTips{UserID: changedUserID} c.Notification(ctx, mcontext.GetOpUserID(ctx), needNotifiedUserID, constant.FriendInfoUpdatedNotification, &tips) } diff --git a/pkg/rpcclient/notification/group.go b/pkg/rpcclient/notification/group.go index 5175f3740..f93f0786a 100644 --- a/pkg/rpcclient/notification/group.go +++ b/pkg/rpcclient/notification/group.go @@ -16,7 +16,11 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" ) -func NewGroupNotificationSender(db controller.GroupDatabase, msgRpcClient *rpcclient.MessageRpcClient, fn func(ctx context.Context, userIDs []string) ([]CommonUser, error)) *GroupNotificationSender { +func NewGroupNotificationSender( + db controller.GroupDatabase, + msgRpcClient *rpcclient.MessageRpcClient, + fn func(ctx context.Context, userIDs []string) ([]CommonUser, error), +) *GroupNotificationSender { return &GroupNotificationSender{ NotificationSender: rpcclient.NewNotificationSender(rpcclient.WithRpcClient(msgRpcClient)), getUsersInfo: fn, @@ -80,7 +84,11 @@ func (g *GroupNotificationSender) getGroupInfo(ctx context.Context, groupID stri }, nil } -func (g *GroupNotificationSender) getGroupMembers(ctx context.Context, groupID string, userIDs []string) ([]*sdkws.GroupMemberFullInfo, error) { +func (g *GroupNotificationSender) getGroupMembers( + ctx context.Context, + groupID string, + userIDs []string, +) ([]*sdkws.GroupMemberFullInfo, error) { members, err := g.db.FindGroupMember(ctx, []string{groupID}, userIDs, nil) if err != nil { return nil, err @@ -95,7 +103,9 @@ func (g *GroupNotificationSender) getGroupMembers(ctx context.Context, groupID s for _, member := range members { user, ok := users[member.UserID] if !ok { - return nil, errs.ErrUserIDNotFound.Wrap(fmt.Sprintf("group %s member %s not in user", member.GroupID, member.UserID)) + return nil, errs.ErrUserIDNotFound.Wrap( + fmt.Sprintf("group %s member %s not in user", member.GroupID, member.UserID), + ) } if member.Nickname == "" { member.Nickname = user.Nickname @@ -117,7 +127,11 @@ func (g *GroupNotificationSender) getGroupMembers(ctx context.Context, groupID s return res, nil } -func (g *GroupNotificationSender) getGroupMemberMap(ctx context.Context, groupID string, userIDs []string) (map[string]*sdkws.GroupMemberFullInfo, error) { +func (g *GroupNotificationSender) getGroupMemberMap( + ctx context.Context, + groupID string, + userIDs []string, +) (map[string]*sdkws.GroupMemberFullInfo, error) { members, err := g.getGroupMembers(ctx, groupID, userIDs) if err != nil { return nil, err @@ -129,7 +143,11 @@ func (g *GroupNotificationSender) getGroupMemberMap(ctx context.Context, groupID return m, nil } -func (g *GroupNotificationSender) getGroupMember(ctx context.Context, groupID string, userID string) (*sdkws.GroupMemberFullInfo, error) { +func (g *GroupNotificationSender) getGroupMember( + ctx context.Context, + groupID string, + userID string, +) (*sdkws.GroupMemberFullInfo, error) { members, err := g.getGroupMembers(ctx, groupID, []string{userID}) if err != nil { return nil, err @@ -149,7 +167,11 @@ func (g *GroupNotificationSender) getGroupOwnerAndAdminUserID(ctx context.Contex return utils.Slice(members, fn), nil } -func (g *GroupNotificationSender) groupDB2PB(group *relation.GroupModel, ownerUserID string, memberCount uint32) *sdkws.GroupInfo { +func (g *GroupNotificationSender) groupDB2PB( + group *relation.GroupModel, + ownerUserID string, + memberCount uint32, +) *sdkws.GroupInfo { return &sdkws.GroupInfo{ GroupID: group.GroupID, GroupName: group.GroupName, @@ -171,7 +193,10 @@ func (g *GroupNotificationSender) groupDB2PB(group *relation.GroupModel, ownerUs } } -func (g *GroupNotificationSender) groupMemberDB2PB(member *relation.GroupMemberModel, appMangerLevel int32) *sdkws.GroupMemberFullInfo { +func (g *GroupNotificationSender) groupMemberDB2PB( + member *relation.GroupMemberModel, + appMangerLevel int32, +) *sdkws.GroupMemberFullInfo { return &sdkws.GroupMemberFullInfo{ GroupID: member.GroupID, UserID: member.UserID, @@ -188,7 +213,10 @@ func (g *GroupNotificationSender) groupMemberDB2PB(member *relation.GroupMemberM } } -func (g *GroupNotificationSender) getUsersInfoMap(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error) { +func (g *GroupNotificationSender) getUsersInfoMap( + ctx context.Context, + userIDs []string, +) (map[string]*sdkws.UserInfo, error) { users, err := g.getUsersInfo(ctx, userIDs) if err != nil { return nil, err @@ -200,7 +228,11 @@ func (g *GroupNotificationSender) getUsersInfoMap(ctx context.Context, userIDs [ return result, nil } -func (g *GroupNotificationSender) fillOpUser(ctx context.Context, opUser **sdkws.GroupMemberFullInfo, groupID string) error { +func (g *GroupNotificationSender) fillOpUser( + ctx context.Context, + opUser **sdkws.GroupMemberFullInfo, + groupID string, +) error { if opUser == nil { return errs.ErrInternalServer.Wrap("**sdkws.GroupMemberFullInfo is nil") } @@ -239,35 +271,62 @@ func (g *GroupNotificationSender) fillOpUser(ctx context.Context, opUser **sdkws return nil } -func (g *GroupNotificationSender) GroupCreatedNotification(ctx context.Context, tips *sdkws.GroupCreatedTips) (err error) { +func (g *GroupNotificationSender) GroupCreatedNotification( + ctx context.Context, + tips *sdkws.GroupCreatedTips, +) (err error) { if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } return g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupCreatedNotification, tips) } -func (g *GroupNotificationSender) GroupInfoSetNotification(ctx context.Context, tips *sdkws.GroupInfoSetTips) (err error) { +func (g *GroupNotificationSender) GroupInfoSetNotification( + ctx context.Context, + tips *sdkws.GroupInfoSetTips, +) (err error) { if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } return g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetNotification, tips) } -func (g *GroupNotificationSender) GroupInfoSetNameNotification(ctx context.Context, tips *sdkws.GroupInfoSetNameTips) (err error) { +func (g *GroupNotificationSender) GroupInfoSetNameNotification( + ctx context.Context, + tips *sdkws.GroupInfoSetNameTips, +) (err error) { if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } - return g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetNameNotification, tips) + return g.Notification( + ctx, + mcontext.GetOpUserID(ctx), + tips.Group.GroupID, + constant.GroupInfoSetNameNotification, + tips, + ) } -func (g *GroupNotificationSender) GroupInfoSetAnnouncementNotification(ctx context.Context, tips *sdkws.GroupInfoSetAnnouncementTips) (err error) { +func (g *GroupNotificationSender) GroupInfoSetAnnouncementNotification( + ctx context.Context, + tips *sdkws.GroupInfoSetAnnouncementTips, +) (err error) { if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } - return g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetAnnouncementNotification, tips) + return g.Notification( + ctx, + mcontext.GetOpUserID(ctx), + tips.Group.GroupID, + constant.GroupInfoSetAnnouncementNotification, + tips, + ) } -func (g *GroupNotificationSender) JoinGroupApplicationNotification(ctx context.Context, req *pbGroup.JoinGroupReq) (err error) { +func (g *GroupNotificationSender) JoinGroupApplicationNotification( + ctx context.Context, + req *pbGroup.JoinGroupReq, +) (err error) { group, err := g.getGroupInfo(ctx, req.GroupID) if err != nil { return err @@ -291,7 +350,10 @@ func (g *GroupNotificationSender) JoinGroupApplicationNotification(ctx context.C return nil } -func (g *GroupNotificationSender) MemberQuitNotification(ctx context.Context, member *sdkws.GroupMemberFullInfo) (err error) { +func (g *GroupNotificationSender) MemberQuitNotification( + ctx context.Context, + member *sdkws.GroupMemberFullInfo, +) (err error) { defer log.ZDebug(ctx, "return") defer func() { if err != nil { @@ -306,7 +368,10 @@ func (g *GroupNotificationSender) MemberQuitNotification(ctx context.Context, me return g.Notification(ctx, mcontext.GetOpUserID(ctx), member.GroupID, constant.MemberQuitNotification, tips) } -func (g *GroupNotificationSender) GroupApplicationAcceptedNotification(ctx context.Context, req *pbGroup.GroupApplicationResponseReq) (err error) { +func (g *GroupNotificationSender) GroupApplicationAcceptedNotification( + ctx context.Context, + req *pbGroup.GroupApplicationResponseReq, +) (err error) { defer log.ZDebug(ctx, "return") defer func() { if err != nil { @@ -326,7 +391,13 @@ func (g *GroupNotificationSender) GroupApplicationAcceptedNotification(ctx conte return err } for _, userID := range append(userIDs, mcontext.GetOpUserID(ctx)) { - err = g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.GroupApplicationAcceptedNotification, tips) + err = g.Notification( + ctx, + mcontext.GetOpUserID(ctx), + userID, + constant.GroupApplicationAcceptedNotification, + tips, + ) if err != nil { log.ZError(ctx, "failed", err) } @@ -334,7 +405,10 @@ func (g *GroupNotificationSender) GroupApplicationAcceptedNotification(ctx conte return nil } -func (g *GroupNotificationSender) GroupApplicationRejectedNotification(ctx context.Context, req *pbGroup.GroupApplicationResponseReq) (err error) { +func (g *GroupNotificationSender) GroupApplicationRejectedNotification( + ctx context.Context, + req *pbGroup.GroupApplicationResponseReq, +) (err error) { group, err := g.getGroupInfo(ctx, req.GroupID) if err != nil { return err @@ -348,7 +422,13 @@ func (g *GroupNotificationSender) GroupApplicationRejectedNotification(ctx conte return err } for _, userID := range append(userIDs, mcontext.GetOpUserID(ctx)) { - err = g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.GroupApplicationRejectedNotification, tips) + err = g.Notification( + ctx, + mcontext.GetOpUserID(ctx), + userID, + constant.GroupApplicationRejectedNotification, + tips, + ) if err != nil { log.ZError(ctx, "failed", err) } @@ -356,7 +436,10 @@ func (g *GroupNotificationSender) GroupApplicationRejectedNotification(ctx conte return nil } -func (g *GroupNotificationSender) GroupOwnerTransferredNotification(ctx context.Context, req *pbGroup.TransferGroupOwnerReq) (err error) { +func (g *GroupNotificationSender) GroupOwnerTransferredNotification( + ctx context.Context, + req *pbGroup.TransferGroupOwnerReq, +) (err error) { group, err := g.getGroupInfo(ctx, req.GroupID) if err != nil { return err @@ -366,21 +449,38 @@ func (g *GroupNotificationSender) GroupOwnerTransferredNotification(ctx context. if err != nil { return err } - tips := &sdkws.GroupOwnerTransferredTips{Group: group, OpUser: member[opUserID], NewGroupOwner: member[req.NewOwnerUserID]} + tips := &sdkws.GroupOwnerTransferredTips{ + Group: group, + OpUser: member[opUserID], + NewGroupOwner: member[req.NewOwnerUserID], + } if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } - return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupOwnerTransferredNotification, tips) + return g.Notification( + ctx, + mcontext.GetOpUserID(ctx), + group.GroupID, + constant.GroupOwnerTransferredNotification, + tips, + ) } -func (g *GroupNotificationSender) MemberKickedNotification(ctx context.Context, tips *sdkws.MemberKickedTips) (err error) { +func (g *GroupNotificationSender) MemberKickedNotification( + ctx context.Context, + tips *sdkws.MemberKickedTips, +) (err error) { if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } return g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.MemberKickedNotification, tips) } -func (g *GroupNotificationSender) MemberInvitedNotification(ctx context.Context, groupID, reason string, invitedUserIDList []string) (err error) { +func (g *GroupNotificationSender) MemberInvitedNotification( + ctx context.Context, + groupID, reason string, + invitedUserIDList []string, +) (err error) { group, err := g.getGroupInfo(ctx, groupID) if err != nil { return err @@ -399,7 +499,10 @@ func (g *GroupNotificationSender) MemberInvitedNotification(ctx context.Context, return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.MemberInvitedNotification, tips) } -func (g *GroupNotificationSender) MemberEnterNotification(ctx context.Context, req *pbGroup.GroupApplicationResponseReq) (err error) { +func (g *GroupNotificationSender) MemberEnterNotification( + ctx context.Context, + req *pbGroup.GroupApplicationResponseReq, +) (err error) { group, err := g.getGroupInfo(ctx, req.GroupID) if err != nil { return err @@ -412,14 +515,21 @@ func (g *GroupNotificationSender) MemberEnterNotification(ctx context.Context, r return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.MemberEnterNotification, tips) } -func (g *GroupNotificationSender) GroupDismissedNotification(ctx context.Context, tips *sdkws.GroupDismissedTips) (err error) { +func (g *GroupNotificationSender) GroupDismissedNotification( + ctx context.Context, + tips *sdkws.GroupDismissedTips, +) (err error) { if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } return g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupDismissedNotification, tips) } -func (g *GroupNotificationSender) GroupMemberMutedNotification(ctx context.Context, groupID, groupMemberUserID string, mutedSeconds uint32) (err error) { +func (g *GroupNotificationSender) GroupMemberMutedNotification( + ctx context.Context, + groupID, groupMemberUserID string, + mutedSeconds uint32, +) (err error) { group, err := g.getGroupInfo(ctx, groupID) if err != nil { return err @@ -436,7 +546,10 @@ func (g *GroupNotificationSender) GroupMemberMutedNotification(ctx context.Conte return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberMutedNotification, tips) } -func (g *GroupNotificationSender) GroupMemberCancelMutedNotification(ctx context.Context, groupID, groupMemberUserID string) (err error) { +func (g *GroupNotificationSender) GroupMemberCancelMutedNotification( + ctx context.Context, + groupID, groupMemberUserID string, +) (err error) { group, err := g.getGroupInfo(ctx, groupID) if err != nil { return err @@ -445,11 +558,21 @@ func (g *GroupNotificationSender) GroupMemberCancelMutedNotification(ctx context if err != nil { return err } - tips := &sdkws.GroupMemberCancelMutedTips{Group: group, OpUser: user[mcontext.GetOpUserID(ctx)], MutedUser: user[groupMemberUserID]} + tips := &sdkws.GroupMemberCancelMutedTips{ + Group: group, + OpUser: user[mcontext.GetOpUserID(ctx)], + MutedUser: user[groupMemberUserID], + } if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } - return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberCancelMutedNotification, tips) + return g.Notification( + ctx, + mcontext.GetOpUserID(ctx), + group.GroupID, + constant.GroupMemberCancelMutedNotification, + tips, + ) } func (g *GroupNotificationSender) GroupMutedNotification(ctx context.Context, groupID string) (err error) { @@ -490,7 +613,10 @@ func (g *GroupNotificationSender) GroupCancelMutedNotification(ctx context.Conte return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupCancelMutedNotification, tips) } -func (g *GroupNotificationSender) GroupMemberInfoSetNotification(ctx context.Context, groupID, groupMemberUserID string) (err error) { +func (g *GroupNotificationSender) GroupMemberInfoSetNotification( + ctx context.Context, + groupID, groupMemberUserID string, +) (err error) { group, err := g.getGroupInfo(ctx, groupID) if err != nil { return err @@ -499,14 +625,21 @@ func (g *GroupNotificationSender) GroupMemberInfoSetNotification(ctx context.Con if err != nil { return err } - tips := &sdkws.GroupMemberInfoSetTips{Group: group, OpUser: user[mcontext.GetOpUserID(ctx)], ChangedUser: user[groupMemberUserID]} + tips := &sdkws.GroupMemberInfoSetTips{ + Group: group, + OpUser: user[mcontext.GetOpUserID(ctx)], + ChangedUser: user[groupMemberUserID], + } if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberInfoSetNotification, tips) } -func (g *GroupNotificationSender) GroupMemberSetToAdminNotification(ctx context.Context, groupID, groupMemberUserID string) (err error) { +func (g *GroupNotificationSender) GroupMemberSetToAdminNotification( + ctx context.Context, + groupID, groupMemberUserID string, +) (err error) { group, err := g.getGroupInfo(ctx, groupID) if err != nil { return err @@ -515,14 +648,27 @@ func (g *GroupNotificationSender) GroupMemberSetToAdminNotification(ctx context. if err != nil { return err } - tips := &sdkws.GroupMemberInfoSetTips{Group: group, OpUser: user[mcontext.GetOpUserID(ctx)], ChangedUser: user[groupMemberUserID]} + tips := &sdkws.GroupMemberInfoSetTips{ + Group: group, + OpUser: user[mcontext.GetOpUserID(ctx)], + ChangedUser: user[groupMemberUserID], + } if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } - return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberSetToAdminNotification, tips) + return g.Notification( + ctx, + mcontext.GetOpUserID(ctx), + group.GroupID, + constant.GroupMemberSetToAdminNotification, + tips, + ) } -func (g *GroupNotificationSender) GroupMemberSetToOrdinaryUserNotification(ctx context.Context, groupID, groupMemberUserID string) (err error) { +func (g *GroupNotificationSender) GroupMemberSetToOrdinaryUserNotification( + ctx context.Context, + groupID, groupMemberUserID string, +) (err error) { group, err := g.getGroupInfo(ctx, groupID) if err != nil { return err @@ -531,14 +677,28 @@ func (g *GroupNotificationSender) GroupMemberSetToOrdinaryUserNotification(ctx c if err != nil { return err } - tips := &sdkws.GroupMemberInfoSetTips{Group: group, OpUser: user[mcontext.GetOpUserID(ctx)], ChangedUser: user[groupMemberUserID]} + tips := &sdkws.GroupMemberInfoSetTips{ + Group: group, + OpUser: user[mcontext.GetOpUserID(ctx)], + ChangedUser: user[groupMemberUserID], + } if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } - return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberSetToOrdinaryUserNotification, tips) + return g.Notification( + ctx, + mcontext.GetOpUserID(ctx), + group.GroupID, + constant.GroupMemberSetToOrdinaryUserNotification, + tips, + ) } -func (g *GroupNotificationSender) MemberEnterDirectlyNotification(ctx context.Context, groupID string, entrantUserID string) (err error) { +func (g *GroupNotificationSender) MemberEnterDirectlyNotification( + ctx context.Context, + groupID string, + entrantUserID string, +) (err error) { defer log.ZDebug(ctx, "return") defer func() { if err != nil { diff --git a/pkg/rpcclient/push.go b/pkg/rpcclient/push.go index 8ac20b943..5359c31ee 100644 --- a/pkg/rpcclient/push.go +++ b/pkg/rpcclient/push.go @@ -3,10 +3,11 @@ package rpcclient import ( "context" + "google.golang.org/grpc" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/push" - "google.golang.org/grpc" ) type Push struct { @@ -33,6 +34,9 @@ func NewPushRpcClient(discov discoveryregistry.SvcDiscoveryRegistry) PushRpcClie return PushRpcClient(*NewPush(discov)) } -func (p *PushRpcClient) DelUserPushToken(ctx context.Context, req *push.DelUserPushTokenReq) (*push.DelUserPushTokenResp, error) { +func (p *PushRpcClient) DelUserPushToken( + ctx context.Context, + req *push.DelUserPushTokenReq, +) (*push.DelUserPushTokenResp, error) { return p.Client.DelUserPushToken(ctx, req) } diff --git a/pkg/rpcclient/third.go b/pkg/rpcclient/third.go index 46c5e1b92..c657c02a1 100644 --- a/pkg/rpcclient/third.go +++ b/pkg/rpcclient/third.go @@ -3,10 +3,11 @@ package rpcclient import ( "context" + "google.golang.org/grpc" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/third" - "google.golang.org/grpc" ) type Third struct { diff --git a/pkg/rpcclient/user.go b/pkg/rpcclient/user.go index dabc2bdf4..383d0cc67 100644 --- a/pkg/rpcclient/user.go +++ b/pkg/rpcclient/user.go @@ -5,6 +5,8 @@ import ( "strings" "time" + "google.golang.org/grpc" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/tokenverify" "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" @@ -12,7 +14,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/user" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - "google.golang.org/grpc" ) type User struct { @@ -70,7 +71,11 @@ func (u *UserRpcClient) GetUsersInfoMap(ctx context.Context, userIDs []string) ( }), nil } -func (u *UserRpcClient) GetPublicUserInfos(ctx context.Context, userIDs []string, complete bool) ([]*sdkws.PublicUserInfo, error) { +func (u *UserRpcClient) GetPublicUserInfos( + ctx context.Context, + userIDs []string, + complete bool, +) ([]*sdkws.PublicUserInfo, error) { users, err := u.GetUsersInfo(ctx, userIDs) if err != nil { return nil, err @@ -93,7 +98,11 @@ func (u *UserRpcClient) GetPublicUserInfo(ctx context.Context, userID string) (* return users[0], nil } -func (u *UserRpcClient) GetPublicUserInfoMap(ctx context.Context, userIDs []string, complete bool) (map[string]*sdkws.PublicUserInfo, error) { +func (u *UserRpcClient) GetPublicUserInfoMap( + ctx context.Context, + userIDs []string, + complete bool, +) (map[string]*sdkws.PublicUserInfo, error) { users, err := u.GetPublicUserInfos(ctx, userIDs, complete) if err != nil { return nil, err diff --git a/pkg/startrpc/start.go b/pkg/startrpc/start.go index c7490f4e0..de5be74e5 100644 --- a/pkg/startrpc/start.go +++ b/pkg/startrpc/start.go @@ -6,6 +6,10 @@ import ( "strconv" "time" + grpcPrometheus "github.com/grpc-ecosystem/go-grpc-prometheus" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mw" @@ -14,21 +18,38 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" openKeeper "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry/zookeeper" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - grpcPrometheus "github.com/grpc-ecosystem/go-grpc-prometheus" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" ) -func Start(rpcPort int, rpcRegisterName string, prometheusPort int, rpcFn func(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error, options ...grpc.ServerOption) error { - fmt.Println("start", rpcRegisterName, "server, port: ", rpcPort, "prometheusPort:", prometheusPort, ", OpenIM version: ", config.Version) - listener, err := net.Listen("tcp", net.JoinHostPort(network.GetListenIP(config.Config.Rpc.ListenIP), strconv.Itoa(rpcPort))) +func Start( + rpcPort int, + rpcRegisterName string, + prometheusPort int, + rpcFn func(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error, + options ...grpc.ServerOption, +) error { + fmt.Println( + "start", + rpcRegisterName, + "server, port: ", + rpcPort, + "prometheusPort:", + prometheusPort, + ", OpenIM version: ", + config.Version, + ) + listener, err := net.Listen( + "tcp", + net.JoinHostPort(network.GetListenIP(config.Config.Rpc.ListenIP), strconv.Itoa(rpcPort)), + ) if err != nil { return err } defer listener.Close() zkClient, err := openKeeper.NewClient(config.Config.Zookeeper.ZkAddr, config.Config.Zookeeper.Schema, - openKeeper.WithFreq(time.Hour), openKeeper.WithUserNameAndPassword(config.Config.Zookeeper.Username, - config.Config.Zookeeper.Password), openKeeper.WithRoundRobin(), openKeeper.WithTimeout(10), openKeeper.WithLogger(log.NewZkLogger())) + openKeeper.WithFreq(time.Hour), openKeeper.WithUserNameAndPassword( + config.Config.Zookeeper.Username, + config.Config.Zookeeper.Password, + ), openKeeper.WithRoundRobin(), openKeeper.WithTimeout(10), openKeeper.WithLogger(log.NewZkLogger())) if err != nil { return utils.Wrap1(err) } @@ -57,7 +78,12 @@ func Start(rpcPort int, rpcRegisterName string, prometheusPort int, rpcFn func(c if err != nil { return utils.Wrap1(err) } - err = zkClient.Register(rpcRegisterName, registerIP, rpcPort, grpc.WithTransportCredentials(insecure.NewCredentials())) + err = zkClient.Register( + rpcRegisterName, + registerIP, + rpcPort, + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) if err != nil { return utils.Wrap1(err) } diff --git a/pkg/statistics/statistics.go b/pkg/statistics/statistics.go index 3ec3c6aa8..87cba474c 100644 --- a/pkg/statistics/statistics.go +++ b/pkg/statistics/statistics.go @@ -31,7 +31,21 @@ func (s *Statistics) output() { intervalCount = *s.AllCount - sum } timeIntervalNum++ - log.ZWarn(context.Background(), " system stat ", nil, "args", s.PrintArgs, "intervalCount", intervalCount, "total:", *s.AllCount, "intervalNum", timeIntervalNum, "avg", (*s.AllCount)/(timeIntervalNum)/s.SleepTime) + log.ZWarn( + context.Background(), + " system stat ", + nil, + "args", + s.PrintArgs, + "intervalCount", + intervalCount, + "total:", + *s.AllCount, + "intervalNum", + timeIntervalNum, + "avg", + (*s.AllCount)/(timeIntervalNum)/s.SleepTime, + ) } } diff --git a/pkg/utils/callback.go b/pkg/utils/callback.go index 313619191..139d166c0 100644 --- a/pkg/utils/callback.go +++ b/pkg/utils/callback.go @@ -1,9 +1,10 @@ package utils import ( + "google.golang.org/protobuf/proto" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" - "google.golang.org/protobuf/proto" ) func GetContent(msg *sdkws.MsgData) string { diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index b15cb025d..1138313d2 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -9,12 +9,13 @@ import ( "strings" "time" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" - sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" "github.com/jinzhu/copier" "github.com/pkg/errors" "google.golang.org/protobuf/proto" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" + sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" ) // copy a by b b->a diff --git a/scripts/make-rules/golang.mk b/scripts/make-rules/golang.mk index b12ad0c93..f9a73b5bd 100644 --- a/scripts/make-rules/golang.mk +++ b/scripts/make-rules/golang.mk @@ -131,8 +131,7 @@ go.build.multiarch: go.build.verify $(foreach p,$(PLATFORMS),$(addprefix go.buil .PHONY: go.lint go.lint: tools.verify.golangci-lint @echo "===========> Run golangci to lint source codes" - @$(TOOLS_BIN)/golangci-lint run -c $(ROOT_DIR)/.golangci.yml $(ROOT_DIR)/... - + @$(TOOLS_DIR)/golangci-lint run --color always -c $(ROOT_DIR)/.golangci.yml $(ROOT_DIR)/... ## go.test: Run unit test .PHONY: go.test go.test: diff --git a/test/mongo/cmd/main.go b/test/mongo/cmd/main.go index 3e4017960..7873fee90 100644 --- a/test/mongo/cmd/main.go +++ b/test/mongo/cmd/main.go @@ -28,7 +28,8 @@ import ( func init() { uri := "mongodb://sample.host:27017/?maxPoolSize=20&w=majority" if config.Config.Mongo.DBUri != "" { - // example: mongodb://$user:$password@mongo1.mongo:27017,mongo2.mongo:27017,mongo3.mongo:27017/$DBDatabase/?replicaSet=rs0&readPreference=secondary&authSource=admin&maxPoolSize=$DBMaxPoolSize + // example: + // mongodb://$user:$password@mongo1.mongo:27017,mongo2.mongo:27017,mongo3.mongo:27017/$DBDatabase/?replicaSet=rs0&readPreference=secondary&authSource=admin&maxPoolSize=$DBMaxPoolSize uri = config.Config.Mongo.DBUri } else { if config.Config.Mongo.DBPassword != "" && config.Config.Mongo.DBUserName != "" { From cf6f7be56f9c6e7c0288ad7819a7ad3eb010d072 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Mon, 3 Jul 2023 17:24:18 +0800 Subject: [PATCH 04/73] feat: add code ql system Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- .github/workflows/codeql-analysis.yml | 105 ++++++++++++++------------ 1 file changed, 55 insertions(+), 50 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 38508b770..6233ffcad 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,71 +1,76 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. +# Copyright © 2023 OpenIM open source community. All rights reserved. # -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. +# http://www.apache.org/licenses/LICENSE-2.0 # -name: "CodeQL" +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +name: "Code Scanning - Action" on: push: - branches: [ main ] + branches: [main] pull_request: - # The branches below must be a subset of the branches above - branches: "*" -# schedule: -# - cron: '23 2 * * 2' + branches: [main] + schedule: + # ┌───────────── minute (0 - 59) + # │ ┌───────────── hour (0 - 23) + # │ │ ┌───────────── day of the month (1 - 31) + # │ │ │ ┌───────────── month (1 - 12 or JAN-DEC) + # │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT) + # │ │ │ │ │ + # │ │ │ │ │ + # │ │ │ │ │ + # * * * * * + - cron: '30 1 * * 0' jobs: - analyze: - name: Analyze + CodeQL-Build: + # CodeQL runs on ubuntu-latest, windows-latest, and macos-latest runs-on: ubuntu-latest + permissions: - actions: read - contents: read + # required for all workflows security-events: write - strategy: - fail-fast: false - matrix: - language: [ 'go' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] - # Learn more: - # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + # only required for workflows in private repositories + actions: read + contents: read steps: - - name: Checkout repository - uses: actions/checkout@v2 + - name: Checkout repository + uses: actions/checkout@v3 - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + # Override language selection by uncommenting this and choosing your languages + # with: + # languages: go, javascript, csharp, python, cpp, java, ruby - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). + # If this step fails, then you should remove it and run the build manually (see below). + - name: Autobuild + uses: github/codeql-action/autobuild@v2 - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language + # ✏️ If the Autobuild fails above, remove it and uncomment the following + # three lines and modify them (or add more) to build your code if your + # project uses a compiled language - #- run: | - # make bootstrap - # make release + #- run: | + # make bootstrap + # make release - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 \ No newline at end of file From 3742973e3d215727b79b34e02d5d9ee155a66eea Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Mon, 3 Jul 2023 22:48:52 +0800 Subject: [PATCH 05/73] feat: add feature Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- .github/workflows/golangci-link.yml | 51 ++++++++++++++++++ .github/workflows/image.yml | 81 +++++++++++++++++++++++++++++ CONTRIBUTING.md | 36 ++++++++----- README-zh_CN.md | 2 +- 4 files changed, 156 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/golangci-link.yml create mode 100644 .github/workflows/image.yml diff --git a/.github/workflows/golangci-link.yml b/.github/workflows/golangci-link.yml new file mode 100644 index 000000000..057dcff74 --- /dev/null +++ b/.github/workflows/golangci-link.yml @@ -0,0 +1,51 @@ +# Copyright © 2023 OpenIM open source community. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +name: OpenKF golangci-lint +on: + push: + branches: [main] + pull_request: + branches: [main] +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: '1.20' + cache: false + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + # Require: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version + version: v1.53 + + # Optional: working directory, useful for monorepos + working-directory: server + + # Optional: golangci-lint command line arguments. + # + # Note: by default the `.golangci.yml` file should be at the root of the repository. + # The location of the configuration file can be changed by using `--config=` + # args: --timeout=30m --config=/my/path/.golangci.yml --issues-exit-code=0 + + # Optional: show only new issues if it's a pull request. The default value is `false`. + only-new-issues: true + + # Optional:The mode to install golangci-lint. It can be 'binary' or 'goinstall'. + install-mode: "goinstall" \ No newline at end of file diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml new file mode 100644 index 000000000..f6d02cf63 --- /dev/null +++ b/.github/workflows/image.yml @@ -0,0 +1,81 @@ +name: Build Image + +on: + push: + branches: + - main + paths: + - "**.go" + - "!**_test.go" + - "build/**" + tags: + - v* + +jobs: + release: + strategy: + matrix: + components: [core, swagger] + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 20 + - uses: dorny/paths-filter@v2 + if: ${{ !startsWith(github.ref_name, 'v') }} + id: changes + with: + filters: | + go: + - '**.go' + - 'build/core/Dockerfile' + api: + - 'openapi/**' + - 'build/swagger/build.sh' + - 'build/swagger/Dockerfile' + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + flavor: | + latest=false + images: | + ${{ secrets.DOCKERHUB_USERNAME }}/openimsdk-${{ matrix.components }} + registry.cn-hangzhou.aliyuncs.com/${{ secrets.ALIREGISTRY_NAMESPACE }}/openimsdk-${{ matrix.components }} + tags: | + type=ref,event=branch + type=sha,prefix={{branch}}-,enable=${{ github.ref_type == 'branch' }} + type=ref,event=tag + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Login to Ali Container Registry + uses: docker/login-action@v2 + with: + registry: registry.cn-hangzhou.aliyuncs.com + username: ${{ secrets.ALIREGISTRY_USERNAME }} + password: ${{ secrets.ALIREGISTRY_TOKEN }} + + - name: Condition + id: condition + run: | + echo "run=${{ startsWith(github.ref_name, 'v') || ((steps.changes.outputs.go == 'true' && (matrix.components == 'core' || matrix.components == 'job')) || (steps.changes.outputs.api == 'true' && matrix.components == 'swagger')) }}" >> $GITHUB_OUTPUT + + - name: Set up QEMU + if: ${{ steps.condition.outputs.run == 'true' }} + uses: docker/setup-qemu-action@v2 + - name: Set up Docker Buildx + if: ${{ steps.condition.outputs.run == 'true' }} + uses: docker/setup-buildx-action@v2 + - name: Build and push + if: ${{ steps.condition.outputs.run == 'true' }} + uses: docker/build-push-action@v3 + with: + context: . + file: ./build/${{ matrix.components }}/Dockerfile + platforms: linux/amd64 + push: true + tags: ${{ steps.meta.outputs.tags }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 215721b19..21359aff6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,13 +8,23 @@ This document provides guidelines and best practices to help you contribute effe ## 📇Topics -- [What we expect of you](#What-we-expect-of-you) -- [Code of Conduct](#Code-of-Conduct) -- [Getting Started](#Getting-Started) -- [Style and Specification](#Style-and-Specification) -- [Engage to help anything](#Engage-to-help-anything) -- [Release version](#Release-version) -- [Contact Us](#Contact-Us) +- [Contributing to Open-IM-Server](#contributing-to-open-im-server) + - [📇Topics](#topics) + - [What we expect of you](#what-we-expect-of-you) + - [Code of ConductCode of Conduct](#code-of-conductcode-of-conduct) + - [Code and doc contribution](#code-and-doc-contribution) + - [Where should I start?](#where-should-i-start) + - [Design documents](#design-documents) + - [Getting Started](#getting-started) + - [Style and Specification](#style-and-specification) + - [Reporting security issues](#reporting-security-issues) + - [Reporting general issues](#reporting-general-issues) + - [Commit Rules](#commit-rules) + - [PR Description](#pr-description) + - [Docs Contribution](#docs-contribution) + - [Engage to help anything](#engage-to-help-anything) + - [Release version](#release-version) + - [Contact Us](#contact-us) ## What we expect of you @@ -32,7 +42,7 @@ The [Makefile](./Makefile) is for every developer, even if you don't know how to #### Code and doc contribution -Every action to make project Open-IM-Server better is encouraged. On GitHub, every improvement for Open-IM-Server could be via a [PR](https://github.com/Open-IM-Server/pulls) (short for pull request). +Every action to make project Open-IM-Server better is encouraged. On GitHub, every improvement for Open-IM-Server could be via a [PR](https://github.com/OpenIMSDK/Open-IM-Server/pulls) (short for pull request). + If you find a typo, try to fix it! + If you find a bug, try to fix it! @@ -48,7 +58,7 @@ Every action to make project Open-IM-Server better is encouraged. On GitHub, eve #### Where should I start? + If you are new to the project, don't know how to contribute Open-IM-Server, please check out the [good first issue](https://github.com/OpenIMSDK/Open-IM-Server/issues?q=is%3Aopen+label%3A"good+first+issue"+sort%3Aupdated-desc) label. -+ You should be good at filtering the Open-IM-Server issue tags and finding the ones you like, such as [RFC](https://github.com/OpenIMSDK/Open-IM-Server/issues?q=is%3Aissue+is%3Aopen+RFC+label%3ARFC) for big initiatives, features for [feature](https://github.com/OpenIMSDK/Open-IM-Server/issues?q=is%3Aissue+label%3Afeature) proposals, and [bug](https://github.com/{github/issues?q=is%3Aissue+label%3Abug+) fixes. ++ You should be good at filtering the Open-IM-Server issue tags and finding the ones you like, such as [RFC](https://github.com/OpenIMSDK/Open-IM-Server/issues?q=is%3Aissue+is%3Aopen+RFC+label%3ARFC) for big initiatives, features for [feature](https://github.com/OpenIMSDK/Open-IM-Server/issues?q=is%3Aissue+label%3Afeature) proposals, and [bug](https://github.com/OpenIMSDK/Open-IM-Server/issues?q=is%3Aissue+label%3Abug+) fixes. + If you are looking for something to work on, check out our [open issues](https://github.com/OpenIMSDK/Open-IM-Server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc). + If you have an idea for a new feature, please [open an issue](https://github.com/OpenIMSDK/Open-IM-Server/issues/new/choose), and we can discuss it. @@ -180,7 +190,7 @@ To be honest, we regard every user of Open-IM-Serveras a very kind contributor. Since we collaborate project Open-IM-Server in a distributed way, we appreciate **WELL-WRITTEN**, **DETAILED**, **EXPLICIT** issue reports. To make the communication more efficient, we wish everyone could search if your issue is an existing one in the searching list. If you find it existing, please add your details in comments under the existing issue instead of opening a brand new one. -To make the issue details as standard as possible, we setup an [ISSUE TEMPLATE](https://github.com/OpenIMSDK/Open-IM-Server/tree/main/.github/ISSUE_TEMPLATE) for issue reporters. You can find three kinds of issue templates there: question, bug report and feature request. Please **BE SURE** to follow the instructions to fill fields in template. +To make the issue details as standard as possible, we setup an [ISSUE TEMPLATE](https://github.com/OpenIMSDK/.github/tree/main/.github/ISSUE_TEMPLATE) for issue reporters. You can find three kinds of issue templates there: question, bug report and feature request. Please **BE SURE** to follow the instructions to fill fields in template. **There are a lot of cases when you could open an issue:** @@ -242,7 +252,7 @@ An example for this could be: #### PR Description -PR is the only way to make change to Open-IM-Server project files. To help reviewers better get your purpose, PR description could not be too detailed. We encourage contributors to follow the [PR template](https://github.com/OpenIMSDK/Open-IM-Server/tree/main/.github/PULL_REQUEST_TEMPLATE.md) to finish the pull request. +PR is the only way to make change to Open-IM-Server project files. To help reviewers better get your purpose, PR description could not be too detailed. We encourage contributors to follow the [PR template](https://github.com/OpenIMSDK/.github/tree/main/.github/PULL_REQUEST_TEMPLATE.md) to finish the pull request. You can find some very formal PR in [RFC](https://github.com/OpenIMSDK/Open-IM-Server/issues?q=is%3Aissue+is%3Aopen+RFC+label%3ARFC) issues and learn about them. @@ -295,7 +305,7 @@ The documentation for Open-IM-Server includes: + [README.md](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/README.md): This file includes the basic information and instructions for getting started with Open-IM-Server. + [CONTRIBUTING.md](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/CONTRIBUTING.md): This file contains guidelines for contributing to Open-IM-Server's codebase, such as how to submit issues, pull requests, and code reviews. -+ [DEVELOPGUIDE.md](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/DEVELOPGUIDE.md): This file provides a more in-depth guide to developing Open-IM-Server, including information on the project's architecture, coding conventions, and testing practices. ++ [DEVELOPGUIDE.md](https://github.com/OpenIMSDK/.github/blob/main/DEVELOPGUIDE.md): This file provides a more in-depth guide to developing Open-IM-Server, including information on the project's architecture, coding conventions, and testing practices. + [Official Documentation](https://doc.rentsoft.cn/): This is the official documentation for Open-IM-Server, which includes comprehensive information on all of its features, configuration options, and troubleshooting tips. Please obey the following rules to better format the docs, which would greatly improve the reading experience. @@ -315,7 +325,7 @@ We choose GitHub as the primary place for Open-IM-Server to collaborate. So the + help solve other user's problems; + help review other's [PR](https://github.com/OpenIMSDK/Open-IM-Server/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-desc) design; + discuss about Open-IM-Server to make things clearer; -+ advocate [Open-IM-Server](google.com/search?q=Open-IM-Server) technology beyond GitHub; ++ advocate [Open-IM-Server](https://google.com/search?q=Open-IM-Server) technology beyond GitHub; + write blogs on Open-IM-Server and so on. In a word, **ANY HELP IS CONTRIBUTION.** diff --git a/README-zh_CN.md b/README-zh_CN.md index 1308ef28b..87d675c88 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -19,7 +19,7 @@

English • - 中文 + 中文

From d03097108c518cac76c172330de0eb47aef71272 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Tue, 4 Jul 2023 09:16:40 +0800 Subject: [PATCH 06/73] feat: add code comment Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- Makefile | 7 ++++--- scripts/coverage.awk | 13 +++++++++++++ scripts/coverage.sh | 16 ++++++++++++++++ scripts/make-rules/golang.mk | 2 +- 4 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 scripts/coverage.awk create mode 100644 scripts/coverage.sh diff --git a/Makefile b/Makefile index 2dd10fdbd..8e697535e 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,7 @@ include scripts/make-rules/copyright.mk include scripts/make-rules/gen.mk include scripts/make-rules/dependencies.mk include scripts/make-rules/tools.mk +include scripts/make-rules/release.mk # ============================================================================== # Usage @@ -54,9 +55,9 @@ export USAGE_OPTIONS build: @$(MAKE) go.build -## build-multiarch: Build binaries for multiple platforms. See option PLATFORMS. -.PHONY: build-multiarch -build-multiarch: +## multiarch: Build binaries for multiple platforms. See option PLATFORMS. +.PHONY: multiarch +multiarch: @$(MAKE) go.build.multiarch ## tidy: tidy go.mod diff --git a/scripts/coverage.awk b/scripts/coverage.awk new file mode 100644 index 000000000..49389054e --- /dev/null +++ b/scripts/coverage.awk @@ -0,0 +1,13 @@ +#!/usr/bin/env awk + +{ + print $0 + if (match($0, /^total:/)) { + sub(/%/, "", $NF); + printf("test coverage is %s%(quality gate is %s%)\n", $NF, target) + if (strtonum($NF) < target) { + printf("test coverage does not meet expectations: %d%, please add test cases!\n", target) + exit 1; + } + } +} diff --git a/scripts/coverage.sh b/scripts/coverage.sh new file mode 100644 index 000000000..9fb77b59a --- /dev/null +++ b/scripts/coverage.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +# http://stackoverflow.com/a/21142256/2055281 + +echo "mode: atomic" > coverage.txt + +for d in $(find ./* -maxdepth 10 -type d); do + if ls $d/*.go &> /dev/null; then + go test -coverprofile=profile.out -covermode=atomic $d + if [ -f profile.out ]; then + cat profile.out | grep -v "mode: " >> /tmp/coverage.txt + rm profile.out + fi + fi +done + diff --git a/scripts/make-rules/golang.mk b/scripts/make-rules/golang.mk index f9a73b5bd..25939b090 100644 --- a/scripts/make-rules/golang.mk +++ b/scripts/make-rules/golang.mk @@ -150,7 +150,7 @@ go.test: .PHONY: go.test.junit-report go.test.junit-report: tools.verify.go-junit-report @echo "===========> Run unit test > $(TMP_DIR)/report.xml" - @$(GO) test -v -coverprofile=$(TMP_DIR)/coverage.out 2>&1 ./... | $(TOOLS_DIR)/go-junit-report -set-exit-code > $(OUTPUT_DIR)/report.xml + @$(GO) test -v -coverprofile=$(TMP_DIR)/coverage.out 2>&1 ./... | $(TOOLS_DIR)/go-junit-report -set-exit-code > $(TMP_DIR)/report.xml @sed -i '/mock_.*.go/d' $(TMP_DIR)/coverage.out @echo "===========> Test coverage of Go code is reported to $(TMP_DIR)/coverage.html by generating HTML" @$(GO) tool cover -html=$(TMP_DIR)/coverage.out -o $(TMP_DIR)/coverage.html From dc318bbdb594ae5a8fddba7c6780a9861bbc2498 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Tue, 4 Jul 2023 10:14:18 +0800 Subject: [PATCH 07/73] cide: fix make file command Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- scripts/make-rules/golang.mk | 6 +++--- scripts/release.sh | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 scripts/release.sh diff --git a/scripts/make-rules/golang.mk b/scripts/make-rules/golang.mk index 25939b090..255306062 100644 --- a/scripts/make-rules/golang.mk +++ b/scripts/make-rules/golang.mk @@ -114,9 +114,9 @@ go.build.%: @if [ "$(COMMAND)" == "openim-sdk-core" ]; then \ echo "===========> DEBUG: Compilation is not yet supported $(COMMAND)"; \ elif [ "$(COMMAND)" == "rpc" ]; then \ - for d in $(wildcard $(ROOT_DIR)/cmd/rpc/*/); do \ - cd $$d && CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) $(GO) build $(GO_BUILD_FLAGS) -o\ - $(BIN_DIR)/platforms/$(OS)/$(ARCH)/$$(basename $$d)$(GO_OUT_EXT) .; \ + for d in $(wildcard $(ROOT_DIR)/cmd/rpc/*); do \ + cd $${d} && CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) $(GO) build $(GO_BUILD_FLAGS) -o \ + $(BIN_DIR)/platforms/$(OS)/$(ARCH)/$$(basename $${d})$(GO_OUT_EXT) $${d}/main.go; \ done; \ else \ CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) $(GO) build $(GO_BUILD_FLAGS) -o \ diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100644 index 000000000..df4022cde --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +# Build a IAM release. This will build the binaries, create the Docker +# images and other build artifacts. + +set -o errexit +set -o nounset +set -o pipefail + +IAM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +source "${IAM_ROOT}/scripts/common.sh" +source "${IAM_ROOT}/scripts/lib/release.sh" + +IAM_RELEASE_RUN_TESTS=${IAM_RELEASE_RUN_TESTS-y} + +iam::golang::setup_env +iam::build::verify_prereqs +iam::release::verify_prereqs +#iam::build::build_image +iam::build::build_command +iam::release::package_tarballs +iam::release::updload_tarballs +git push origin ${VERSION} +#iam::release::github_release +#iam::release::generate_changelog From 91979285ebe562f085fba92a45616a43a97e01fb Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Tue, 4 Jul 2023 10:28:19 +0800 Subject: [PATCH 08/73] feat: add scripts lib Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- scripts/lib/color.sh | 38 ++ scripts/lib/golang.sh | 193 ++++++++++ scripts/lib/init.sh | 28 ++ scripts/lib/logging.sh | 161 +++++++++ scripts/lib/release.sh | 597 ++++++++++++++++++++++++++++++ scripts/lib/util.sh | 683 +++++++++++++++++++++++++++++++++++ scripts/lib/version.sh | 137 +++++++ scripts/make-rules/golang.mk | 11 +- 8 files changed, 1839 insertions(+), 9 deletions(-) create mode 100755 scripts/lib/color.sh create mode 100755 scripts/lib/golang.sh create mode 100755 scripts/lib/init.sh create mode 100755 scripts/lib/logging.sh create mode 100755 scripts/lib/release.sh create mode 100755 scripts/lib/util.sh create mode 100755 scripts/lib/version.sh diff --git a/scripts/lib/color.sh b/scripts/lib/color.sh new file mode 100755 index 000000000..bdbe0e843 --- /dev/null +++ b/scripts/lib/color.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +# Copyright 2020 Lingfei Kong . All rights reserved. +# Use of this source code is governed by a MIT style +# license that can be found in the LICENSE file. + +#Define color variables +#Feature +COLOR_NORMAL='\033[0m';COLOR_BOLD='\033[1m';COLOR_DIM='\033[2m';COLOR_UNDER='\033[4m'; +COLOR_ITALIC='\033[3m';COLOR_NOITALIC='\033[23m';COLOR_BLINK='\033[5m'; +COLOR_REVERSE='\033[7m';COLOR_CONCEAL='\033[8m';COLOR_NOBOLD='\033[22m'; +COLOR_NOUNDER='\033[24m';COLOR_NOBLINK='\033[25m'; + +#Front color +COLOR_BLACK='\033[30m';COLOR_RED='\033[31m';COLOR_GREEN='\033[32m';COLOR_YELLOW='\033[33m'; +COLOR_BLUE='\033[34m';COLOR_MAGENTA='\033[35m';COLOR_CYAN='\033[36m';COLOR_WHITE='\033[37m'; + +#background color +COLOR_BBLACK='\033[40m';COLOR_BRED='\033[41m'; +COLOR_BGREEN='\033[42m';COLOR_BYELLOW='\033[43m'; +COLOR_BBLUE='\033[44m';COLOR_BMAGENTA='\033[45m'; +COLOR_BCYAN='\033[46m';COLOR_BWHITE='\033[47m'; + +# Print colors you can use +iam::color::print_color() +{ + echo + echo -e ${bmagenta}--back-color:${normal} + echo "bblack; bgreen; bblue; bcyan; bred; byellow; bmagenta; bwhite" + echo + echo -e ${red}--font-color:${normal} + echo "black; red; green; yellow; blue; magenta; cyan; white" + echo + echo -e ${bold}--font:${normal} + echo "normal; italic; reverse; nounder; bold; noitalic; conceal; noblink; + dim; blink; nobold; under" + echo +} diff --git a/scripts/lib/golang.sh b/scripts/lib/golang.sh new file mode 100755 index 000000000..f58e08649 --- /dev/null +++ b/scripts/lib/golang.sh @@ -0,0 +1,193 @@ +#!/usr/bin/env bash + +# Copyright 2020 Lingfei Kong . All rights reserved. +# Use of this source code is governed by a MIT style +# license that can be found in the LICENSE file. + +# shellcheck disable=SC2034 # Variables sourced in other scripts. + +# The server platform we are building on. +readonly IAM_SUPPORTED_SERVER_PLATFORMS=( + linux/amd64 + linux/arm64 +) + +# If we update this we should also update the set of platforms whose standard +# library is precompiled for in build/build-image/cross/Dockerfile +readonly IAM_SUPPORTED_CLIENT_PLATFORMS=( + linux/amd64 + linux/arm64 +) + +# The set of server targets that we are only building for Linux +# If you update this list, please also update build/BUILD. +iam::golang::server_targets() { + local targets=( + iam-apiserver + iam-authz-server + iam-pump + iam-watcher + ) + echo "${targets[@]}" +} + +IFS=" " read -ra IAM_SERVER_TARGETS <<< "$(iam::golang::server_targets)" +readonly IAM_SERVER_TARGETS +readonly IAM_SERVER_BINARIES=("${IAM_SERVER_TARGETS[@]##*/}") + +# The set of server targets we build docker images for +iam::golang::server_image_targets() { + # NOTE: this contains cmd targets for iam::build::get_docker_wrapped_binaries + local targets=( + cmd/iam-apiserver + cmd/iam-authz-server + cmd/iam-pump + cmd/iam-watcher + ) + echo "${targets[@]}" +} + +IFS=" " read -ra IAM_SERVER_IMAGE_TARGETS <<< "$(iam::golang::server_image_targets)" +readonly IAM_SERVER_IMAGE_TARGETS +readonly IAM_SERVER_IMAGE_BINARIES=("${IAM_SERVER_IMAGE_TARGETS[@]##*/}") + +# ------------ +# NOTE: All functions that return lists should use newlines. +# bash functions can't return arrays, and spaces are tricky, so newline +# separators are the preferred pattern. +# To transform a string of newline-separated items to an array, use iam::util::read-array: +# iam::util::read-array FOO < <(iam::golang::dups a b c a) +# +# ALWAYS remember to quote your subshells. Not doing so will break in +# bash 4.3, and potentially cause other issues. +# ------------ + +# Returns a sorted newline-separated list containing only duplicated items. +iam::golang::dups() { + # We use printf to insert newlines, which are required by sort. + printf "%s\n" "$@" | sort | uniq -d +} + +# Returns a sorted newline-separated list with duplicated items removed. +iam::golang::dedup() { + # We use printf to insert newlines, which are required by sort. + printf "%s\n" "$@" | sort -u +} + +# Depends on values of user-facing IAM_BUILD_PLATFORMS, IAM_FASTBUILD, +# and IAM_BUILDER_OS. +# Configures IAM_SERVER_PLATFORMS and IAM_CLIENT_PLATFORMS, then sets them +# to readonly. +# The configured vars will only contain platforms allowed by the +# IAM_SUPPORTED* vars at the top of this file. +declare -a IAM_SERVER_PLATFORMS +declare -a IAM_CLIENT_PLATFORMS +iam::golang::setup_platforms() { + if [[ -n "${IAM_BUILD_PLATFORMS:-}" ]]; then + # IAM_BUILD_PLATFORMS needs to be read into an array before the next + # step, or quoting treats it all as one element. + local -a platforms + IFS=" " read -ra platforms <<< "${IAM_BUILD_PLATFORMS}" + + # Deduplicate to ensure the intersection trick with iam::golang::dups + # is not defeated by duplicates in user input. + iam::util::read-array platforms < <(iam::golang::dedup "${platforms[@]}") + + # Use iam::golang::dups to restrict the builds to the platforms in + # IAM_SUPPORTED_*_PLATFORMS. Items should only appear at most once in each + # set, so if they appear twice after the merge they are in the intersection. + iam::util::read-array IAM_SERVER_PLATFORMS < <(iam::golang::dups \ + "${platforms[@]}" \ + "${IAM_SUPPORTED_SERVER_PLATFORMS[@]}" \ + ) + readonly IAM_SERVER_PLATFORMS + + iam::util::read-array IAM_CLIENT_PLATFORMS < <(iam::golang::dups \ + "${platforms[@]}" \ + "${IAM_SUPPORTED_CLIENT_PLATFORMS[@]}" \ + ) + readonly IAM_CLIENT_PLATFORMS + + elif [[ "${IAM_FASTBUILD:-}" == "true" ]]; then + IAM_SERVER_PLATFORMS=(linux/amd64) + readonly IAM_SERVER_PLATFORMS + IAM_CLIENT_PLATFORMS=(linux/amd64) + readonly IAM_CLIENT_PLATFORMS + else + IAM_SERVER_PLATFORMS=("${IAM_SUPPORTED_SERVER_PLATFORMS[@]}") + readonly IAM_SERVER_PLATFORMS + + IAM_CLIENT_PLATFORMS=("${IAM_SUPPORTED_CLIENT_PLATFORMS[@]}") + readonly IAM_CLIENT_PLATFORMS + fi +} + +iam::golang::setup_platforms + +# The set of client targets that we are building for all platforms +# If you update this list, please also update build/BUILD. +readonly IAM_CLIENT_TARGETS=( + iamctl +) +readonly IAM_CLIENT_BINARIES=("${IAM_CLIENT_TARGETS[@]##*/}") + +readonly IAM_ALL_TARGETS=( + "${IAM_SERVER_TARGETS[@]}" + "${IAM_CLIENT_TARGETS[@]}" +) +readonly IAM_ALL_BINARIES=("${IAM_ALL_TARGETS[@]##*/}") + +# Asks golang what it thinks the host platform is. The go tool chain does some +# slightly different things when the target platform matches the host platform. +iam::golang::host_platform() { + echo "$(go env GOHOSTOS)/$(go env GOHOSTARCH)" +} + +# Ensure the go tool exists and is a viable version. +iam::golang::verify_go_version() { + if [[ -z "$(command -v go)" ]]; then + iam::log::usage_from_stdin <. All rights reserved. +# Use of this source code is governed by a MIT style +# license that can be found in the LICENSE file. + +set -o errexit +set +o nounset +set -o pipefail + +# Unset CDPATH so that path interpolation can work correctly +# https://github.com/iamrnetes/iamrnetes/issues/52255 +unset CDPATH + +# Default use go modules +export GO111MODULE=on + +# The root of the build/dist directory +IAM_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd -P)" + +source "${IAM_ROOT}/scripts/lib/util.sh" +source "${IAM_ROOT}/scripts/lib/logging.sh" +source "${IAM_ROOT}/scripts/lib/color.sh" + +iam::log::install_errexit + +source "${IAM_ROOT}/scripts/lib/version.sh" +source "${IAM_ROOT}/scripts/lib/golang.sh" diff --git a/scripts/lib/logging.sh b/scripts/lib/logging.sh new file mode 100755 index 000000000..39b805c11 --- /dev/null +++ b/scripts/lib/logging.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash + +# Copyright 2020 Lingfei Kong . All rights reserved. +# Use of this source code is governed by a MIT style +# license that can be found in the LICENSE file. + +# Controls verbosity of the script output and logging. +IAM_VERBOSE="${IAM_VERBOSE:-5}" + +# Handler for when we exit automatically on an error. +# Borrowed from https://gist.github.com/ahendrix/7030300 +iam::log::errexit() { + local err="${PIPESTATUS[*]}" + + # If the shell we are in doesn't have errexit set (common in subshells) then + # don't dump stacks. + set +o | grep -qe "-o errexit" || return + + set +o xtrace + local code="${1:-1}" + # Print out the stack trace described by $function_stack + if [ ${#FUNCNAME[@]} -gt 2 ] + then + iam::log::error "Call tree:" + for ((i=1;i<${#FUNCNAME[@]}-1;i++)) + do + iam::log::error " ${i}: ${BASH_SOURCE[${i}+1]}:${BASH_LINENO[${i}]} ${FUNCNAME[${i}]}(...)" + done + fi + iam::log::error_exit "Error in ${BASH_SOURCE[1]}:${BASH_LINENO[0]}. '${BASH_COMMAND}' exited with status ${err}" "${1:-1}" 1 +} + +iam::log::install_errexit() { + # trap ERR to provide an error handler whenever a command exits nonzero this + # is a more verbose version of set -o errexit + trap 'iam::log::errexit' ERR + + # setting errtrace allows our ERR trap handler to be propagated to functions, + # expansions and subshells + set -o errtrace +} + +# Print out the stack trace +# +# Args: +# $1 The number of stack frames to skip when printing. +iam::log::stack() { + local stack_skip=${1:-0} + stack_skip=$((stack_skip + 1)) + if [[ ${#FUNCNAME[@]} -gt ${stack_skip} ]]; then + echo "Call stack:" >&2 + local i + for ((i=1 ; i <= ${#FUNCNAME[@]} - stack_skip ; i++)) + do + local frame_no=$((i - 1 + stack_skip)) + local source_file=${BASH_SOURCE[${frame_no}]} + local source_lineno=${BASH_LINENO[$((frame_no - 1))]} + local funcname=${FUNCNAME[${frame_no}]} + echo " ${i}: ${source_file}:${source_lineno} ${funcname}(...)" >&2 + done + fi +} + +# Log an error and exit. +# Args: +# $1 Message to log with the error +# $2 The error code to return +# $3 The number of stack frames to skip when printing. +iam::log::error_exit() { + local message="${1:-}" + local code="${2:-1}" + local stack_skip="${3:-0}" + stack_skip=$((stack_skip + 1)) + + if [[ ${IAM_VERBOSE} -ge 4 ]]; then + local source_file=${BASH_SOURCE[${stack_skip}]} + local source_line=${BASH_LINENO[$((stack_skip - 1))]} + echo "!!! Error in ${source_file}:${source_line}" >&2 + [[ -z ${1-} ]] || { + echo " ${1}" >&2 + } + + iam::log::stack ${stack_skip} + + echo "Exiting with status ${code}" >&2 + fi + + exit "${code}" +} + +# Log an error but keep going. Don't dump the stack or exit. +iam::log::error() { + timestamp=$(date +"[%m%d %H:%M:%S]") + echo "!!! ${timestamp} ${1-}" >&2 + shift + for message; do + echo " ${message}" >&2 + done +} + +# Print an usage message to stderr. The arguments are printed directly. +iam::log::usage() { + echo >&2 + local message + for message; do + echo "${message}" >&2 + done + echo >&2 +} + +iam::log::usage_from_stdin() { + local messages=() + while read -r line; do + messages+=("${line}") + done + + iam::log::usage "${messages[@]}" +} + +# Print out some info that isn't a top level status line +iam::log::info() { + local V="${V:-0}" + if [[ ${IAM_VERBOSE} < ${V} ]]; then + return + fi + + for message; do + echo "${message}" + done +} + +# Just like iam::log::info, but no \n, so you can make a progress bar +iam::log::progress() { + for message; do + echo -e -n "${message}" + done +} + +iam::log::info_from_stdin() { + local messages=() + while read -r line; do + messages+=("${line}") + done + + iam::log::info "${messages[@]}" +} + +# Print a status line. Formatted to show up in a stream of output. +iam::log::status() { + local V="${V:-0}" + if [[ ${IAM_VERBOSE} < ${V} ]]; then + return + fi + + timestamp=$(date +"[%m%d %H:%M:%S]") + echo "+++ ${timestamp} ${1}" + shift + for message; do + echo " ${message}" + done +} diff --git a/scripts/lib/release.sh b/scripts/lib/release.sh new file mode 100755 index 000000000..acc3730ef --- /dev/null +++ b/scripts/lib/release.sh @@ -0,0 +1,597 @@ +#!/usr/bin/env bash + +# Copyright 2020 Lingfei Kong . All rights reserved. +# Use of this source code is governed by a MIT style +# license that can be found in the LICENSE file. + +# This file creates release artifacts (tar files, container images) that are +# ready to distribute to install or distribute to end users. + +############################################################################### +# Most of the ::release:: namespace functions have been moved to +# github.com/iam/release. Have a look in that repo and specifically in +# lib/releaselib.sh for ::release::-related functionality. +############################################################################### + +# Tencent cos configuration +readonly BUCKET="marmotedu-1254073058" +readonly REGION="ap-beijing" +readonly COS_RELEASE_DIR="iam-release" +readonly COSTOOL="coscmd" + +# This is where the final release artifacts are created locally +readonly RELEASE_STAGE="${LOCAL_OUTPUT_ROOT}/release-stage" +readonly RELEASE_TARS="${LOCAL_OUTPUT_ROOT}/release-tars" +readonly RELEASE_IMAGES="${LOCAL_OUTPUT_ROOT}/release-images" + +# IAM github account info +readonly IAM_GITHUB_ORG=marmotedu +readonly IAM_GITHUB_REPO=iam + +readonly ARTIFACT=iam.tar.gz +readonly CHECKSUM=${ARTIFACT}.sha1sum + +IAM_BUILD_CONFORMANCE=${IAM_BUILD_CONFORMANCE:-y} +IAM_BUILD_PULL_LATEST_IMAGES=${IAM_BUILD_PULL_LATEST_IMAGES:-y} + +# Validate a ci version +# +# Globals: +# None +# Arguments: +# version +# Returns: +# If version is a valid ci version +# Sets: (e.g. for '1.2.3-alpha.4.56+abcdef12345678') +# VERSION_MAJOR (e.g. '1') +# VERSION_MINOR (e.g. '2') +# VERSION_PATCH (e.g. '3') +# VERSION_PRERELEASE (e.g. 'alpha') +# VERSION_PRERELEASE_REV (e.g. '4') +# VERSION_BUILD_INFO (e.g. '.56+abcdef12345678') +# VERSION_COMMITS (e.g. '56') +function iam::release::parse_and_validate_ci_version() { + # Accept things like "v1.2.3-alpha.4.56+abcdef12345678" or "v1.2.3-beta.4" + local -r version_regex="^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)-([a-zA-Z0-9]+)\\.(0|[1-9][0-9]*)(\\.(0|[1-9][0-9]*)\\+[0-9a-f]{7,40})?$" + local -r version="${1-}" + [[ "${version}" =~ ${version_regex} ]] || { + iam::log::error "Invalid ci version: '${version}', must match regex ${version_regex}" + return 1 + } + + # The VERSION variables are used when this file is sourced, hence + # the shellcheck SC2034 'appears unused' warning is to be ignored. + + # shellcheck disable=SC2034 + VERSION_MAJOR="${BASH_REMATCH[1]}" + # shellcheck disable=SC2034 + VERSION_MINOR="${BASH_REMATCH[2]}" + # shellcheck disable=SC2034 + VERSION_PATCH="${BASH_REMATCH[3]}" + # shellcheck disable=SC2034 + VERSION_PRERELEASE="${BASH_REMATCH[4]}" + # shellcheck disable=SC2034 + VERSION_PRERELEASE_REV="${BASH_REMATCH[5]}" + # shellcheck disable=SC2034 + VERSION_BUILD_INFO="${BASH_REMATCH[6]}" + # shellcheck disable=SC2034 + VERSION_COMMITS="${BASH_REMATCH[7]}" +} + +# --------------------------------------------------------------------------- +# Build final release artifacts +function iam::release::clean_cruft() { + # Clean out cruft + find "${RELEASE_STAGE}" -name '*~' -exec rm {} \; + find "${RELEASE_STAGE}" -name '#*#' -exec rm {} \; + find "${RELEASE_STAGE}" -name '.DS*' -exec rm {} \; +} + +function iam::release::package_tarballs() { + # Clean out any old releases + rm -rf "${RELEASE_STAGE}" "${RELEASE_TARS}" "${RELEASE_IMAGES}" + mkdir -p "${RELEASE_TARS}" + iam::release::package_src_tarball & + iam::release::package_client_tarballs & + iam::release::package_iam_manifests_tarball & + iam::release::package_server_tarballs & + iam::util::wait-for-jobs || { iam::log::error "previous tarball phase failed"; return 1; } + + iam::release::package_final_tarball & # _final depends on some of the previous phases + iam::util::wait-for-jobs || { iam::log::error "previous tarball phase failed"; return 1; } +} + +function iam::release::updload_tarballs() { + iam::log::info "upload ${RELEASE_TARS}/* to cos bucket ${BUCKET}." + for file in $(ls ${RELEASE_TARS}/*) + do + if [ "${COSTOOL}" == "coscli" ];then + coscli cp "${file}" "cos://${BUCKET}/${COS_RELEASE_DIR}/${IAM_GIT_VERSION}/${file##*/}" + coscli cp "${file}" "cos://${BUCKET}/${COS_RELEASE_DIR}/latest/${file##*/}" + else + coscmd upload "${file}" "${COS_RELEASE_DIR}/${IAM_GIT_VERSION}/" + coscmd upload "${file}" "${COS_RELEASE_DIR}/latest/" + fi + done +} + +# Package the source code we built, for compliance/licensing/audit/yadda. +function iam::release::package_src_tarball() { + local -r src_tarball="${RELEASE_TARS}/iam-src.tar.gz" + iam::log::status "Building tarball: src" + if [[ "${IAM_GIT_TREE_STATE-}" = 'clean' ]]; then + git archive -o "${src_tarball}" HEAD + else + find "${IAM_ROOT}" -mindepth 1 -maxdepth 1 \ + ! \( \ + \( -path "${IAM_ROOT}"/_\* -o \ + -path "${IAM_ROOT}"/.git\* -o \ + -path "${IAM_ROOT}"/.gitignore\* -o \ + -path "${IAM_ROOT}"/.gsemver.yaml\* -o \ + -path "${IAM_ROOT}"/.config\* -o \ + -path "${IAM_ROOT}"/.chglog\* -o \ + -path "${IAM_ROOT}"/.gitlint -o \ + -path "${IAM_ROOT}"/.golangci.yaml -o \ + -path "${IAM_ROOT}"/.goreleaser.yml -o \ + -path "${IAM_ROOT}"/.note.md -o \ + -path "${IAM_ROOT}"/.todo.md \ + \) -prune \ + \) -print0 \ + | "${TAR}" czf "${src_tarball}" --transform "s|${IAM_ROOT#/*}|iam|" --null -T - + fi +} + +# Package up all of the server binaries +function iam::release::package_server_tarballs() { + # Find all of the built client binaries + local long_platforms=("${LOCAL_OUTPUT_BINPATH}"/*/*) + if [[ -n ${IAM_BUILD_PLATFORMS-} ]]; then + read -ra long_platforms <<< "${IAM_BUILD_PLATFORMS}" + fi + + for platform_long in "${long_platforms[@]}"; do + local platform + local platform_tag + platform=${platform_long##${LOCAL_OUTPUT_BINPATH}/} # Strip LOCAL_OUTPUT_BINPATH + platform_tag=${platform/\//-} # Replace a "/" for a "-" + iam::log::status "Starting tarball: server $platform_tag" + + ( + local release_stage="${RELEASE_STAGE}/server/${platform_tag}/iam" + rm -rf "${release_stage}" + mkdir -p "${release_stage}/server/bin" + + local server_bins=("${IAM_SERVER_BINARIES[@]}") + + # This fancy expression will expand to prepend a path + # (${LOCAL_OUTPUT_BINPATH}/${platform}/) to every item in the + # server_bins array. + cp "${server_bins[@]/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}" \ + "${release_stage}/server/bin/" + + iam::release::clean_cruft + + local package_name="${RELEASE_TARS}/iam-server-${platform_tag}.tar.gz" + iam::release::create_tarball "${package_name}" "${release_stage}/.." + ) & + done + + iam::log::status "Waiting on tarballs" + iam::util::wait-for-jobs || { iam::log::error "server tarball creation failed"; exit 1; } + } + +# Package up all of the cross compiled clients. Over time this should grow into +# a full SDK +function iam::release::package_client_tarballs() { + # Find all of the built client binaries + local long_platforms=("${LOCAL_OUTPUT_BINPATH}"/*/*) + if [[ -n ${IAM_BUILD_PLATFORMS-} ]]; then + read -ra long_platforms <<< "${IAM_BUILD_PLATFORMS}" + fi + + for platform_long in "${long_platforms[@]}"; do + local platform + local platform_tag + platform=${platform_long##${LOCAL_OUTPUT_BINPATH}/} # Strip LOCAL_OUTPUT_BINPATH + platform_tag=${platform/\//-} # Replace a "/" for a "-" + iam::log::status "Starting tarball: client $platform_tag" + + ( + local release_stage="${RELEASE_STAGE}/client/${platform_tag}/iam" + rm -rf "${release_stage}" + mkdir -p "${release_stage}/client/bin" + + local client_bins=("${IAM_CLIENT_BINARIES[@]}") + + # This fancy expression will expand to prepend a path + # (${LOCAL_OUTPUT_BINPATH}/${platform}/) to every item in the + # client_bins array. + cp "${client_bins[@]/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}" \ + "${release_stage}/client/bin/" + + iam::release::clean_cruft + + local package_name="${RELEASE_TARS}/iam-client-${platform_tag}.tar.gz" + iam::release::create_tarball "${package_name}" "${release_stage}/.." + ) & + done + + iam::log::status "Waiting on tarballs" + iam::util::wait-for-jobs || { iam::log::error "client tarball creation failed"; exit 1; } +} + +# Package up all of the server binaries in docker images +function iam::release::build_server_images() { + # Clean out any old images + rm -rf "${RELEASE_IMAGES}" + local platform + for platform in "${IAM_SERVER_PLATFORMS[@]}"; do + local platform_tag + local arch + platform_tag=${platform/\//-} # Replace a "/" for a "-" + arch=$(basename "${platform}") + iam::log::status "Building images: $platform_tag" + + local release_stage + release_stage="${RELEASE_STAGE}/server/${platform_tag}/iam" + rm -rf "${release_stage}" + mkdir -p "${release_stage}/server/bin" + + # This fancy expression will expand to prepend a path + # (${LOCAL_OUTPUT_BINPATH}/${platform}/) to every item in the + # IAM_SERVER_IMAGE_BINARIES array. + cp "${IAM_SERVER_IMAGE_BINARIES[@]/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}" \ + "${release_stage}/server/bin/" + + iam::release::create_docker_images_for_server "${release_stage}/server/bin" "${arch}" + done +} + +function iam::release::md5() { + if which md5 >/dev/null 2>&1; then + md5 -q "$1" + else + md5sum "$1" | awk '{ print $1 }' + fi +} + +function iam::release::sha1() { + if which sha1sum >/dev/null 2>&1; then + sha1sum "$1" | awk '{ print $1 }' + else + shasum -a1 "$1" | awk '{ print $1 }' + fi +} + +function iam::release::build_conformance_image() { + local -r arch="$1" + local -r registry="$2" + local -r version="$3" + local -r save_dir="${4-}" + iam::log::status "Building conformance image for arch: ${arch}" + ARCH="${arch}" REGISTRY="${registry}" VERSION="${version}" \ + make -C cluster/images/conformance/ build >/dev/null + + local conformance_tag + conformance_tag="${registry}/conformance-${arch}:${version}" + if [[ -n "${save_dir}" ]]; then + "${DOCKER[@]}" save "${conformance_tag}" > "${save_dir}/conformance-${arch}.tar" + fi + iam::log::status "Deleting conformance image ${conformance_tag}" + "${DOCKER[@]}" rmi "${conformance_tag}" &>/dev/null || true +} + +# This builds all the release docker images (One docker image per binary) +# Args: +# $1 - binary_dir, the directory to save the tared images to. +# $2 - arch, architecture for which we are building docker images. +function iam::release::create_docker_images_for_server() { + # Create a sub-shell so that we don't pollute the outer environment + ( + local binary_dir + local arch + local binaries + local images_dir + binary_dir="$1" + arch="$2" + binaries=$(iam::build::get_docker_wrapped_binaries "${arch}") + images_dir="${RELEASE_IMAGES}/${arch}" + mkdir -p "${images_dir}" + + # k8s.gcr.io is the constant tag in the docker archives, this is also the default for config scripts in GKE. + # We can use IAM_DOCKER_REGISTRY to include and extra registry in the docker archive. + # If we use IAM_DOCKER_REGISTRY="k8s.gcr.io", then the extra tag (same) is ignored, see release_docker_image_tag below. + local -r docker_registry="k8s.gcr.io" + # Docker tags cannot contain '+' + local docker_tag="${IAM_GIT_VERSION/+/_}" + if [[ -z "${docker_tag}" ]]; then + iam::log::error "git version information missing; cannot create Docker tag" + return 1 + fi + + # provide `--pull` argument to `docker build` if `IAM_BUILD_PULL_LATEST_IMAGES` + # is set to y or Y; otherwise try to build the image without forcefully + # pulling the latest base image. + local docker_build_opts + docker_build_opts= + if [[ "${IAM_BUILD_PULL_LATEST_IMAGES}" =~ [yY] ]]; then + docker_build_opts='--pull' + fi + + for wrappable in $binaries; do + + local binary_name=${wrappable%%,*} + local base_image=${wrappable##*,} + local binary_file_path="${binary_dir}/${binary_name}" + local docker_build_path="${binary_file_path}.dockerbuild" + local docker_file_path="${docker_build_path}/Dockerfile" + local docker_image_tag="${docker_registry}/${binary_name}-${arch}:${docker_tag}" + + iam::log::status "Starting docker build for image: ${binary_name}-${arch}" + ( + rm -rf "${docker_build_path}" + mkdir -p "${docker_build_path}" + ln "${binary_file_path}" "${docker_build_path}/${binary_name}" + ln "${IAM_ROOT}/build/nsswitch.conf" "${docker_build_path}/nsswitch.conf" + chmod 0644 "${docker_build_path}/nsswitch.conf" + cat < "${docker_file_path}" +FROM ${base_image} +COPY ${binary_name} /usr/local/bin/${binary_name} +EOF + # ensure /etc/nsswitch.conf exists so go's resolver respects /etc/hosts + if [[ "${base_image}" =~ busybox ]]; then + echo "COPY nsswitch.conf /etc/" >> "${docker_file_path}" + fi + + "${DOCKER[@]}" build ${docker_build_opts:+"${docker_build_opts}"} -q -t "${docker_image_tag}" "${docker_build_path}" >/dev/null + # If we are building an official/alpha/beta release we want to keep + # docker images and tag them appropriately. + local -r release_docker_image_tag="${IAM_DOCKER_REGISTRY-$docker_registry}/${binary_name}-${arch}:${IAM_DOCKER_IMAGE_TAG-$docker_tag}" + if [[ "${release_docker_image_tag}" != "${docker_image_tag}" ]]; then + iam::log::status "Tagging docker image ${docker_image_tag} as ${release_docker_image_tag}" + "${DOCKER[@]}" rmi "${release_docker_image_tag}" 2>/dev/null || true + "${DOCKER[@]}" tag "${docker_image_tag}" "${release_docker_image_tag}" 2>/dev/null + fi + "${DOCKER[@]}" save -o "${binary_file_path}.tar" "${docker_image_tag}" "${release_docker_image_tag}" + echo "${docker_tag}" > "${binary_file_path}.docker_tag" + rm -rf "${docker_build_path}" + ln "${binary_file_path}.tar" "${images_dir}/" + + iam::log::status "Deleting docker image ${docker_image_tag}" + "${DOCKER[@]}" rmi "${docker_image_tag}" &>/dev/null || true + ) & + done + + if [[ "${IAM_BUILD_CONFORMANCE}" =~ [yY] ]]; then + iam::release::build_conformance_image "${arch}" "${docker_registry}" \ + "${docker_tag}" "${images_dir}" & + fi + + iam::util::wait-for-jobs || { iam::log::error "previous Docker build failed"; return 1; } + iam::log::status "Docker builds done" + ) + +} + +# This will pack iam-system manifests files for distros such as COS. +function iam::release::package_iam_manifests_tarball() { + iam::log::status "Building tarball: manifests" + + local src_dir="${IAM_ROOT}/deployments" + + local release_stage="${RELEASE_STAGE}/manifests/iam" + rm -rf "${release_stage}" + + local dst_dir="${release_stage}" + mkdir -p "${dst_dir}" + cp -r ${src_dir}/* "${dst_dir}" + #cp "${src_dir}/iam-apiserver.yaml" "${dst_dir}" + #cp "${src_dir}/iam-authz-server.yaml" "${dst_dir}" + #cp "${src_dir}/iam-pump.yaml" "${dst_dir}" + #cp "${src_dir}/iam-watcher.yaml" "${dst_dir}" + #cp "${IAM_ROOT}/cluster/gce/gci/health-monitor.sh" "${dst_dir}/health-monitor.sh" + + iam::release::clean_cruft + + local package_name="${RELEASE_TARS}/iam-manifests.tar.gz" + iam::release::create_tarball "${package_name}" "${release_stage}/.." +} + +# This is all the platform-independent stuff you need to run/install iam. +# Arch-specific binaries will need to be downloaded separately (possibly by +# using the bundled cluster/get-iam-binaries.sh script). +# Included in this tarball: +# - Cluster spin up/down scripts and configs for various cloud providers +# - Tarballs for manifest configs that are ready to be uploaded +# - Examples (which may or may not still work) +# - The remnants of the docs/ directory +function iam::release::package_final_tarball() { + iam::log::status "Building tarball: final" + + # This isn't a "full" tarball anymore, but the release lib still expects + # artifacts under "full/iam/" + local release_stage="${RELEASE_STAGE}/full/iam" + rm -rf "${release_stage}" + mkdir -p "${release_stage}" + + mkdir -p "${release_stage}/client" + cat < "${release_stage}/client/README" +Client binaries are no longer included in the IAM final tarball. + +Run release/get-iam-binaries.sh to download client and server binaries. +EOF + + # We want everything in /scripts. + mkdir -p "${release_stage}/release" + cp -R "${IAM_ROOT}/scripts/release" "${release_stage}/" + cat < "${release_stage}/release/get-iam-binaries.sh" +#!/usr/bin/env bash + +# Copyright 2020 Lingfei Kong . All rights reserved. +# Use of this source code is governed by a MIT style +# license that can be found in the LICENSE file. + +# This file download iam client and server binaries from tencent cos bucket. + +os=linux arch=amd64 version=${IAM_GIT_VERSION} && wget https://${BUCKET}.cos.${REGION}.myqcloud.com/${COS_RELEASE_DIR}/\$version/{iam-client-\$os-\$arch.tar.gz,iam-server-\$os-\$arch.tar.gz} +EOF + chmod +x ${release_stage}/release/get-iam-binaries.sh + + mkdir -p "${release_stage}/server" + cp "${RELEASE_TARS}/iam-manifests.tar.gz" "${release_stage}/server/" + cat < "${release_stage}/server/README" +Server binary tarballs are no longer included in the IAM final tarball. + +Run release/get-iam-binaries.sh to download client and server binaries. +EOF + + # Include hack/lib as a dependency for the cluster/ scripts + #mkdir -p "${release_stage}/hack" + #cp -R "${IAM_ROOT}/hack/lib" "${release_stage}/hack/" + + cp -R ${IAM_ROOT}/{docs,configs,scripts,deployments,init,README.md,LICENSE} "${release_stage}/" + + echo "${IAM_GIT_VERSION}" > "${release_stage}/version" + + iam::release::clean_cruft + + local package_name="${RELEASE_TARS}/${ARTIFACT}" + iam::release::create_tarball "${package_name}" "${release_stage}/.." +} + +# Build a release tarball. $1 is the output tar name. $2 is the base directory +# of the files to be packaged. This assumes that ${2}/iamis what is +# being packaged. +function iam::release::create_tarball() { + iam::build::ensure_tar + + local tarfile=$1 + local stagingdir=$2 + + "${TAR}" czf "${tarfile}" -C "${stagingdir}" iam --owner=0 --group=0 +} + +function iam::release::install_github_release(){ + GO111MODULE=on go install github.com/github-release/github-release@latest +} + +# Require the following tools: +# - github-release +# - gsemver +# - git-chglog +# - coscmd or coscli +function iam::release::verify_prereqs(){ + if [ -z "$(which github-release 2>/dev/null)" ]; then + iam::log::info "'github-release' tool not installed, try to install it." + + if ! iam::release::install_github_release; then + iam::log::error "failed to install 'github-release'" + return 1 + fi + fi + + if [ -z "$(which git-chglog 2>/dev/null)" ]; then + iam::log::info "'git-chglog' tool not installed, try to install it." + + if ! go install github.com/git-chglog/git-chglog/cmd/git-chglog@latest &>/dev/null; then + iam::log::error "failed to install 'git-chglog'" + return 1 + fi + fi + + if [ -z "$(which gsemver 2>/dev/null)" ]; then + iam::log::info "'gsemver' tool not installed, try to install it." + + if ! go install github.com/arnaud-deprez/gsemver@latest &>/dev/null; then + iam::log::error "failed to install 'gsemver'" + return 1 + fi + fi + + + if [ -z "$(which ${COSTOOL} 2>/dev/null)" ]; then + iam::log::info "${COSTOOL} tool not installed, try to install it." + + if ! make -C "${IAM_ROOT}" tools.install.${COSTOOL}; then + iam::log::error "failed to install ${COSTOOL}" + return 1 + fi + fi + + if [ -z "${TENCENT_SECRET_ID}" -o -z "${TENCENT_SECRET_KEY}" ];then + iam::log::error "can not find env: TENCENT_SECRET_ID and TENCENT_SECRET_KEY" + return 1 + fi + + if [ "${COSTOOL}" == "coscli" ];then + if [ ! -f "${HOME}/.cos.yaml" ];then + cat << EOF > "${HOME}/.cos.yaml" +cos: + base: + secretid: ${TENCENT_SECRET_ID} + secretkey: ${TENCENT_SECRET_KEY} + sessiontoken: "" + buckets: + - name: ${BUCKET} + alias: ${BUCKET} + region: ${REGION} +EOF + fi + else + if [ ! -f "${HOME}/.cos.conf" ];then + cat << EOF > "${HOME}/.cos.conf" +[common] +secret_id = ${TENCENT_SECRET_ID} +secret_key = ${TENCENT_SECRET_KEY} +bucket = ${BUCKET} +region =${REGION} +max_thread = 5 +part_size = 1 +schema = https +EOF + fi + fi +} + +# Create a github release with specified tarballs. +# NOTICE: Must export 'GITHUB_TOKEN' env in the shell, details: +# https://github.com/github-release/github-release +function iam::release::github_release() { + # create a github release + iam::log::info "create a new github release with tag ${IAM_GIT_VERSION}" + github-release release \ + --user ${IAM_GITHUB_ORG} \ + --repo ${IAM_GITHUB_REPO} \ + --tag ${IAM_GIT_VERSION} \ + --description "" \ + --pre-release + + # update iam tarballs + iam::log::info "upload ${ARTIFACT} to release ${IAM_GIT_VERSION}" + github-release upload \ + --user ${IAM_GITHUB_ORG} \ + --repo ${IAM_GITHUB_REPO} \ + --tag ${IAM_GIT_VERSION} \ + --name ${ARTIFACT} \ + --file ${RELEASE_TARS}/${ARTIFACT} + + iam::log::info "upload iam-src.tar.gz to release ${IAM_GIT_VERSION}" + github-release upload \ + --user ${IAM_GITHUB_ORG} \ + --repo ${IAM_GITHUB_REPO} \ + --tag ${IAM_GIT_VERSION} \ + --name "iam-src.tar.gz" \ + --file ${RELEASE_TARS}/iam-src.tar.gz +} + +function iam::release::generate_changelog() { + iam::log::info "generate CHANGELOG-${IAM_GIT_VERSION#v}.md and commit it" + + git-chglog ${IAM_GIT_VERSION} > ${IAM_ROOT}/CHANGELOG/CHANGELOG-${IAM_GIT_VERSION#v}.md + + set +o errexit + git add ${IAM_ROOT}/CHANGELOG/CHANGELOG-${IAM_GIT_VERSION#v}.md + git commit -a -m "docs(changelog): add CHANGELOG-${IAM_GIT_VERSION#v}.md" + git push -f origin master # 最后将 CHANGELOG 也 push 上去 +} + diff --git a/scripts/lib/util.sh b/scripts/lib/util.sh new file mode 100755 index 000000000..86c32a69e --- /dev/null +++ b/scripts/lib/util.sh @@ -0,0 +1,683 @@ +#!/usr/bin/env bash + +# Copyright 2020 Lingfei Kong . All rights reserved. +# Use of this source code is governed by a MIT style +# license that can be found in the LICENSE file. + +function iam::util::sourced_variable { + # Call this function to tell shellcheck that a variable is supposed to + # be used from other calling context. This helps quiet an "unused + # variable" warning from shellcheck and also document your code. + true +} + +iam::util::sortable_date() { + date "+%Y%m%d-%H%M%S" +} + +# arguments: target, item1, item2, item3, ... +# returns 0 if target is in the given items, 1 otherwise. +iam::util::array_contains() { + local search="$1" + local element + shift + for element; do + if [[ "${element}" == "${search}" ]]; then + return 0 + fi + done + return 1 +} + +iam::util::wait_for_url() { + local url=$1 + local prefix=${2:-} + local wait=${3:-1} + local times=${4:-30} + local maxtime=${5:-1} + + command -v curl >/dev/null || { + iam::log::usage "curl must be installed" + exit 1 + } + + local i + for i in $(seq 1 "${times}"); do + local out + if out=$(curl --max-time "${maxtime}" -gkfs "${url}" 2>/dev/null); then + iam::log::status "On try ${i}, ${prefix}: ${out}" + return 0 + fi + sleep "${wait}" + done + iam::log::error "Timed out waiting for ${prefix} to answer at ${url}; tried ${times} waiting ${wait} between each" + return 1 +} + +# Example: iam::util::wait_for_success 120 5 "iamctl get nodes|grep localhost" +# arguments: wait time, sleep time, shell command +# returns 0 if the shell command get output, 1 otherwise. +iam::util::wait_for_success(){ + local wait_time="$1" + local sleep_time="$2" + local cmd="$3" + while [ "$wait_time" -gt 0 ]; do + if eval "$cmd"; then + return 0 + else + sleep "$sleep_time" + wait_time=$((wait_time-sleep_time)) + fi + done + return 1 +} + +# Example: iam::util::trap_add 'echo "in trap DEBUG"' DEBUG +# See: http://stackoverflow.com/questions/3338030/multiple-bash-traps-for-the-same-signal +iam::util::trap_add() { + local trap_add_cmd + trap_add_cmd=$1 + shift + + for trap_add_name in "$@"; do + local existing_cmd + local new_cmd + + # Grab the currently defined trap commands for this trap + existing_cmd=$(trap -p "${trap_add_name}" | awk -F"'" '{print $2}') + + if [[ -z "${existing_cmd}" ]]; then + new_cmd="${trap_add_cmd}" + else + new_cmd="${trap_add_cmd};${existing_cmd}" + fi + + # Assign the test. Disable the shellcheck warning telling that trap + # commands should be single quoted to avoid evaluating them at this + # point instead evaluating them at run time. The logic of adding new + # commands to a single trap requires them to be evaluated right away. + # shellcheck disable=SC2064 + trap "${new_cmd}" "${trap_add_name}" + done +} + +# Opposite of iam::util::ensure-temp-dir() +iam::util::cleanup-temp-dir() { + rm -rf "${IAM_TEMP}" +} + +# Create a temp dir that'll be deleted at the end of this bash session. +# +# Vars set: +# IAM_TEMP +iam::util::ensure-temp-dir() { + if [[ -z ${IAM_TEMP-} ]]; then + IAM_TEMP=$(mktemp -d 2>/dev/null || mktemp -d -t iamrnetes.XXXXXX) + iam::util::trap_add iam::util::cleanup-temp-dir EXIT + fi +} + +iam::util::host_os() { + local host_os + case "$(uname -s)" in + Darwin) + host_os=darwin + ;; + Linux) + host_os=linux + ;; + *) + iam::log::error "Unsupported host OS. Must be Linux or Mac OS X." + exit 1 + ;; + esac + echo "${host_os}" +} + +iam::util::host_arch() { + local host_arch + case "$(uname -m)" in + x86_64*) + host_arch=amd64 + ;; + i?86_64*) + host_arch=amd64 + ;; + amd64*) + host_arch=amd64 + ;; + aarch64*) + host_arch=arm64 + ;; + arm64*) + host_arch=arm64 + ;; + arm*) + host_arch=arm + ;; + i?86*) + host_arch=x86 + ;; + s390x*) + host_arch=s390x + ;; + ppc64le*) + host_arch=ppc64le + ;; + *) + iam::log::error "Unsupported host arch. Must be x86_64, 386, arm, arm64, s390x or ppc64le." + exit 1 + ;; + esac + echo "${host_arch}" +} + +# This figures out the host platform without relying on golang. We need this as +# we don't want a golang install to be a prerequisite to building yet we need +# this info to figure out where the final binaries are placed. +iam::util::host_platform() { + echo "$(iam::util::host_os)/$(iam::util::host_arch)" +} + +# looks for $1 in well-known output locations for the platform ($2) +# $IAM_ROOT must be set +iam::util::find-binary-for-platform() { + local -r lookfor="$1" + local -r platform="$2" + local locations=( + "${IAM_ROOT}/_output/bin/${lookfor}" + "${IAM_ROOT}/_output/${platform}/${lookfor}" + "${IAM_ROOT}/_output/local/bin/${platform}/${lookfor}" + "${IAM_ROOT}/_output/platforms/${platform}/${lookfor}" + ) + + # List most recently-updated location. + local -r bin=$( (ls -t "${locations[@]}" 2>/dev/null || true) | head -1 ) + echo -n "${bin}" +} + +# looks for $1 in well-known output locations for the host platform +# $IAM_ROOT must be set +iam::util::find-binary() { + iam::util::find-binary-for-platform "$1" "$(iam::util::host_platform)" +} + +# Run all known doc generators (today gendocs and genman for iamctl) +# $1 is the directory to put those generated documents +iam::util::gen-docs() { + local dest="$1" + + # Find binary + gendocs=$(iam::util::find-binary "gendocs") + geniamdocs=$(iam::util::find-binary "geniamdocs") + genman=$(iam::util::find-binary "genman") + genyaml=$(iam::util::find-binary "genyaml") + genfeddocs=$(iam::util::find-binary "genfeddocs") + + # TODO: If ${genfeddocs} is not used from anywhere (it isn't used at + # least from k/k tree), remove it completely. + iam::util::sourced_variable "${genfeddocs}" + + mkdir -p "${dest}/docs/guide/en-US/cmd/iamctl/" + "${gendocs}" "${dest}/docs/guide/en-US/cmd/iamctl/" + + mkdir -p "${dest}/docs/guide/en-US/cmd/" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "iam-apiserver" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "iam-authz-server" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "iam-pump" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "iam-watcher" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/iamctl" "iamctl" + + mkdir -p "${dest}/docs/man/man1/" + "${genman}" "${dest}/docs/man/man1/" "iam-apiserver" + "${genman}" "${dest}/docs/man/man1/" "iam-authz-server" + "${genman}" "${dest}/docs/man/man1/" "iam-pump" + "${genman}" "${dest}/docs/man/man1/" "iam-watcher" + "${genman}" "${dest}/docs/man/man1/" "iamctl" + + mkdir -p "${dest}/docs/guide/en-US/yaml/iamctl/" + "${genyaml}" "${dest}/docs/guide/en-US/yaml/iamctl/" + + # create the list of generated files + pushd "${dest}" > /dev/null || return 1 + touch docs/.generated_docs + find . -type f | cut -sd / -f 2- | LC_ALL=C sort > docs/.generated_docs + popd > /dev/null || return 1 +} + +# Removes previously generated docs-- we don't want to check them in. $IAM_ROOT +# must be set. +iam::util::remove-gen-docs() { + if [ -e "${IAM_ROOT}/docs/.generated_docs" ]; then + # remove all of the old docs; we don't want to check them in. + while read -r file; do + rm "${IAM_ROOT}/${file}" 2>/dev/null || true + done <"${IAM_ROOT}/docs/.generated_docs" + # The docs/.generated_docs file lists itself, so we don't need to explicitly + # delete it. + fi +} + +# Returns the name of the upstream remote repository name for the local git +# repo, e.g. "upstream" or "origin". +iam::util::git_upstream_remote_name() { + git remote -v | grep fetch |\ + grep -E 'github.com[/:]marmotedu/iam|marmotedu.io/iam' |\ + head -n 1 | awk '{print $1}' +} + +# Exits script if working directory is dirty. If it's run interactively in the terminal +# the user can commit changes in a second terminal. This script will wait. +iam::util::ensure_clean_working_dir() { + while ! git diff HEAD --exit-code &>/dev/null; do + echo -e "\nUnexpected dirty working directory:\n" + if tty -s; then + git status -s + else + git diff -a # be more verbose in log files without tty + exit 1 + fi | sed 's/^/ /' + echo -e "\nCommit your changes in another terminal and then continue here by pressing enter." + read -r + done 1>&2 +} + +# Find the base commit using: +# $PULL_BASE_SHA if set (from Prow) +# current ref from the remote upstream branch +iam::util::base_ref() { + local -r git_branch=$1 + + if [[ -n ${PULL_BASE_SHA:-} ]]; then + echo "${PULL_BASE_SHA}" + return + fi + + full_branch="$(iam::util::git_upstream_remote_name)/${git_branch}" + + # make sure the branch is valid, otherwise the check will pass erroneously. + if ! git describe "${full_branch}" >/dev/null; then + # abort! + exit 1 + fi + + echo "${full_branch}" +} + +# Checks whether there are any files matching pattern $2 changed between the +# current branch and upstream branch named by $1. +# Returns 1 (false) if there are no changes +# 0 (true) if there are changes detected. +iam::util::has_changes() { + local -r git_branch=$1 + local -r pattern=$2 + local -r not_pattern=${3:-totallyimpossiblepattern} + + local base_ref + base_ref=$(iam::util::base_ref "${git_branch}") + echo "Checking for '${pattern}' changes against '${base_ref}'" + + # notice this uses ... to find the first shared ancestor + if git diff --name-only "${base_ref}...HEAD" | grep -v -E "${not_pattern}" | grep "${pattern}" > /dev/null; then + return 0 + fi + # also check for pending changes + if git status --porcelain | grep -v -E "${not_pattern}" | grep "${pattern}" > /dev/null; then + echo "Detected '${pattern}' uncommitted changes." + return 0 + fi + echo "No '${pattern}' changes detected." + return 1 +} + +iam::util::download_file() { + local -r url=$1 + local -r destination_file=$2 + + rm "${destination_file}" 2&> /dev/null || true + + for i in $(seq 5) + do + if ! curl -fsSL --retry 3 --keepalive-time 2 "${url}" -o "${destination_file}"; then + echo "Downloading ${url} failed. $((5-i)) retries left." + sleep 1 + else + echo "Downloading ${url} succeed" + return 0 + fi + done + return 1 +} + +# Test whether openssl is installed. +# Sets: +# OPENSSL_BIN: The path to the openssl binary to use +function iam::util::test_openssl_installed { + if ! openssl version >& /dev/null; then + echo "Failed to run openssl. Please ensure openssl is installed" + exit 1 + fi + + OPENSSL_BIN=$(command -v openssl) +} + +# creates a client CA, args are sudo, dest-dir, ca-id, purpose +# purpose is dropped in after "key encipherment", you usually want +# '"client auth"' +# '"server auth"' +# '"client auth","server auth"' +function iam::util::create_signing_certkey { + local sudo=$1 + local dest_dir=$2 + local id=$3 + local purpose=$4 + # Create client ca + ${sudo} /usr/bin/env bash -e < "${dest_dir}/${id}-ca-config.json" +EOF +} + +# signs a client certificate: args are sudo, dest-dir, CA, filename (roughly), username, groups... +function iam::util::create_client_certkey { + local sudo=$1 + local dest_dir=$2 + local ca=$3 + local id=$4 + local cn=${5:-$4} + local groups="" + local SEP="" + shift 5 + while [ -n "${1:-}" ]; do + groups+="${SEP}{\"O\":\"$1\"}" + SEP="," + shift 1 + done + ${sudo} /usr/bin/env bash -e < /dev/null +apiVersion: v1 +kind: Config +clusters: + - cluster: + certificate-authority: ${ca_file} + server: https://${api_host}:${api_port}/ + name: local-up-cluster +users: + - user: + token: ${token} + client-certificate: ${dest_dir}/client-${client_id}.crt + client-key: ${dest_dir}/client-${client_id}.key + name: local-up-cluster +contexts: + - context: + cluster: local-up-cluster + user: local-up-cluster + name: local-up-cluster +current-context: local-up-cluster +EOF + + # flatten the iamconfig files to make them self contained + username=$(whoami) + ${sudo} /usr/bin/env bash -e < "/tmp/${client_id}.iamconfig" + mv -f "/tmp/${client_id}.iamconfig" "${dest_dir}/${client_id}.iamconfig" + chown ${username} "${dest_dir}/${client_id}.iamconfig" +EOF +} + +# Determines if docker can be run, failures may simply require that the user be added to the docker group. +function iam::util::ensure_docker_daemon_connectivity { + IFS=" " read -ra DOCKER <<< "${DOCKER_OPTS}" + # Expand ${DOCKER[@]} only if it's not unset. This is to work around + # Bash 3 issue with unbound variable. + DOCKER=(docker ${DOCKER[@]:+"${DOCKER[@]}"}) + if ! "${DOCKER[@]}" info > /dev/null 2>&1 ; then + cat <<'EOF' >&2 +Can't connect to 'docker' daemon. please fix and retry. + +Possible causes: + - Docker Daemon not started + - Linux: confirm via your init system + - macOS w/ docker-machine: run `docker-machine ls` and `docker-machine start ` + - macOS w/ Docker for Mac: Check the menu bar and start the Docker application + - DOCKER_HOST hasn't been set or is set incorrectly + - Linux: domain socket is used, DOCKER_* should be unset. In Bash run `unset ${!DOCKER_*}` + - macOS w/ docker-machine: run `eval "$(docker-machine env )"` + - macOS w/ Docker for Mac: domain socket is used, DOCKER_* should be unset. In Bash run `unset ${!DOCKER_*}` + - Other things to check: + - Linux: User isn't in 'docker' group. Add and relogin. + - Something like 'sudo usermod -a -G docker ${USER}' + - RHEL7 bug and workaround: https://bugzilla.redhat.com/show_bug.cgi?id=1119282#c8 +EOF + return 1 + fi +} + +# Wait for background jobs to finish. Return with +# an error status if any of the jobs failed. +iam::util::wait-for-jobs() { + local fail=0 + local job + for job in $(jobs -p); do + wait "${job}" || fail=$((fail + 1)) + done + return ${fail} +} + +# iam::util::join +# Concatenates the list elements with the delimiter passed as first parameter +# +# Ex: iam::util::join , a b c +# -> a,b,c +function iam::util::join { + local IFS="$1" + shift + echo "$*" +} + +# Downloads cfssl/cfssljson/cfssl-certinfo into $1 directory if they do not already exist in PATH +# +# Assumed vars: +# $1 (cfssl directory) (optional) +# +# Sets: +# CFSSL_BIN: The path of the installed cfssl binary +# CFSSLJSON_BIN: The path of the installed cfssljson binary +# CFSSLCERTINFO_BIN: The path of the installed cfssl-certinfo binary +# +function iam::util::ensure-cfssl { + if command -v cfssl &>/dev/null && command -v cfssljson &>/dev/null && command -v cfssl-certinfo &>/dev/null; then + CFSSL_BIN=$(command -v cfssl) + CFSSLJSON_BIN=$(command -v cfssljson) + CFSSLCERTINFO_BIN=$(command -v cfssl-certinfo) + return 0 + fi + + host_arch=$(iam::util::host_arch) + + if [[ "${host_arch}" != "amd64" ]]; then + echo "Cannot download cfssl on non-amd64 hosts and cfssl does not appear to be installed." + echo "Please install cfssl, cfssljson and cfssl-certinfo and verify they are in \$PATH." + echo "Hint: export PATH=\$PATH:\$GOPATH/bin; go get -u github.com/cloudflare/cfssl/cmd/..." + exit 1 + fi + + # Create a temp dir for cfssl if no directory was given + local cfssldir=${1:-} + if [[ -z "${cfssldir}" ]]; then + cfssldir="$HOME/bin" + fi + + mkdir -p "${cfssldir}" + pushd "${cfssldir}" > /dev/null || return 1 + + echo "Unable to successfully run 'cfssl' from ${PATH}; downloading instead..." + kernel=$(uname -s) + case "${kernel}" in + Linux) + curl --retry 10 -L -o cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 + curl --retry 10 -L -o cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 + curl --retry 10 -L -o cfssl-certinfo https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 + ;; + Darwin) + curl --retry 10 -L -o cfssl https://pkg.cfssl.org/R1.2/cfssl_darwin-amd64 + curl --retry 10 -L -o cfssljson https://pkg.cfssl.org/R1.2/cfssljson_darwin-amd64 + curl --retry 10 -L -o cfssl-certinfo https://pkg.cfssl.org/R1.2/cfssl-certinfo_darwin-amd64 + ;; + *) + echo "Unknown, unsupported platform: ${kernel}." >&2 + echo "Supported platforms: Linux, Darwin." >&2 + exit 2 + esac + + chmod +x cfssl || true + chmod +x cfssljson || true + chmod +x cfssl-certinfo || true + + CFSSL_BIN="${cfssldir}/cfssl" + CFSSLJSON_BIN="${cfssldir}/cfssljson" + CFSSLCERTINFO_BIN="${cfssldir}/cfssl-certinfo" + if [[ ! -x ${CFSSL_BIN} || ! -x ${CFSSLJSON_BIN} || ! -x ${CFSSLCERTINFO_BIN} ]]; then + echo "Failed to download 'cfssl'." + echo "Please install cfssl, cfssljson and cfssl-certinfo and verify they are in \$PATH." + echo "Hint: export PATH=\$PATH:\$GOPATH/bin; go get -u github.com/cloudflare/cfssl/cmd/..." + exit 1 + fi + popd > /dev/null || return 1 +} + +# iam::util::ensure-gnu-sed +# Determines which sed binary is gnu-sed on linux/darwin +# +# Sets: +# SED: The name of the gnu-sed binary +# +function iam::util::ensure-gnu-sed { + # NOTE: the echo below is a workaround to ensure sed is executed before the grep. + # see: https://github.com/iamrnetes/iamrnetes/issues/87251 + sed_help="$(LANG=C sed --help 2>&1 || true)" + if echo "${sed_help}" | grep -q "GNU\|BusyBox"; then + SED="sed" + elif command -v gsed &>/dev/null; then + SED="gsed" + else + iam::log::error "Failed to find GNU sed as sed or gsed. If you are on Mac: brew install gnu-sed." >&2 + return 1 + fi + iam::util::sourced_variable "${SED}" +} + +# iam::util::check-file-in-alphabetical-order +# Check that the file is in alphabetical order +# +function iam::util::check-file-in-alphabetical-order { + local failure_file="$1" + if ! diff -u "${failure_file}" <(LC_ALL=C sort "${failure_file}"); then + { + echo + echo "${failure_file} is not in alphabetical order. Please sort it:" + echo + echo " LC_ALL=C sort -o ${failure_file} ${failure_file}" + echo + } >&2 + false + fi +} + +# iam::util::require-jq +# Checks whether jq is installed. +function iam::util::require-jq { + if ! command -v jq &>/dev/null; then + echo "jq not found. Please install." 1>&2 + return 1 + fi +} + +# outputs md5 hash of $1, works on macOS and Linux +function iam::util::md5() { + if which md5 >/dev/null 2>&1; then + md5 -q "$1" + else + md5sum "$1" | awk '{ print $1 }' + fi +} + +# iam::util::read-array +# Reads in stdin and adds it line by line to the array provided. This can be +# used instead of "mapfile -t", and is bash 3 compatible. +# +# Assumed vars: +# $1 (name of array to create/modify) +# +# Example usage: +# iam::util::read-array files < <(ls -1) +# +function iam::util::read-array { + local i=0 + unset -v "$1" + while IFS= read -r "$1[i++]"; do :; done + eval "[[ \${$1[--i]} ]]" || unset "$1[i]" # ensures last element isn't empty +} + +# Some useful colors. +if [[ -z "${color_start-}" ]]; then + declare -r color_start="\033[" + declare -r color_red="${color_start}0;31m" + declare -r color_yellow="${color_start}0;33m" + declare -r color_green="${color_start}0;32m" + declare -r color_blue="${color_start}1;34m" + declare -r color_cyan="${color_start}1;36m" + declare -r color_norm="${color_start}0m" + + iam::util::sourced_variable "${color_start}" + iam::util::sourced_variable "${color_red}" + iam::util::sourced_variable "${color_yellow}" + iam::util::sourced_variable "${color_green}" + iam::util::sourced_variable "${color_blue}" + iam::util::sourced_variable "${color_cyan}" + iam::util::sourced_variable "${color_norm}" +fi + +# ex: ts=2 sw=2 et filetype=sh diff --git a/scripts/lib/version.sh b/scripts/lib/version.sh new file mode 100755 index 000000000..0edeac114 --- /dev/null +++ b/scripts/lib/version.sh @@ -0,0 +1,137 @@ +#!/usr/bin/env bash + +# Copyright 2020 Lingfei Kong . All rights reserved. +# Use of this source code is governed by a MIT style +# license that can be found in the LICENSE file. + +# ----------------------------------------------------------------------------- +# Version management helpers. These functions help to set, save and load the +# following variables: +# +# IAM_GIT_COMMIT - The git commit id corresponding to this +# source code. +# IAM_GIT_TREE_STATE - "clean" indicates no changes since the git commit id +# "dirty" indicates source code changes after the git commit id +# "archive" indicates the tree was produced by 'git archive' +# IAM_GIT_VERSION - "vX.Y" used to indicate the last release version. +# IAM_GIT_MAJOR - The major part of the version +# IAM_GIT_MINOR - The minor component of the version + +# Grovels through git to set a set of env variables. +# +# If IAM_GIT_VERSION_FILE, this function will load from that file instead of +# querying git. +iam::version::get_version_vars() { + if [[ -n ${IAM_GIT_VERSION_FILE-} ]]; then + iam::version::load_version_vars "${IAM_GIT_VERSION_FILE}" + return + fi + + # If the iamrnetes source was exported through git archive, then + # we likely don't have a git tree, but these magic values may be filled in. + # shellcheck disable=SC2016,SC2050 + # Disabled as we're not expanding these at runtime, but rather expecting + # that another tool may have expanded these and rewritten the source (!) + if [[ '$Format:%%$' == "%" ]]; then + IAM_GIT_COMMIT='$Format:%H$' + IAM_GIT_TREE_STATE="archive" + # When a 'git archive' is exported, the '$Format:%D$' below will look + # something like 'HEAD -> release-1.8, tag: v1.8.3' where then 'tag: ' + # can be extracted from it. + if [[ '$Format:%D$' =~ tag:\ (v[^ ,]+) ]]; then + IAM_GIT_VERSION="${BASH_REMATCH[1]}" + fi + fi + + local git=(git --work-tree "${IAM_ROOT}") + + if [[ -n ${IAM_GIT_COMMIT-} ]] || IAM_GIT_COMMIT=$("${git[@]}" rev-parse "HEAD^{commit}" 2>/dev/null); then + if [[ -z ${IAM_GIT_TREE_STATE-} ]]; then + # Check if the tree is dirty. default to dirty + if git_status=$("${git[@]}" status --porcelain 2>/dev/null) && [[ -z ${git_status} ]]; then + IAM_GIT_TREE_STATE="clean" + else + IAM_GIT_TREE_STATE="dirty" + fi + fi + + # Use git describe to find the version based on tags. + if [[ -n ${IAM_GIT_VERSION-} ]] || IAM_GIT_VERSION=$("${git[@]}" describe --tags --always --match='v*' 2>/dev/null); then + # This translates the "git describe" to an actual semver.org + # compatible semantic version that looks something like this: + # v1.1.0-alpha.0.6+84c76d1142ea4d + # + # TODO: We continue calling this "git version" because so many + # downstream consumers are expecting it there. + # + # These regexes are painful enough in sed... + # We don't want to do them in pure shell, so disable SC2001 + # shellcheck disable=SC2001 + DASHES_IN_VERSION=$(echo "${IAM_GIT_VERSION}" | sed "s/[^-]//g") + if [[ "${DASHES_IN_VERSION}" == "---" ]] ; then + # shellcheck disable=SC2001 + # We have distance to subversion (v1.1.0-subversion-1-gCommitHash) + IAM_GIT_VERSION=$(echo "${IAM_GIT_VERSION}" | sed "s/-\([0-9]\{1,\}\)-g\([0-9a-f]\{14\}\)$/.\1\+\2/") + elif [[ "${DASHES_IN_VERSION}" == "--" ]] ; then + # shellcheck disable=SC2001 + # We have distance to base tag (v1.1.0-1-gCommitHash) + IAM_GIT_VERSION=$(echo "${IAM_GIT_VERSION}" | sed "s/-g\([0-9a-f]\{14\}\)$/+\1/") + fi + if [[ "${IAM_GIT_TREE_STATE}" == "dirty" ]]; then + # git describe --dirty only considers changes to existing files, but + # that is problematic since new untracked .go files affect the build, + # so use our idea of "dirty" from git status instead. + # TODO? + #IAM_GIT_VERSION+="-dirty" + : + fi + + + # Try to match the "git describe" output to a regex to try to extract + # the "major" and "minor" versions and whether this is the exact tagged + # version or whether the tree is between two tagged versions. + if [[ "${IAM_GIT_VERSION}" =~ ^v([0-9]+)\.([0-9]+)(\.[0-9]+)?([-].*)?([+].*)?$ ]]; then + IAM_GIT_MAJOR=${BASH_REMATCH[1]} + IAM_GIT_MINOR=${BASH_REMATCH[2]} + if [[ -n "${BASH_REMATCH[4]}" ]]; then + IAM_GIT_MINOR+="+" + fi + fi + + # If IAM_GIT_VERSION is not a valid Semantic Version, then refuse to build. + if ! [[ "${IAM_GIT_VERSION}" =~ ^v([0-9]+)\.([0-9]+)(\.[0-9]+)?(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then + echo "IAM_GIT_VERSION should be a valid Semantic Version. Current value: ${IAM_GIT_VERSION}" + echo "Please see more details here: https://semver.org" + exit 1 + fi + fi + fi +} + +# Saves the environment flags to $1 +iam::version::save_version_vars() { + local version_file=${1-} + [[ -n ${version_file} ]] || { + echo "!!! Internal error. No file specified in iam::version::save_version_vars" + return 1 + } + + cat <"${version_file}" +IAM_GIT_COMMIT='${IAM_GIT_COMMIT-}' +IAM_GIT_TREE_STATE='${IAM_GIT_TREE_STATE-}' +IAM_GIT_VERSION='${IAM_GIT_VERSION-}' +IAM_GIT_MAJOR='${IAM_GIT_MAJOR-}' +IAM_GIT_MINOR='${IAM_GIT_MINOR-}' +EOF +} + +# Loads up the version variables from file $1 +iam::version::load_version_vars() { + local version_file=${1-} + [[ -n ${version_file} ]] || { + echo "!!! Internal error. No file specified in iam::version::load_version_vars" + return 1 + } + + source "${version_file}" +} diff --git a/scripts/make-rules/golang.mk b/scripts/make-rules/golang.mk index 255306062..bf503db63 100644 --- a/scripts/make-rules/golang.mk +++ b/scripts/make-rules/golang.mk @@ -132,24 +132,17 @@ go.build.multiarch: go.build.verify $(foreach p,$(PLATFORMS),$(addprefix go.buil go.lint: tools.verify.golangci-lint @echo "===========> Run golangci to lint source codes" @$(TOOLS_DIR)/golangci-lint run --color always -c $(ROOT_DIR)/.golangci.yml $(ROOT_DIR)/... + ## go.test: Run unit test .PHONY: go.test go.test: @$(GO) test ./... -# ## go.test.junit-report: Run unit test -# .PHONY: go.test.junit-report -# go.test.junit-report: tools.verify.go-junit-report -# @echo "===========> Run unit test > $(TMP_DIR)/report.xml" -# @$(GO) test -v -coverprofile=$(TMP_DIR)/coverage.out 2>&1 $(GO_BUILD_FLAGS) ./... | $(TOOLS_DIR)/go-junit-report -set-exit-code > $(TMP_DIR)/report.xml -# @sed -i '/mock_.*.go/d' $(TMP_DIR)/coverage.out -# @echo "===========> Test coverage of Go code is reported to $(TMP_DIR)/coverage.html by generating HTML" -# @$(GO) tool cover -html=$(TMP_DIR)/coverage.out -o $(TMP_DIR)/coverage.html - ## go.test.junit-report: Run unit test .PHONY: go.test.junit-report go.test.junit-report: tools.verify.go-junit-report @echo "===========> Run unit test > $(TMP_DIR)/report.xml" +# @$(GO) test -v -coverprofile=$(TMP_DIR)/coverage.out 2>&1 $(GO_BUILD_FLAGS) ./... | $(TOOLS_DIR)/go-junit-report -set-exit-code > $(TMP_DIR)/report.xml @$(GO) test -v -coverprofile=$(TMP_DIR)/coverage.out 2>&1 ./... | $(TOOLS_DIR)/go-junit-report -set-exit-code > $(TMP_DIR)/report.xml @sed -i '/mock_.*.go/d' $(TMP_DIR)/coverage.out @echo "===========> Test coverage of Go code is reported to $(TMP_DIR)/coverage.html by generating HTML" From 21789bd7ac8cea24fd660fd8468f6c3c50dbab4d Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Tue, 4 Jul 2023 11:15:20 +0800 Subject: [PATCH 09/73] feat: add copyright Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- .docker-compose_cfg/grafana.ini | 2 +- cmd/api/deploy.Dockerfile | 14 + cmd/api/main.go | 14 + cmd/cmdutils/main.go | 14 + cmd/crontask/deploy.Dockerfile | 14 + cmd/crontask/main.go | 14 + cmd/msggateway/deploy.Dockerfile | 14 + cmd/msggateway/main.go | 14 + cmd/msgtransfer/deploy.Dockerfile | 14 + cmd/msgtransfer/main.go | 14 + cmd/push/deploy.Dockerfile | 14 + cmd/push/main.go | 14 + cmd/rpc/auth/deploy.Dockerfile | 14 + cmd/rpc/auth/main.go | 14 + cmd/rpc/conversation/deploy.Dockerfile | 14 + cmd/rpc/conversation/main.go | 14 + cmd/rpc/friend/deploy.Dockerfile | 14 + cmd/rpc/friend/main.go | 14 + cmd/rpc/group/deploy.Dockerfile | 14 + cmd/rpc/group/main.go | 14 + cmd/rpc/msg/deploy.Dockerfile | 14 + cmd/rpc/msg/main.go | 14 + cmd/rpc/third/deploy.Dockerfile | 14 + cmd/rpc/third/main.go | 14 + cmd/rpc/user/deploy.Dockerfile | 14 + cmd/rpc/user/main.go | 14 + config/config.yaml | 14 + config/notification.yaml | 14 + internal/api/auth.go | 14 + internal/api/conversation.go | 14 + internal/api/custom_validator.go | 14 + internal/api/friend.go | 14 + internal/api/group.go | 14 + internal/api/msg.go | 14 + internal/api/route.go | 14 + internal/api/statistics.go | 14 + internal/api/third.go | 14 + internal/api/user.go | 14 + internal/msggateway/callback.go | 14 + internal/msggateway/client.go | 14 + internal/msggateway/compressor.go | 14 + internal/msggateway/constant.go | 14 + internal/msggateway/context.go | 14 + internal/msggateway/encoder.go | 14 + internal/msggateway/http_error.go | 14 + internal/msggateway/hub_server.go | 14 + internal/msggateway/init.go | 14 + internal/msggateway/long_conn.go | 14 + internal/msggateway/message_handler.go | 14 + internal/msggateway/n_ws_server.go | 14 + internal/msggateway/options.go | 14 + internal/msggateway/user_map.go | 14 + internal/msgtransfer/init.go | 14 + internal/msgtransfer/modify_msg_handler.go | 14 + .../msgtransfer/online_history_msg_handler.go | 14 + .../online_msg_to_mongo_handler.go | 14 + internal/push/callback.go | 14 + internal/push/consumer_init.go | 14 + internal/push/offlinepush/fcm/push.go | 14 + internal/push/offlinepush/fcm/push_test.go | 14 + internal/push/offlinepush/getui/body.go | 14 + internal/push/offlinepush/getui/push.go | 14 + .../push/offlinepush/jpush/body/audience.go | 14 + .../push/offlinepush/jpush/body/message.go | 14 + .../offlinepush/jpush/body/notification.go | 14 + .../push/offlinepush/jpush/body/options.go | 14 + .../push/offlinepush/jpush/body/platform.go | 14 + .../push/offlinepush/jpush/body/pushobj.go | 14 + internal/push/offlinepush/jpush/push.go | 14 + .../push/offlinepush/offlinepush_interface.go | 14 + internal/push/push_handler.go | 14 + internal/push/push_rpc_server.go | 14 + internal/push/push_to_client.go | 14 + internal/rpc/auth/auth.go | 14 + internal/rpc/conversation/conversaion.go | 14 + internal/rpc/friend/black.go | 14 + internal/rpc/friend/callback.go | 14 + internal/rpc/friend/friend.go | 14 + internal/rpc/group/cache.go | 14 + internal/rpc/group/callback.go | 14 + internal/rpc/group/convert.go | 14 + internal/rpc/group/db_map.go | 14 + internal/rpc/group/fill.go | 14 + internal/rpc/group/group.go | 14 + internal/rpc/group/super_group.go | 14 + internal/rpc/msg/as_read.go | 14 + internal/rpc/msg/callback.go | 14 + internal/rpc/msg/delete.go | 14 + internal/rpc/msg/extend_msg.go | 14 + internal/rpc/msg/extend_msg_callback.go | 14 + internal/rpc/msg/lock.go | 14 + internal/rpc/msg/message_interceptor.go | 14 + internal/rpc/msg/msg_status.go | 14 + internal/rpc/msg/revoke.go | 14 + internal/rpc/msg/send.go | 14 + internal/rpc/msg/seq.go | 14 + internal/rpc/msg/server.go | 14 + internal/rpc/msg/sync_msg.go | 14 + internal/rpc/msg/utils.go | 14 + internal/rpc/msg/verify.go | 14 + internal/rpc/statistics/statistics.go | 14 + internal/rpc/third/s3.go | 14 + internal/rpc/third/third.go | 14 + internal/rpc/user/statistics.go | 14 + internal/rpc/user/user.go | 14 + internal/tools/cron_task.go | 14 + internal/tools/msg.go | 14 + internal/tools/msg_test.go | 14 + pkg/a2r/api2rpc.go | 14 + pkg/apiresp/gin.go | 14 + pkg/apiresp/http.go | 14 + pkg/apiresp/resp.go | 14 + pkg/apistruct/auth.go | 14 + pkg/apistruct/aws.go | 14 + pkg/apistruct/conversation.go | 14 + pkg/apistruct/cos.go | 14 + pkg/apistruct/friend.go | 14 + pkg/apistruct/group.go | 14 + pkg/apistruct/manage.go | 14 + pkg/apistruct/msg.go | 14 + pkg/apistruct/oss.go | 14 + pkg/apistruct/pagination.go | 14 + pkg/apistruct/public.go | 14 + pkg/apistruct/super_group.go | 14 + pkg/apistruct/third.go | 14 + pkg/callbackstruct/common.go | 14 + pkg/callbackstruct/friend.go | 14 + pkg/callbackstruct/group.go | 14 + pkg/callbackstruct/message.go | 14 + pkg/callbackstruct/msg_gateway.go | 14 + pkg/callbackstruct/push.go | 14 + pkg/checker/check.go | 14 + pkg/common/cmd/api.go | 14 + pkg/common/cmd/cron_task.go | 14 + pkg/common/cmd/msg_gateway.go | 14 + pkg/common/cmd/msg_transfer.go | 14 + pkg/common/cmd/msg_utils.go | 14 + pkg/common/cmd/root.go | 14 + pkg/common/cmd/rpc.go | 14 + pkg/common/config/config.go | 14 + pkg/common/config/parse.go | 14 + pkg/common/constant/constant.go | 14 + pkg/common/constant/limit.go | 14 + pkg/common/constant/platform_id_to_name.go | 14 + pkg/common/convert/black.go | 14 + pkg/common/convert/conversation.go | 14 + pkg/common/convert/friend.go | 14 + pkg/common/convert/group.go | 14 + pkg/common/convert/msg.go | 14 + pkg/common/convert/user.go | 14 + pkg/common/db/cache/black.go | 14 + pkg/common/db/cache/conversation.go | 14 + pkg/common/db/cache/extend_msg_set.go | 14 + pkg/common/db/cache/friend.go | 14 + pkg/common/db/cache/group.go | 14 + pkg/common/db/cache/init_redis.go | 14 + pkg/common/db/cache/meta_cache.go | 14 + pkg/common/db/cache/msg.go | 14 + pkg/common/db/cache/user.go | 14 + pkg/common/db/controller/auth.go | 14 + pkg/common/db/controller/black.go | 14 + pkg/common/db/controller/chatlog.go | 14 + pkg/common/db/controller/conversation.go | 14 + pkg/common/db/controller/extend_msg.go | 14 + pkg/common/db/controller/friend.go | 14 + pkg/common/db/controller/group.go | 14 + pkg/common/db/controller/msg.go | 14 + pkg/common/db/controller/msg_test.go | 14 + pkg/common/db/controller/push.go | 14 + pkg/common/db/controller/storage.go | 14 + pkg/common/db/controller/third.go | 14 + pkg/common/db/controller/user.go | 14 + pkg/common/db/localcache/conversation.go | 14 + pkg/common/db/localcache/group.go | 14 + pkg/common/db/localcache/meta_local_cache.go | 14 + pkg/common/db/obj/minio.go | 14 + pkg/common/db/obj/obj.go | 14 + pkg/common/db/ormutil/utils.go | 14 + pkg/common/db/relation/black_model.go | 14 + pkg/common/db/relation/chat_log_model.go | 14 + pkg/common/db/relation/conversation_model.go | 14 + pkg/common/db/relation/friend_model.go | 14 + .../db/relation/friend_request_model.go | 14 + pkg/common/db/relation/group_member_model.go | 14 + pkg/common/db/relation/group_model.go | 14 + pkg/common/db/relation/group_request_model.go | 14 + pkg/common/db/relation/meta_db.go | 14 + pkg/common/db/relation/mysql_init.go | 14 + pkg/common/db/relation/object_hash_model.go | 14 + pkg/common/db/relation/object_info_model.go | 14 + pkg/common/db/relation/object_put_model.go | 14 + pkg/common/db/relation/user_model.go | 14 + pkg/common/db/table/relation/black.go | 14 + pkg/common/db/table/relation/chatlog.go | 14 + pkg/common/db/table/relation/conversation.go | 14 + pkg/common/db/table/relation/friend.go | 14 + .../db/table/relation/friend_request.go | 14 + pkg/common/db/table/relation/group.go | 14 + pkg/common/db/table/relation/group_member.go | 14 + pkg/common/db/table/relation/group_request.go | 14 + pkg/common/db/table/relation/object_hash.go | 14 + pkg/common/db/table/relation/object_info.go | 14 + pkg/common/db/table/relation/object_put.go | 14 + pkg/common/db/table/relation/user.go | 14 + pkg/common/db/table/relation/utils.go | 14 + pkg/common/db/table/unrelation/common.go | 14 + .../db/table/unrelation/extend_msg_set.go | 14 + pkg/common/db/table/unrelation/msg.go | 14 + pkg/common/db/table/unrelation/super_group.go | 14 + pkg/common/db/tx/gorm.go | 14 + pkg/common/db/tx/mongo.go | 14 + pkg/common/db/tx/tx.go | 14 + pkg/common/db/unrelation/extend_msg.go | 14 + pkg/common/db/unrelation/mongo.go | 14 + pkg/common/db/unrelation/msg.go | 14 + pkg/common/db/unrelation/super_group.go | 14 + pkg/common/kafka/consumer.go | 14 + pkg/common/kafka/producer.go | 14 + pkg/common/log/color.go | 14 + pkg/common/log/logger.go | 14 + pkg/common/log/sql_logger.go | 14 + pkg/common/log/zap.go | 14 + pkg/common/log/zk_logger.go | 14 + pkg/common/mcontext/ctx.go | 14 + pkg/common/mw/check.go | 14 + pkg/common/mw/check_test.go | 14 + pkg/common/mw/gin.go | 14 + pkg/common/mw/intercept_chain.go | 14 + pkg/common/mw/rpc_client_interceptor.go | 14 + pkg/common/mw/rpc_server_interceptor.go | 14 + pkg/common/mw/specialerror/error.go | 14 + pkg/common/network/ip.go | 14 + pkg/common/prome/gather.go | 14 + pkg/common/prome/prometheus.go | 14 + pkg/common/tokenverify/jwt_token.go | 14 + pkg/common/tokenverify/jwt_token_test.go | 14 + pkg/discoveryregistry/discovery_register.go | 14 + pkg/discoveryregistry/zookeeper/conf.go | 14 + pkg/discoveryregistry/zookeeper/discover.go | 14 + pkg/discoveryregistry/zookeeper/register.go | 14 + pkg/discoveryregistry/zookeeper/resolver.go | 14 + pkg/discoveryregistry/zookeeper/zk.go | 14 + pkg/errs/code.go | 14 + pkg/errs/coderr.go | 14 + pkg/errs/predefine.go | 14 + pkg/errs/relation.go | 14 + pkg/proto/auth/auth.go | 14 + pkg/proto/auth/auth.proto | 14 + pkg/proto/conversation/conversation.go | 14 + pkg/proto/conversation/conversation.proto | 14 + pkg/proto/errinfo/errinfo.proto | 14 + pkg/proto/friend/friend.go | 14 + pkg/proto/friend/friend.proto | 14 + pkg/proto/gen.sh | 14 + pkg/proto/group/group.go | 14 + pkg/proto/group/group.proto | 14 + pkg/proto/msg/msg.go | 14 + pkg/proto/msg/msg.proto | 14 + pkg/proto/msggateway/msggateway.go | 14 + pkg/proto/msggateway/msggateway.proto | 14 + pkg/proto/push/push.go | 14 + pkg/proto/push/push.proto | 14 + pkg/proto/sdkws/sdkws.go | 14 + pkg/proto/sdkws/sdkws.proto | 14 + pkg/proto/statistics/statistics.proto | 14 + pkg/proto/third/third.go | 14 + pkg/proto/third/third.proto | 14 + pkg/proto/user/user.go | 14 + pkg/proto/user/user.proto | 14 + pkg/proto/wrapperspb/wrapperspb.go | 14 + pkg/proto/wrapperspb/wrapperspb.proto | 14 + pkg/rpcclient/auth.go | 14 + pkg/rpcclient/conversation.go | 14 + pkg/rpcclient/friend.go | 14 + pkg/rpcclient/group.go | 14 + pkg/rpcclient/msg.go | 14 + pkg/rpcclient/notification/common.go | 14 + pkg/rpcclient/notification/conevrsation.go | 14 + pkg/rpcclient/notification/extend_msg.go | 14 + pkg/rpcclient/notification/friend.go | 14 + pkg/rpcclient/notification/group.go | 14 + pkg/rpcclient/push.go | 14 + pkg/rpcclient/third.go | 14 + pkg/rpcclient/user.go | 14 + pkg/startrpc/start.go | 14 + pkg/statistics/statistics.go | 14 + pkg/utils/base64.go | 14 + pkg/utils/callback.go | 14 + pkg/utils/encryption.go | 14 + pkg/utils/file.go | 14 + pkg/utils/get_server_ip.go | 14 + pkg/utils/id.go | 14 + pkg/utils/id_test.go | 14 + pkg/utils/image.go | 14 + pkg/utils/image_test.go | 14 + pkg/utils/map.go | 14 + pkg/utils/md5_test.go | 14 + pkg/utils/options.go | 14 + pkg/utils/page.go | 14 + pkg/utils/platform_number_id_to_name_test.go | 14 + pkg/utils/retry/retry.go | 14 + pkg/utils/retry/stratey.go | 14 + pkg/utils/splitter/tools.go | 14 + pkg/utils/utils.go | 14 + pkg/utils/utils_v2.go | 14 + pkg/utils/utils_v2_test.go | 14 + scripts/batch_build_all_service.sh | 14 + scripts/batch_start_all.sh | 14 + scripts/build_all_service.sh | 14 + scripts/build_images.sh | 14 + scripts/build_push_k8s_images.sh | 14 + scripts/check_all.sh | 14 + scripts/coverage.sh | 14 + scripts/docker_check_service.sh | 14 + scripts/docker_start_all.sh | 14 + scripts/ensure_tag.sh | 23 ++ scripts/enterprise/check_all.sh | 14 + scripts/enterprise/function.sh | 14 + scripts/env_check.sh | 14 + scripts/function.sh | 14 + scripts/githooks/pre-commit | 1 - scripts/init_pwd.sh | 14 + scripts/lib/color.sh | 18 +- scripts/lib/golang.sh | 152 ++++---- scripts/lib/init.sh | 30 +- scripts/lib/logging.sh | 62 +-- scripts/lib/release.sh | 352 +++++++++--------- scripts/lib/util.sh | 204 +++++----- scripts/lib/version.sh | 94 ++--- scripts/make-rules/gen.mk | 4 +- scripts/make-rules/tools.mk | 2 +- scripts/mongo-init.sh | 14 + scripts/msg_gateway_start.sh | 14 + scripts/msg_transfer_start.sh | 14 + scripts/push_start.sh | 14 + scripts/release.sh | 42 ++- scripts/sdk_svr_start.sh | 14 + scripts/start_all.sh | 14 + scripts/start_cron.sh | 14 + scripts/start_rpc_service.sh | 14 + scripts/stop_all.sh | 14 + 341 files changed, 5138 insertions(+), 440 deletions(-) create mode 100644 scripts/ensure_tag.sh diff --git a/.docker-compose_cfg/grafana.ini b/.docker-compose_cfg/grafana.ini index b9380d7f1..8c1d9dd11 100644 --- a/.docker-compose_cfg/grafana.ini +++ b/.docker-compose_cfg/grafana.ini @@ -633,7 +633,7 @@ active_sync_enabled = true #################################### AWS ########################### [aws] # Enter a comma-separated list of allowed AWS authentication providers. -# Options are: default (AWS SDK Default), keys (Access && secret key), credentials (Credentials field), ec2_iam_role (EC2 IAM Role) +# Options are: default (AWS SDK Default), keys (Access && secret key), credentials (Credentials field), ec2_iam_role (EC2 OpenIM Role) allowed_auth_providers = default,keys,credentials # Allow AWS users to assume a role using temporary security credentials. diff --git a/cmd/api/deploy.Dockerfile b/cmd/api/deploy.Dockerfile index ad397ca23..a236c02f0 100644 --- a/cmd/api/deploy.Dockerfile +++ b/cmd/api/deploy.Dockerfile @@ -1,3 +1,17 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + FROM ubuntu WORKDIR /Open-IM-Server/bin diff --git a/cmd/api/main.go b/cmd/api/main.go index fb613b4da..aac83a805 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( diff --git a/cmd/cmdutils/main.go b/cmd/cmdutils/main.go index 8c68fca6d..64079c7e1 100644 --- a/cmd/cmdutils/main.go +++ b/cmd/cmdutils/main.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( diff --git a/cmd/crontask/deploy.Dockerfile b/cmd/crontask/deploy.Dockerfile index 80ba5abf0..e80006def 100644 --- a/cmd/crontask/deploy.Dockerfile +++ b/cmd/crontask/deploy.Dockerfile @@ -1,3 +1,17 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + FROM ubuntu WORKDIR /Open-IM-Server/bin diff --git a/cmd/crontask/main.go b/cmd/crontask/main.go index 21a1cdff1..73deb8c66 100644 --- a/cmd/crontask/main.go +++ b/cmd/crontask/main.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( diff --git a/cmd/msggateway/deploy.Dockerfile b/cmd/msggateway/deploy.Dockerfile index bacb37b74..7310fe0dc 100644 --- a/cmd/msggateway/deploy.Dockerfile +++ b/cmd/msggateway/deploy.Dockerfile @@ -1,3 +1,17 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + FROM ubuntu WORKDIR /Open-IM-Server/bin diff --git a/cmd/msggateway/main.go b/cmd/msggateway/main.go index 695e45e1c..2782e4fd3 100644 --- a/cmd/msggateway/main.go +++ b/cmd/msggateway/main.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( diff --git a/cmd/msgtransfer/deploy.Dockerfile b/cmd/msgtransfer/deploy.Dockerfile index 4e48f8e3d..0c9485694 100644 --- a/cmd/msgtransfer/deploy.Dockerfile +++ b/cmd/msgtransfer/deploy.Dockerfile @@ -1,3 +1,17 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + FROM ubuntu WORKDIR /Open-IM-Server/bin diff --git a/cmd/msgtransfer/main.go b/cmd/msgtransfer/main.go index 7f8d1ce80..aef347793 100644 --- a/cmd/msgtransfer/main.go +++ b/cmd/msgtransfer/main.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( diff --git a/cmd/push/deploy.Dockerfile b/cmd/push/deploy.Dockerfile index f474b9ab2..79b8cb4f1 100644 --- a/cmd/push/deploy.Dockerfile +++ b/cmd/push/deploy.Dockerfile @@ -1,3 +1,17 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + FROM ubuntu WORKDIR /Open-IM-Server/bin diff --git a/cmd/push/main.go b/cmd/push/main.go index 05d8e70b4..03db8ae03 100644 --- a/cmd/push/main.go +++ b/cmd/push/main.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( diff --git a/cmd/rpc/auth/deploy.Dockerfile b/cmd/rpc/auth/deploy.Dockerfile index 700e85d4f..a7b69cc0a 100644 --- a/cmd/rpc/auth/deploy.Dockerfile +++ b/cmd/rpc/auth/deploy.Dockerfile @@ -1,3 +1,17 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + FROM ubuntu WORKDIR /Open-IM-Server/bin diff --git a/cmd/rpc/auth/main.go b/cmd/rpc/auth/main.go index 4d6986f84..524804988 100644 --- a/cmd/rpc/auth/main.go +++ b/cmd/rpc/auth/main.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( diff --git a/cmd/rpc/conversation/deploy.Dockerfile b/cmd/rpc/conversation/deploy.Dockerfile index 6b3a8ad40..4e7591499 100644 --- a/cmd/rpc/conversation/deploy.Dockerfile +++ b/cmd/rpc/conversation/deploy.Dockerfile @@ -1,3 +1,17 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + FROM ubuntu WORKDIR /Open-IM-Server/bin diff --git a/cmd/rpc/conversation/main.go b/cmd/rpc/conversation/main.go index 9f227fe17..fec8226f8 100644 --- a/cmd/rpc/conversation/main.go +++ b/cmd/rpc/conversation/main.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( diff --git a/cmd/rpc/friend/deploy.Dockerfile b/cmd/rpc/friend/deploy.Dockerfile index a57f9ccdc..d159ee0dd 100644 --- a/cmd/rpc/friend/deploy.Dockerfile +++ b/cmd/rpc/friend/deploy.Dockerfile @@ -1,3 +1,17 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + FROM ubuntu WORKDIR /Open-IM-Server/bin diff --git a/cmd/rpc/friend/main.go b/cmd/rpc/friend/main.go index 0838eaf5b..fbd44038e 100644 --- a/cmd/rpc/friend/main.go +++ b/cmd/rpc/friend/main.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( diff --git a/cmd/rpc/group/deploy.Dockerfile b/cmd/rpc/group/deploy.Dockerfile index 1d083c927..e676708d5 100644 --- a/cmd/rpc/group/deploy.Dockerfile +++ b/cmd/rpc/group/deploy.Dockerfile @@ -1,3 +1,17 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + FROM ubuntu WORKDIR /Open-IM-Server/bin diff --git a/cmd/rpc/group/main.go b/cmd/rpc/group/main.go index b73dd73c9..06baac155 100644 --- a/cmd/rpc/group/main.go +++ b/cmd/rpc/group/main.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( diff --git a/cmd/rpc/msg/deploy.Dockerfile b/cmd/rpc/msg/deploy.Dockerfile index 9b5f4194d..8e6307ccb 100644 --- a/cmd/rpc/msg/deploy.Dockerfile +++ b/cmd/rpc/msg/deploy.Dockerfile @@ -1,3 +1,17 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + FROM ubuntu WORKDIR /Open-IM-Server/bin diff --git a/cmd/rpc/msg/main.go b/cmd/rpc/msg/main.go index 7b9756680..356081d33 100644 --- a/cmd/rpc/msg/main.go +++ b/cmd/rpc/msg/main.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( diff --git a/cmd/rpc/third/deploy.Dockerfile b/cmd/rpc/third/deploy.Dockerfile index 3c5b23120..462798b00 100644 --- a/cmd/rpc/third/deploy.Dockerfile +++ b/cmd/rpc/third/deploy.Dockerfile @@ -1,3 +1,17 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + FROM ubuntu WORKDIR /Open-IM-Server/bin diff --git a/cmd/rpc/third/main.go b/cmd/rpc/third/main.go index 7705852d7..c070e6811 100644 --- a/cmd/rpc/third/main.go +++ b/cmd/rpc/third/main.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( diff --git a/cmd/rpc/user/deploy.Dockerfile b/cmd/rpc/user/deploy.Dockerfile index c0f0956e8..047698788 100644 --- a/cmd/rpc/user/deploy.Dockerfile +++ b/cmd/rpc/user/deploy.Dockerfile @@ -1,3 +1,17 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + FROM ubuntu WORKDIR /Open-IM-Server/bin diff --git a/cmd/rpc/user/main.go b/cmd/rpc/user/main.go index dfebb307d..6d6d9008d 100644 --- a/cmd/rpc/user/main.go +++ b/cmd/rpc/user/main.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( diff --git a/config/config.yaml b/config/config.yaml index 90e8a6913..def91d2b5 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -1,3 +1,17 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + #OpenIM config diff --git a/config/notification.yaml b/config/notification.yaml index 4b0f3a180..c25c58ee3 100644 --- a/config/notification.yaml +++ b/config/notification.yaml @@ -1,3 +1,17 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + groupCreated: isSendMsg: true #是否发送消息,false不发消息为无声的触发同步。true发消息需要触发会话,rpc notification直接发两次,一次消息一次通知, options字段isNotification是否为通知 reliabilityLevel: 1 # 1为online才发送 2为必达 diff --git a/internal/api/auth.go b/internal/api/auth.go index a9cf17707..531b39622 100644 --- a/internal/api/auth.go +++ b/internal/api/auth.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package api import ( diff --git a/internal/api/conversation.go b/internal/api/conversation.go index 767cc8517..7f1732fec 100644 --- a/internal/api/conversation.go +++ b/internal/api/conversation.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package api import ( diff --git a/internal/api/custom_validator.go b/internal/api/custom_validator.go index b434c792e..42e50647f 100644 --- a/internal/api/custom_validator.go +++ b/internal/api/custom_validator.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package api import ( diff --git a/internal/api/friend.go b/internal/api/friend.go index 8fe9033d5..60a08a8e7 100644 --- a/internal/api/friend.go +++ b/internal/api/friend.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package api import ( diff --git a/internal/api/group.go b/internal/api/group.go index c165f8aab..432645caa 100644 --- a/internal/api/group.go +++ b/internal/api/group.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package api import ( diff --git a/internal/api/msg.go b/internal/api/msg.go index 6f0cf7033..a4da4ea7e 100644 --- a/internal/api/msg.go +++ b/internal/api/msg.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package api import ( diff --git a/internal/api/route.go b/internal/api/route.go index 8a786dc6f..b014f66ab 100644 --- a/internal/api/route.go +++ b/internal/api/route.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package api import ( diff --git a/internal/api/statistics.go b/internal/api/statistics.go index 4a85dd5ef..5b5de01d9 100644 --- a/internal/api/statistics.go +++ b/internal/api/statistics.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package api import ( diff --git a/internal/api/third.go b/internal/api/third.go index 559869f1e..79be58a46 100644 --- a/internal/api/third.go +++ b/internal/api/third.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package api import ( diff --git a/internal/api/user.go b/internal/api/user.go index d3b7bae0e..e99e17b00 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package api import ( diff --git a/internal/msggateway/callback.go b/internal/msggateway/callback.go index 79bcdac97..d705e25f0 100644 --- a/internal/msggateway/callback.go +++ b/internal/msggateway/callback.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msggateway import ( diff --git a/internal/msggateway/client.go b/internal/msggateway/client.go index ef92438e6..439a98fee 100644 --- a/internal/msggateway/client.go +++ b/internal/msggateway/client.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msggateway import ( diff --git a/internal/msggateway/compressor.go b/internal/msggateway/compressor.go index a37c74ccd..97a9b1eff 100644 --- a/internal/msggateway/compressor.go +++ b/internal/msggateway/compressor.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msggateway import ( diff --git a/internal/msggateway/constant.go b/internal/msggateway/constant.go index 58ee6e940..043c9d688 100644 --- a/internal/msggateway/constant.go +++ b/internal/msggateway/constant.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msggateway import "time" diff --git a/internal/msggateway/context.go b/internal/msggateway/context.go index 61f4d7970..e5ccc00f4 100644 --- a/internal/msggateway/context.go +++ b/internal/msggateway/context.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msggateway import ( diff --git a/internal/msggateway/encoder.go b/internal/msggateway/encoder.go index 6915766c6..a34a66e66 100644 --- a/internal/msggateway/encoder.go +++ b/internal/msggateway/encoder.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msggateway import ( diff --git a/internal/msggateway/http_error.go b/internal/msggateway/http_error.go index fd00276fb..03121aba3 100644 --- a/internal/msggateway/http_error.go +++ b/internal/msggateway/http_error.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msggateway import "github.com/OpenIMSDK/Open-IM-Server/pkg/apiresp" diff --git a/internal/msggateway/hub_server.go b/internal/msggateway/hub_server.go index 6916f5309..a7d571e20 100644 --- a/internal/msggateway/hub_server.go +++ b/internal/msggateway/hub_server.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msggateway import ( diff --git a/internal/msggateway/init.go b/internal/msggateway/init.go index c21ac02ce..3ec2a56ac 100644 --- a/internal/msggateway/init.go +++ b/internal/msggateway/init.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msggateway import ( diff --git a/internal/msggateway/long_conn.go b/internal/msggateway/long_conn.go index 911e9a4a1..309ec2d8b 100644 --- a/internal/msggateway/long_conn.go +++ b/internal/msggateway/long_conn.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msggateway import ( diff --git a/internal/msggateway/message_handler.go b/internal/msggateway/message_handler.go index 73b22b83d..874101a05 100644 --- a/internal/msggateway/message_handler.go +++ b/internal/msggateway/message_handler.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msggateway import ( diff --git a/internal/msggateway/n_ws_server.go b/internal/msggateway/n_ws_server.go index c1ec5d076..53469fca8 100644 --- a/internal/msggateway/n_ws_server.go +++ b/internal/msggateway/n_ws_server.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msggateway import ( diff --git a/internal/msggateway/options.go b/internal/msggateway/options.go index a54ffe880..cee415f99 100644 --- a/internal/msggateway/options.go +++ b/internal/msggateway/options.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msggateway import "time" diff --git a/internal/msggateway/user_map.go b/internal/msggateway/user_map.go index 46bdb16a4..9e6757731 100644 --- a/internal/msggateway/user_map.go +++ b/internal/msggateway/user_map.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msggateway import ( diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go index 5b82a7d30..946048f0c 100644 --- a/internal/msgtransfer/init.go +++ b/internal/msgtransfer/init.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msgtransfer import ( diff --git a/internal/msgtransfer/modify_msg_handler.go b/internal/msgtransfer/modify_msg_handler.go index e4130addb..38a2f5fce 100644 --- a/internal/msgtransfer/modify_msg_handler.go +++ b/internal/msgtransfer/modify_msg_handler.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msgtransfer import ( diff --git a/internal/msgtransfer/online_history_msg_handler.go b/internal/msgtransfer/online_history_msg_handler.go index ee7c3bd95..574288de0 100644 --- a/internal/msgtransfer/online_history_msg_handler.go +++ b/internal/msgtransfer/online_history_msg_handler.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msgtransfer import ( diff --git a/internal/msgtransfer/online_msg_to_mongo_handler.go b/internal/msgtransfer/online_msg_to_mongo_handler.go index 7a4799fac..a7bfa3c8f 100644 --- a/internal/msgtransfer/online_msg_to_mongo_handler.go +++ b/internal/msgtransfer/online_msg_to_mongo_handler.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msgtransfer import ( diff --git a/internal/push/callback.go b/internal/push/callback.go index 854d27fc2..a3387905e 100644 --- a/internal/push/callback.go +++ b/internal/push/callback.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package push import ( diff --git a/internal/push/consumer_init.go b/internal/push/consumer_init.go index a76de754f..3c07ab284 100644 --- a/internal/push/consumer_init.go +++ b/internal/push/consumer_init.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package push import ( diff --git a/internal/push/offlinepush/fcm/push.go b/internal/push/offlinepush/fcm/push.go index 63e945d98..8b880ae1f 100644 --- a/internal/push/offlinepush/fcm/push.go +++ b/internal/push/offlinepush/fcm/push.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package fcm import ( diff --git a/internal/push/offlinepush/fcm/push_test.go b/internal/push/offlinepush/fcm/push_test.go index 5cef4ef2b..856b75e0b 100644 --- a/internal/push/offlinepush/fcm/push_test.go +++ b/internal/push/offlinepush/fcm/push_test.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package fcm import ( diff --git a/internal/push/offlinepush/getui/body.go b/internal/push/offlinepush/getui/body.go index b7a4bc082..bd0f7b07f 100644 --- a/internal/push/offlinepush/getui/body.go +++ b/internal/push/offlinepush/getui/body.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package getui import ( diff --git a/internal/push/offlinepush/getui/push.go b/internal/push/offlinepush/getui/push.go index 17278a4ff..2681aede6 100644 --- a/internal/push/offlinepush/getui/push.go +++ b/internal/push/offlinepush/getui/push.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package getui import ( diff --git a/internal/push/offlinepush/jpush/body/audience.go b/internal/push/offlinepush/jpush/body/audience.go index 124c1072a..f29930886 100644 --- a/internal/push/offlinepush/jpush/body/audience.go +++ b/internal/push/offlinepush/jpush/body/audience.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package body const ( diff --git a/internal/push/offlinepush/jpush/body/message.go b/internal/push/offlinepush/jpush/body/message.go index 955a0fffb..670cd4c78 100644 --- a/internal/push/offlinepush/jpush/body/message.go +++ b/internal/push/offlinepush/jpush/body/message.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package body type Message struct { diff --git a/internal/push/offlinepush/jpush/body/notification.go b/internal/push/offlinepush/jpush/body/notification.go index 004730906..cec725784 100644 --- a/internal/push/offlinepush/jpush/body/notification.go +++ b/internal/push/offlinepush/jpush/body/notification.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package body import ( diff --git a/internal/push/offlinepush/jpush/body/options.go b/internal/push/offlinepush/jpush/body/options.go index 323637414..2edf80cf0 100644 --- a/internal/push/offlinepush/jpush/body/options.go +++ b/internal/push/offlinepush/jpush/body/options.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package body type Options struct { diff --git a/internal/push/offlinepush/jpush/body/platform.go b/internal/push/offlinepush/jpush/body/platform.go index 68264fea0..eae782c63 100644 --- a/internal/push/offlinepush/jpush/body/platform.go +++ b/internal/push/offlinepush/jpush/body/platform.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package body import ( diff --git a/internal/push/offlinepush/jpush/body/pushobj.go b/internal/push/offlinepush/jpush/body/pushobj.go index e91faf25a..950f93777 100644 --- a/internal/push/offlinepush/jpush/body/pushobj.go +++ b/internal/push/offlinepush/jpush/body/pushobj.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package body type PushObj struct { diff --git a/internal/push/offlinepush/jpush/push.go b/internal/push/offlinepush/jpush/push.go index e22e97df4..64933db50 100644 --- a/internal/push/offlinepush/jpush/push.go +++ b/internal/push/offlinepush/jpush/push.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package jpush import ( diff --git a/internal/push/offlinepush/offlinepush_interface.go b/internal/push/offlinepush/offlinepush_interface.go index 0060b3e30..44dfce2b8 100644 --- a/internal/push/offlinepush/offlinepush_interface.go +++ b/internal/push/offlinepush/offlinepush_interface.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package offlinepush import ( diff --git a/internal/push/push_handler.go b/internal/push/push_handler.go index 2d0694088..86db1c762 100644 --- a/internal/push/push_handler.go +++ b/internal/push/push_handler.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package push import ( diff --git a/internal/push/push_rpc_server.go b/internal/push/push_rpc_server.go index 08edc738e..1b84a1b82 100644 --- a/internal/push/push_rpc_server.go +++ b/internal/push/push_rpc_server.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package push import ( diff --git a/internal/push/push_to_client.go b/internal/push/push_to_client.go index 3cc91d20f..7cc5b1ea4 100644 --- a/internal/push/push_to_client.go +++ b/internal/push/push_to_client.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package push import ( diff --git a/internal/rpc/auth/auth.go b/internal/rpc/auth/auth.go index 2e0b7fe53..9376a5ccc 100644 --- a/internal/rpc/auth/auth.go +++ b/internal/rpc/auth/auth.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package auth import ( diff --git a/internal/rpc/conversation/conversaion.go b/internal/rpc/conversation/conversaion.go index 11859136f..640c09991 100644 --- a/internal/rpc/conversation/conversaion.go +++ b/internal/rpc/conversation/conversaion.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package conversation import ( diff --git a/internal/rpc/friend/black.go b/internal/rpc/friend/black.go index 784e32d20..23aa8efa3 100644 --- a/internal/rpc/friend/black.go +++ b/internal/rpc/friend/black.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package friend import ( diff --git a/internal/rpc/friend/callback.go b/internal/rpc/friend/callback.go index c36e743c5..478988f11 100644 --- a/internal/rpc/friend/callback.go +++ b/internal/rpc/friend/callback.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package friend import ( diff --git a/internal/rpc/friend/friend.go b/internal/rpc/friend/friend.go index da99fd34c..c07b1423c 100644 --- a/internal/rpc/friend/friend.go +++ b/internal/rpc/friend/friend.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package friend import ( diff --git a/internal/rpc/group/cache.go b/internal/rpc/group/cache.go index 0a13a45e5..3c71eb2a9 100644 --- a/internal/rpc/group/cache.go +++ b/internal/rpc/group/cache.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package group import ( diff --git a/internal/rpc/group/callback.go b/internal/rpc/group/callback.go index f61413f30..acab0ea0d 100644 --- a/internal/rpc/group/callback.go +++ b/internal/rpc/group/callback.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package group import ( diff --git a/internal/rpc/group/convert.go b/internal/rpc/group/convert.go index 48e70357b..c0a03aa4e 100644 --- a/internal/rpc/group/convert.go +++ b/internal/rpc/group/convert.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package group import ( diff --git a/internal/rpc/group/db_map.go b/internal/rpc/group/db_map.go index 7eb9ff650..2ab1a75ea 100644 --- a/internal/rpc/group/db_map.go +++ b/internal/rpc/group/db_map.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package group import ( diff --git a/internal/rpc/group/fill.go b/internal/rpc/group/fill.go index 64092eeab..47b536301 100644 --- a/internal/rpc/group/fill.go +++ b/internal/rpc/group/fill.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package group import ( diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index 56bc6cfad..8daf45454 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package group import ( diff --git a/internal/rpc/group/super_group.go b/internal/rpc/group/super_group.go index ab4cbd5ff..2e174f956 100644 --- a/internal/rpc/group/super_group.go +++ b/internal/rpc/group/super_group.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package group import ( diff --git a/internal/rpc/msg/as_read.go b/internal/rpc/msg/as_read.go index c4707c489..91915c647 100644 --- a/internal/rpc/msg/as_read.go +++ b/internal/rpc/msg/as_read.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msg import ( diff --git a/internal/rpc/msg/callback.go b/internal/rpc/msg/callback.go index aac96f75c..2d4988adf 100644 --- a/internal/rpc/msg/callback.go +++ b/internal/rpc/msg/callback.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msg import ( diff --git a/internal/rpc/msg/delete.go b/internal/rpc/msg/delete.go index 97efff41b..d2ead459e 100644 --- a/internal/rpc/msg/delete.go +++ b/internal/rpc/msg/delete.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msg import ( diff --git a/internal/rpc/msg/extend_msg.go b/internal/rpc/msg/extend_msg.go index 7ae56c71e..6f82d980f 100644 --- a/internal/rpc/msg/extend_msg.go +++ b/internal/rpc/msg/extend_msg.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msg import ( diff --git a/internal/rpc/msg/extend_msg_callback.go b/internal/rpc/msg/extend_msg_callback.go index 09fa40735..d510afd0c 100644 --- a/internal/rpc/msg/extend_msg_callback.go +++ b/internal/rpc/msg/extend_msg_callback.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msg import ( diff --git a/internal/rpc/msg/lock.go b/internal/rpc/msg/lock.go index 64744263f..50224c6a3 100644 --- a/internal/rpc/msg/lock.go +++ b/internal/rpc/msg/lock.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msg import ( diff --git a/internal/rpc/msg/message_interceptor.go b/internal/rpc/msg/message_interceptor.go index 702cdc764..49a330d5e 100644 --- a/internal/rpc/msg/message_interceptor.go +++ b/internal/rpc/msg/message_interceptor.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msg import ( diff --git a/internal/rpc/msg/msg_status.go b/internal/rpc/msg/msg_status.go index b867e3065..f9ab0d09c 100644 --- a/internal/rpc/msg/msg_status.go +++ b/internal/rpc/msg/msg_status.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msg import ( diff --git a/internal/rpc/msg/revoke.go b/internal/rpc/msg/revoke.go index 2ea89ee87..c4b64aa46 100644 --- a/internal/rpc/msg/revoke.go +++ b/internal/rpc/msg/revoke.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msg import ( diff --git a/internal/rpc/msg/send.go b/internal/rpc/msg/send.go index c7450fd00..2c27b869d 100644 --- a/internal/rpc/msg/send.go +++ b/internal/rpc/msg/send.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msg import ( diff --git a/internal/rpc/msg/seq.go b/internal/rpc/msg/seq.go index 892d39bfe..ac771d3ee 100644 --- a/internal/rpc/msg/seq.go +++ b/internal/rpc/msg/seq.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msg import ( diff --git a/internal/rpc/msg/server.go b/internal/rpc/msg/server.go index a077adb83..4702eb5f9 100644 --- a/internal/rpc/msg/server.go +++ b/internal/rpc/msg/server.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msg import ( diff --git a/internal/rpc/msg/sync_msg.go b/internal/rpc/msg/sync_msg.go index b9cfed97c..7ac30ff1a 100644 --- a/internal/rpc/msg/sync_msg.go +++ b/internal/rpc/msg/sync_msg.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msg import ( diff --git a/internal/rpc/msg/utils.go b/internal/rpc/msg/utils.go index 199f1535f..10eccbcff 100644 --- a/internal/rpc/msg/utils.go +++ b/internal/rpc/msg/utils.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msg import ( diff --git a/internal/rpc/msg/verify.go b/internal/rpc/msg/verify.go index dd5b3436b..be2945182 100644 --- a/internal/rpc/msg/verify.go +++ b/internal/rpc/msg/verify.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msg import ( diff --git a/internal/rpc/statistics/statistics.go b/internal/rpc/statistics/statistics.go index 12c0dbc25..2f81301a1 100644 --- a/internal/rpc/statistics/statistics.go +++ b/internal/rpc/statistics/statistics.go @@ -1 +1,15 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package statistics diff --git a/internal/rpc/third/s3.go b/internal/rpc/third/s3.go index e0a3a99da..4ff9a3ce8 100644 --- a/internal/rpc/third/s3.go +++ b/internal/rpc/third/s3.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package third import ( diff --git a/internal/rpc/third/third.go b/internal/rpc/third/third.go index 1ac63c245..ebf2c60d1 100644 --- a/internal/rpc/third/third.go +++ b/internal/rpc/third/third.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package third import ( diff --git a/internal/rpc/user/statistics.go b/internal/rpc/user/statistics.go index 7cbbcb82d..7da40bb1e 100644 --- a/internal/rpc/user/statistics.go +++ b/internal/rpc/user/statistics.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package user import ( diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index 5004dc1f6..d2368ceb3 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package user import ( diff --git a/internal/tools/cron_task.go b/internal/tools/cron_task.go index bac433fc5..365c6cbf4 100644 --- a/internal/tools/cron_task.go +++ b/internal/tools/cron_task.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package tools import ( diff --git a/internal/tools/msg.go b/internal/tools/msg.go index 0c58791a0..8389352a8 100644 --- a/internal/tools/msg.go +++ b/internal/tools/msg.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package tools import ( diff --git a/internal/tools/msg_test.go b/internal/tools/msg_test.go index 241748f76..4b1ea0384 100644 --- a/internal/tools/msg_test.go +++ b/internal/tools/msg_test.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package tools import ( diff --git a/pkg/a2r/api2rpc.go b/pkg/a2r/api2rpc.go index 1a064176c..6a1651a3c 100644 --- a/pkg/a2r/api2rpc.go +++ b/pkg/a2r/api2rpc.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package a2r import ( diff --git a/pkg/apiresp/gin.go b/pkg/apiresp/gin.go index b2bfaf4f9..9d5637bfd 100644 --- a/pkg/apiresp/gin.go +++ b/pkg/apiresp/gin.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package apiresp import ( diff --git a/pkg/apiresp/http.go b/pkg/apiresp/http.go index f079fae6a..db1c847cf 100644 --- a/pkg/apiresp/http.go +++ b/pkg/apiresp/http.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package apiresp import ( diff --git a/pkg/apiresp/resp.go b/pkg/apiresp/resp.go index 29dc33996..a7bd271a3 100644 --- a/pkg/apiresp/resp.go +++ b/pkg/apiresp/resp.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package apiresp import ( diff --git a/pkg/apistruct/auth.go b/pkg/apistruct/auth.go index b760dcd08..119cbac77 100644 --- a/pkg/apistruct/auth.go +++ b/pkg/apistruct/auth.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package apistruct type UserRegisterReq struct { diff --git a/pkg/apistruct/aws.go b/pkg/apistruct/aws.go index 0ef2640c5..3518baea0 100644 --- a/pkg/apistruct/aws.go +++ b/pkg/apistruct/aws.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package apistruct type AwsStorageCredentialReq struct { diff --git a/pkg/apistruct/conversation.go b/pkg/apistruct/conversation.go index 1c6ba6e29..c58daa634 100644 --- a/pkg/apistruct/conversation.go +++ b/pkg/apistruct/conversation.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package apistruct type OptResult struct { diff --git a/pkg/apistruct/cos.go b/pkg/apistruct/cos.go index 2695b6bbf..bd2611553 100644 --- a/pkg/apistruct/cos.go +++ b/pkg/apistruct/cos.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package apistruct import sts "github.com/tencentyun/qcloud-cos-sts-sdk/go" diff --git a/pkg/apistruct/friend.go b/pkg/apistruct/friend.go index 14a7438ac..3b1ab919d 100644 --- a/pkg/apistruct/friend.go +++ b/pkg/apistruct/friend.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package apistruct //type ParamsCommFriend struct { diff --git a/pkg/apistruct/group.go b/pkg/apistruct/group.go index 32af09551..45be76cf9 100644 --- a/pkg/apistruct/group.go +++ b/pkg/apistruct/group.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package apistruct import ( diff --git a/pkg/apistruct/manage.go b/pkg/apistruct/manage.go index 832597fd0..455b2fe4c 100644 --- a/pkg/apistruct/manage.go +++ b/pkg/apistruct/manage.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package apistruct import ( diff --git a/pkg/apistruct/msg.go b/pkg/apistruct/msg.go index 5efa917d1..4b776cb48 100644 --- a/pkg/apistruct/msg.go +++ b/pkg/apistruct/msg.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package apistruct import ( diff --git a/pkg/apistruct/oss.go b/pkg/apistruct/oss.go index 55370d50c..4f1e6c00a 100644 --- a/pkg/apistruct/oss.go +++ b/pkg/apistruct/oss.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package apistruct type OSSCredentialReq struct { diff --git a/pkg/apistruct/pagination.go b/pkg/apistruct/pagination.go index 9b64ad8c1..02de39967 100644 --- a/pkg/apistruct/pagination.go +++ b/pkg/apistruct/pagination.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package apistruct type Pagination struct { diff --git a/pkg/apistruct/public.go b/pkg/apistruct/public.go index fc3d3e707..ff3a115ae 100644 --- a/pkg/apistruct/public.go +++ b/pkg/apistruct/public.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package apistruct type ApiUserInfo struct { diff --git a/pkg/apistruct/super_group.go b/pkg/apistruct/super_group.go index 6c8bf9515..92275eabf 100644 --- a/pkg/apistruct/super_group.go +++ b/pkg/apistruct/super_group.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package apistruct type GetJoinedSuperGroupListReq struct { diff --git a/pkg/apistruct/third.go b/pkg/apistruct/third.go index 80e9d4930..76ff89f62 100644 --- a/pkg/apistruct/third.go +++ b/pkg/apistruct/third.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package apistruct import "mime/multipart" diff --git a/pkg/callbackstruct/common.go b/pkg/callbackstruct/common.go index 11ff51deb..4a83bb09f 100644 --- a/pkg/callbackstruct/common.go +++ b/pkg/callbackstruct/common.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package callbackstruct import ( diff --git a/pkg/callbackstruct/friend.go b/pkg/callbackstruct/friend.go index ebb661a18..7e89824e2 100644 --- a/pkg/callbackstruct/friend.go +++ b/pkg/callbackstruct/friend.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package callbackstruct type CallbackBeforeAddFriendReq struct { diff --git a/pkg/callbackstruct/group.go b/pkg/callbackstruct/group.go index 1ae59e658..558ad4d23 100644 --- a/pkg/callbackstruct/group.go +++ b/pkg/callbackstruct/group.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package callbackstruct import ( diff --git a/pkg/callbackstruct/message.go b/pkg/callbackstruct/message.go index 965d91df2..677e70f27 100644 --- a/pkg/callbackstruct/message.go +++ b/pkg/callbackstruct/message.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package callbackstruct import ( diff --git a/pkg/callbackstruct/msg_gateway.go b/pkg/callbackstruct/msg_gateway.go index 48605e791..a70f724ce 100644 --- a/pkg/callbackstruct/msg_gateway.go +++ b/pkg/callbackstruct/msg_gateway.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package callbackstruct type CallbackUserOnlineReq struct { diff --git a/pkg/callbackstruct/push.go b/pkg/callbackstruct/push.go index a1a0f890c..cac37330a 100644 --- a/pkg/callbackstruct/push.go +++ b/pkg/callbackstruct/push.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package callbackstruct import common "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" diff --git a/pkg/checker/check.go b/pkg/checker/check.go index f5913682d..f3f3811a4 100644 --- a/pkg/checker/check.go +++ b/pkg/checker/check.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package checker type Checker interface { diff --git a/pkg/common/cmd/api.go b/pkg/common/cmd/api.go index 22534c39d..4cb5e34fc 100644 --- a/pkg/common/cmd/api.go +++ b/pkg/common/cmd/api.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package cmd import "github.com/spf13/cobra" diff --git a/pkg/common/cmd/cron_task.go b/pkg/common/cmd/cron_task.go index 7795e5e3d..c26cf2e4f 100644 --- a/pkg/common/cmd/cron_task.go +++ b/pkg/common/cmd/cron_task.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package cmd import "github.com/spf13/cobra" diff --git a/pkg/common/cmd/msg_gateway.go b/pkg/common/cmd/msg_gateway.go index deea1c106..f3547d649 100644 --- a/pkg/common/cmd/msg_gateway.go +++ b/pkg/common/cmd/msg_gateway.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package cmd import ( diff --git a/pkg/common/cmd/msg_transfer.go b/pkg/common/cmd/msg_transfer.go index d6d65a1dd..5c9c3d0cb 100644 --- a/pkg/common/cmd/msg_transfer.go +++ b/pkg/common/cmd/msg_transfer.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package cmd import ( diff --git a/pkg/common/cmd/msg_utils.go b/pkg/common/cmd/msg_utils.go index 00b3f7359..c4f202d2b 100644 --- a/pkg/common/cmd/msg_utils.go +++ b/pkg/common/cmd/msg_utils.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package cmd import ( diff --git a/pkg/common/cmd/root.go b/pkg/common/cmd/root.go index ee586e247..4a5744033 100644 --- a/pkg/common/cmd/root.go +++ b/pkg/common/cmd/root.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package cmd import ( diff --git a/pkg/common/cmd/rpc.go b/pkg/common/cmd/rpc.go index be44f7ce0..bfaef5305 100644 --- a/pkg/common/cmd/rpc.go +++ b/pkg/common/cmd/rpc.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package cmd import ( diff --git a/pkg/common/config/config.go b/pkg/common/config/config.go index 40161932b..eef803e12 100644 --- a/pkg/common/config/config.go +++ b/pkg/common/config/config.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package config import ( diff --git a/pkg/common/config/parse.go b/pkg/common/config/parse.go index ebd6c49fc..38f033ab4 100644 --- a/pkg/common/config/parse.go +++ b/pkg/common/config/parse.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package config import ( diff --git a/pkg/common/constant/constant.go b/pkg/common/constant/constant.go index 1d0a98558..55ef31acf 100644 --- a/pkg/common/constant/constant.go +++ b/pkg/common/constant/constant.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package constant const ( diff --git a/pkg/common/constant/limit.go b/pkg/common/constant/limit.go index 57e764eef..fd1551b34 100644 --- a/pkg/common/constant/limit.go +++ b/pkg/common/constant/limit.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package constant const ( diff --git a/pkg/common/constant/platform_id_to_name.go b/pkg/common/constant/platform_id_to_name.go index e8bb129eb..31b1fcdd9 100644 --- a/pkg/common/constant/platform_id_to_name.go +++ b/pkg/common/constant/platform_id_to_name.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package constant // fixme 1<--->IOS 2<--->Android 3<--->Windows diff --git a/pkg/common/convert/black.go b/pkg/common/convert/black.go index cadd822c8..684a40d0d 100644 --- a/pkg/common/convert/black.go +++ b/pkg/common/convert/black.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package convert import ( diff --git a/pkg/common/convert/conversation.go b/pkg/common/convert/conversation.go index 66d1d771c..f00cae58f 100644 --- a/pkg/common/convert/conversation.go +++ b/pkg/common/convert/conversation.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package convert import ( diff --git a/pkg/common/convert/friend.go b/pkg/common/convert/friend.go index b907f9ad0..018aee42f 100644 --- a/pkg/common/convert/friend.go +++ b/pkg/common/convert/friend.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package convert import ( diff --git a/pkg/common/convert/group.go b/pkg/common/convert/group.go index adec0e6f3..e8808d199 100644 --- a/pkg/common/convert/group.go +++ b/pkg/common/convert/group.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package convert import ( diff --git a/pkg/common/convert/msg.go b/pkg/common/convert/msg.go index c8b8a6625..59215bb86 100644 --- a/pkg/common/convert/msg.go +++ b/pkg/common/convert/msg.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package convert import ( diff --git a/pkg/common/convert/user.go b/pkg/common/convert/user.go index 625bfef7d..d8d4c488c 100644 --- a/pkg/common/convert/user.go +++ b/pkg/common/convert/user.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package convert import ( diff --git a/pkg/common/db/cache/black.go b/pkg/common/db/cache/black.go index ca2f2ed9a..d6b82ef33 100644 --- a/pkg/common/db/cache/black.go +++ b/pkg/common/db/cache/black.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package cache import ( diff --git a/pkg/common/db/cache/conversation.go b/pkg/common/db/cache/conversation.go index 1005ee325..c35f0c240 100644 --- a/pkg/common/db/cache/conversation.go +++ b/pkg/common/db/cache/conversation.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package cache import ( diff --git a/pkg/common/db/cache/extend_msg_set.go b/pkg/common/db/cache/extend_msg_set.go index a45bf87f8..d7750e66b 100644 --- a/pkg/common/db/cache/extend_msg_set.go +++ b/pkg/common/db/cache/extend_msg_set.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package cache import ( diff --git a/pkg/common/db/cache/friend.go b/pkg/common/db/cache/friend.go index c816f6d4f..4695987dd 100644 --- a/pkg/common/db/cache/friend.go +++ b/pkg/common/db/cache/friend.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package cache import ( diff --git a/pkg/common/db/cache/group.go b/pkg/common/db/cache/group.go index 11c733d2b..86adc2d0a 100644 --- a/pkg/common/db/cache/group.go +++ b/pkg/common/db/cache/group.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package cache import ( diff --git a/pkg/common/db/cache/init_redis.go b/pkg/common/db/cache/init_redis.go index e4b950ee1..72dfc8caf 100644 --- a/pkg/common/db/cache/init_redis.go +++ b/pkg/common/db/cache/init_redis.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package cache import ( diff --git a/pkg/common/db/cache/meta_cache.go b/pkg/common/db/cache/meta_cache.go index e12f8e93c..86a47f04b 100644 --- a/pkg/common/db/cache/meta_cache.go +++ b/pkg/common/db/cache/meta_cache.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package cache import ( diff --git a/pkg/common/db/cache/msg.go b/pkg/common/db/cache/msg.go index e01836118..5f57103b0 100644 --- a/pkg/common/db/cache/msg.go +++ b/pkg/common/db/cache/msg.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package cache import ( diff --git a/pkg/common/db/cache/user.go b/pkg/common/db/cache/user.go index c8e8b1cc5..455bc9ebe 100644 --- a/pkg/common/db/cache/user.go +++ b/pkg/common/db/cache/user.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package cache import ( diff --git a/pkg/common/db/controller/auth.go b/pkg/common/db/controller/auth.go index 46d2eb55a..454d9707f 100644 --- a/pkg/common/db/controller/auth.go +++ b/pkg/common/db/controller/auth.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package controller import ( diff --git a/pkg/common/db/controller/black.go b/pkg/common/db/controller/black.go index fad4d8acb..13b375787 100644 --- a/pkg/common/db/controller/black.go +++ b/pkg/common/db/controller/black.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package controller import ( diff --git a/pkg/common/db/controller/chatlog.go b/pkg/common/db/controller/chatlog.go index cb161699f..5385fdfec 100644 --- a/pkg/common/db/controller/chatlog.go +++ b/pkg/common/db/controller/chatlog.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package controller import ( diff --git a/pkg/common/db/controller/conversation.go b/pkg/common/db/controller/conversation.go index d5bec4376..8e7b7a350 100644 --- a/pkg/common/db/controller/conversation.go +++ b/pkg/common/db/controller/conversation.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package controller import ( diff --git a/pkg/common/db/controller/extend_msg.go b/pkg/common/db/controller/extend_msg.go index b9395db0e..73b6d9330 100644 --- a/pkg/common/db/controller/extend_msg.go +++ b/pkg/common/db/controller/extend_msg.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package controller import ( diff --git a/pkg/common/db/controller/friend.go b/pkg/common/db/controller/friend.go index 3a98bd8ec..116345219 100644 --- a/pkg/common/db/controller/friend.go +++ b/pkg/common/db/controller/friend.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package controller import ( diff --git a/pkg/common/db/controller/group.go b/pkg/common/db/controller/group.go index 58ee9394b..18b10aca5 100644 --- a/pkg/common/db/controller/group.go +++ b/pkg/common/db/controller/group.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package controller import ( diff --git a/pkg/common/db/controller/msg.go b/pkg/common/db/controller/msg.go index 6ce55deae..435e3e70b 100644 --- a/pkg/common/db/controller/msg.go +++ b/pkg/common/db/controller/msg.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package controller import ( diff --git a/pkg/common/db/controller/msg_test.go b/pkg/common/db/controller/msg_test.go index 6771bdab4..7e50b8489 100644 --- a/pkg/common/db/controller/msg_test.go +++ b/pkg/common/db/controller/msg_test.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package controller import ( diff --git a/pkg/common/db/controller/push.go b/pkg/common/db/controller/push.go index e0e5d4845..c4c761457 100644 --- a/pkg/common/db/controller/push.go +++ b/pkg/common/db/controller/push.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package controller import ( diff --git a/pkg/common/db/controller/storage.go b/pkg/common/db/controller/storage.go index 22b2274f3..ab20e7da8 100644 --- a/pkg/common/db/controller/storage.go +++ b/pkg/common/db/controller/storage.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package controller import "C" diff --git a/pkg/common/db/controller/third.go b/pkg/common/db/controller/third.go index 276084920..906b9b2e4 100644 --- a/pkg/common/db/controller/third.go +++ b/pkg/common/db/controller/third.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package controller import ( diff --git a/pkg/common/db/controller/user.go b/pkg/common/db/controller/user.go index 9a2e01b47..03224b61e 100644 --- a/pkg/common/db/controller/user.go +++ b/pkg/common/db/controller/user.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package controller import ( diff --git a/pkg/common/db/localcache/conversation.go b/pkg/common/db/localcache/conversation.go index 3c188c90e..de9f0401d 100644 --- a/pkg/common/db/localcache/conversation.go +++ b/pkg/common/db/localcache/conversation.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package localcache import ( diff --git a/pkg/common/db/localcache/group.go b/pkg/common/db/localcache/group.go index ea66d122c..d58d0a8eb 100644 --- a/pkg/common/db/localcache/group.go +++ b/pkg/common/db/localcache/group.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package localcache import ( diff --git a/pkg/common/db/localcache/meta_local_cache.go b/pkg/common/db/localcache/meta_local_cache.go index e0c30e523..ed9389c27 100644 --- a/pkg/common/db/localcache/meta_local_cache.go +++ b/pkg/common/db/localcache/meta_local_cache.go @@ -1 +1,15 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package localcache diff --git a/pkg/common/db/obj/minio.go b/pkg/common/db/obj/minio.go index 2b39fff8b..ffd8684c9 100644 --- a/pkg/common/db/obj/minio.go +++ b/pkg/common/db/obj/minio.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package obj import ( diff --git a/pkg/common/db/obj/obj.go b/pkg/common/db/obj/obj.go index 00c8b86b1..d8f5d9390 100644 --- a/pkg/common/db/obj/obj.go +++ b/pkg/common/db/obj/obj.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package obj import ( diff --git a/pkg/common/db/ormutil/utils.go b/pkg/common/db/ormutil/utils.go index c92b091ef..7e4bd4f71 100644 --- a/pkg/common/db/ormutil/utils.go +++ b/pkg/common/db/ormutil/utils.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package ormutil import ( diff --git a/pkg/common/db/relation/black_model.go b/pkg/common/db/relation/black_model.go index 736d9ed01..ca90f43a7 100644 --- a/pkg/common/db/relation/black_model.go +++ b/pkg/common/db/relation/black_model.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/relation/chat_log_model.go b/pkg/common/db/relation/chat_log_model.go index ef6dd2a8a..c4068e3f7 100644 --- a/pkg/common/db/relation/chat_log_model.go +++ b/pkg/common/db/relation/chat_log_model.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/relation/conversation_model.go b/pkg/common/db/relation/conversation_model.go index cd67ad165..47be971c0 100644 --- a/pkg/common/db/relation/conversation_model.go +++ b/pkg/common/db/relation/conversation_model.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/relation/friend_model.go b/pkg/common/db/relation/friend_model.go index 3baaa50cc..5a54f9b7c 100644 --- a/pkg/common/db/relation/friend_model.go +++ b/pkg/common/db/relation/friend_model.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/relation/friend_request_model.go b/pkg/common/db/relation/friend_request_model.go index 1673d6dcf..06fd08ff6 100644 --- a/pkg/common/db/relation/friend_request_model.go +++ b/pkg/common/db/relation/friend_request_model.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/relation/group_member_model.go b/pkg/common/db/relation/group_member_model.go index 78ab4357c..8f5aa937d 100644 --- a/pkg/common/db/relation/group_member_model.go +++ b/pkg/common/db/relation/group_member_model.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/relation/group_model.go b/pkg/common/db/relation/group_model.go index fbcb5763b..8e3431c83 100644 --- a/pkg/common/db/relation/group_model.go +++ b/pkg/common/db/relation/group_model.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/relation/group_request_model.go b/pkg/common/db/relation/group_request_model.go index 9d12005ae..0220f308e 100644 --- a/pkg/common/db/relation/group_request_model.go +++ b/pkg/common/db/relation/group_request_model.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/relation/meta_db.go b/pkg/common/db/relation/meta_db.go index 2c937f0cb..60e4a5464 100644 --- a/pkg/common/db/relation/meta_db.go +++ b/pkg/common/db/relation/meta_db.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/relation/mysql_init.go b/pkg/common/db/relation/mysql_init.go index 89ee8545e..67419c7bf 100644 --- a/pkg/common/db/relation/mysql_init.go +++ b/pkg/common/db/relation/mysql_init.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/relation/object_hash_model.go b/pkg/common/db/relation/object_hash_model.go index 789929c5f..5ff8cd47a 100644 --- a/pkg/common/db/relation/object_hash_model.go +++ b/pkg/common/db/relation/object_hash_model.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/relation/object_info_model.go b/pkg/common/db/relation/object_info_model.go index aa48ae491..642ae8ef2 100644 --- a/pkg/common/db/relation/object_info_model.go +++ b/pkg/common/db/relation/object_info_model.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/relation/object_put_model.go b/pkg/common/db/relation/object_put_model.go index 12808c215..d2df2636d 100644 --- a/pkg/common/db/relation/object_put_model.go +++ b/pkg/common/db/relation/object_put_model.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/relation/user_model.go b/pkg/common/db/relation/user_model.go index 013666859..89a1773d4 100644 --- a/pkg/common/db/relation/user_model.go +++ b/pkg/common/db/relation/user_model.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/table/relation/black.go b/pkg/common/db/table/relation/black.go index bcfffc6db..ec7ca7a56 100644 --- a/pkg/common/db/table/relation/black.go +++ b/pkg/common/db/table/relation/black.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/table/relation/chatlog.go b/pkg/common/db/table/relation/chatlog.go index f43d8a43a..c67edb1fe 100644 --- a/pkg/common/db/table/relation/chatlog.go +++ b/pkg/common/db/table/relation/chatlog.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/table/relation/conversation.go b/pkg/common/db/table/relation/conversation.go index f08ea90e3..5d6d061b1 100644 --- a/pkg/common/db/table/relation/conversation.go +++ b/pkg/common/db/table/relation/conversation.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import "context" diff --git a/pkg/common/db/table/relation/friend.go b/pkg/common/db/table/relation/friend.go index 43724141f..58d8d1d34 100644 --- a/pkg/common/db/table/relation/friend.go +++ b/pkg/common/db/table/relation/friend.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/table/relation/friend_request.go b/pkg/common/db/table/relation/friend_request.go index 59160c819..794f33aaf 100644 --- a/pkg/common/db/table/relation/friend_request.go +++ b/pkg/common/db/table/relation/friend_request.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/table/relation/group.go b/pkg/common/db/table/relation/group.go index 70ccad9b4..be0afbcc3 100644 --- a/pkg/common/db/table/relation/group.go +++ b/pkg/common/db/table/relation/group.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/table/relation/group_member.go b/pkg/common/db/table/relation/group_member.go index 6640a9402..bfde72834 100644 --- a/pkg/common/db/table/relation/group_member.go +++ b/pkg/common/db/table/relation/group_member.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/table/relation/group_request.go b/pkg/common/db/table/relation/group_request.go index 816d3ad35..ba68bcd7b 100644 --- a/pkg/common/db/table/relation/group_request.go +++ b/pkg/common/db/table/relation/group_request.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/table/relation/object_hash.go b/pkg/common/db/table/relation/object_hash.go index 86d038fc4..231f21a81 100644 --- a/pkg/common/db/table/relation/object_hash.go +++ b/pkg/common/db/table/relation/object_hash.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/table/relation/object_info.go b/pkg/common/db/table/relation/object_info.go index 60d8d3c63..e7b7fb57c 100644 --- a/pkg/common/db/table/relation/object_info.go +++ b/pkg/common/db/table/relation/object_info.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/table/relation/object_put.go b/pkg/common/db/table/relation/object_put.go index 62ffe61e7..49b37826c 100644 --- a/pkg/common/db/table/relation/object_put.go +++ b/pkg/common/db/table/relation/object_put.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/table/relation/user.go b/pkg/common/db/table/relation/user.go index 35b52602f..7d2bf0cf5 100644 --- a/pkg/common/db/table/relation/user.go +++ b/pkg/common/db/table/relation/user.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/table/relation/utils.go b/pkg/common/db/table/relation/utils.go index d3bd8d312..c8c59b390 100644 --- a/pkg/common/db/table/relation/utils.go +++ b/pkg/common/db/table/relation/utils.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package relation import ( diff --git a/pkg/common/db/table/unrelation/common.go b/pkg/common/db/table/unrelation/common.go index 9834628a9..bd46ccc2a 100644 --- a/pkg/common/db/table/unrelation/common.go +++ b/pkg/common/db/table/unrelation/common.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package unrelation type CommonUserModel struct { diff --git a/pkg/common/db/table/unrelation/extend_msg_set.go b/pkg/common/db/table/unrelation/extend_msg_set.go index 76588e679..472daf5fa 100644 --- a/pkg/common/db/table/unrelation/extend_msg_set.go +++ b/pkg/common/db/table/unrelation/extend_msg_set.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package unrelation import ( diff --git a/pkg/common/db/table/unrelation/msg.go b/pkg/common/db/table/unrelation/msg.go index 950a60689..57f9b4cca 100644 --- a/pkg/common/db/table/unrelation/msg.go +++ b/pkg/common/db/table/unrelation/msg.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package unrelation import ( diff --git a/pkg/common/db/table/unrelation/super_group.go b/pkg/common/db/table/unrelation/super_group.go index 4875c490d..80c3ef9c7 100644 --- a/pkg/common/db/table/unrelation/super_group.go +++ b/pkg/common/db/table/unrelation/super_group.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package unrelation import ( diff --git a/pkg/common/db/tx/gorm.go b/pkg/common/db/tx/gorm.go index 4d5ccd5be..98e71cb9f 100644 --- a/pkg/common/db/tx/gorm.go +++ b/pkg/common/db/tx/gorm.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package tx import ( diff --git a/pkg/common/db/tx/mongo.go b/pkg/common/db/tx/mongo.go index cd7d24607..a0c38e372 100644 --- a/pkg/common/db/tx/mongo.go +++ b/pkg/common/db/tx/mongo.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package tx import ( diff --git a/pkg/common/db/tx/tx.go b/pkg/common/db/tx/tx.go index e182271ff..9a6fe02ef 100644 --- a/pkg/common/db/tx/tx.go +++ b/pkg/common/db/tx/tx.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package tx import "context" diff --git a/pkg/common/db/unrelation/extend_msg.go b/pkg/common/db/unrelation/extend_msg.go index 2abe1d0bd..17e0b2e19 100644 --- a/pkg/common/db/unrelation/extend_msg.go +++ b/pkg/common/db/unrelation/extend_msg.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package unrelation import ( diff --git a/pkg/common/db/unrelation/mongo.go b/pkg/common/db/unrelation/mongo.go index 8325abff9..51b9e4b7e 100644 --- a/pkg/common/db/unrelation/mongo.go +++ b/pkg/common/db/unrelation/mongo.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package unrelation import ( diff --git a/pkg/common/db/unrelation/msg.go b/pkg/common/db/unrelation/msg.go index 4c48f6eb3..cecbf2301 100644 --- a/pkg/common/db/unrelation/msg.go +++ b/pkg/common/db/unrelation/msg.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package unrelation import ( diff --git a/pkg/common/db/unrelation/super_group.go b/pkg/common/db/unrelation/super_group.go index 3dc9f61bd..f2f867c49 100644 --- a/pkg/common/db/unrelation/super_group.go +++ b/pkg/common/db/unrelation/super_group.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package unrelation import ( diff --git a/pkg/common/kafka/consumer.go b/pkg/common/kafka/consumer.go index 8a21a14d6..67bc3977b 100644 --- a/pkg/common/kafka/consumer.go +++ b/pkg/common/kafka/consumer.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package kafka import ( diff --git a/pkg/common/kafka/producer.go b/pkg/common/kafka/producer.go index 6e5868f24..4c4ebc460 100644 --- a/pkg/common/kafka/producer.go +++ b/pkg/common/kafka/producer.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package kafka import ( diff --git a/pkg/common/log/color.go b/pkg/common/log/color.go index 2cb4fe078..9d1121d61 100644 --- a/pkg/common/log/color.go +++ b/pkg/common/log/color.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package log import ( diff --git a/pkg/common/log/logger.go b/pkg/common/log/logger.go index 221c0ded5..cfbb91bdd 100644 --- a/pkg/common/log/logger.go +++ b/pkg/common/log/logger.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package log import "context" diff --git a/pkg/common/log/sql_logger.go b/pkg/common/log/sql_logger.go index 1785b211c..40b483474 100644 --- a/pkg/common/log/sql_logger.go +++ b/pkg/common/log/sql_logger.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package log import ( diff --git a/pkg/common/log/zap.go b/pkg/common/log/zap.go index a173ac055..9cf3fe144 100644 --- a/pkg/common/log/zap.go +++ b/pkg/common/log/zap.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package log import ( diff --git a/pkg/common/log/zk_logger.go b/pkg/common/log/zk_logger.go index d69077d73..3579e2022 100644 --- a/pkg/common/log/zk_logger.go +++ b/pkg/common/log/zk_logger.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package log import ( diff --git a/pkg/common/mcontext/ctx.go b/pkg/common/mcontext/ctx.go index 5dee1341a..bbca660b9 100644 --- a/pkg/common/mcontext/ctx.go +++ b/pkg/common/mcontext/ctx.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package mcontext import ( diff --git a/pkg/common/mw/check.go b/pkg/common/mw/check.go index 5c8a3ee0c..81ea7e017 100644 --- a/pkg/common/mw/check.go +++ b/pkg/common/mw/check.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package mw import ( diff --git a/pkg/common/mw/check_test.go b/pkg/common/mw/check_test.go index 091942550..b893d7e4e 100644 --- a/pkg/common/mw/check_test.go +++ b/pkg/common/mw/check_test.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package mw import ( diff --git a/pkg/common/mw/gin.go b/pkg/common/mw/gin.go index 42798d060..65f98dca3 100644 --- a/pkg/common/mw/gin.go +++ b/pkg/common/mw/gin.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package mw import ( diff --git a/pkg/common/mw/intercept_chain.go b/pkg/common/mw/intercept_chain.go index 8feebab90..ae5361631 100644 --- a/pkg/common/mw/intercept_chain.go +++ b/pkg/common/mw/intercept_chain.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package mw import ( diff --git a/pkg/common/mw/rpc_client_interceptor.go b/pkg/common/mw/rpc_client_interceptor.go index 530847280..40933810c 100644 --- a/pkg/common/mw/rpc_client_interceptor.go +++ b/pkg/common/mw/rpc_client_interceptor.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package mw import ( diff --git a/pkg/common/mw/rpc_server_interceptor.go b/pkg/common/mw/rpc_server_interceptor.go index 4dcd4e582..01eb77b89 100644 --- a/pkg/common/mw/rpc_server_interceptor.go +++ b/pkg/common/mw/rpc_server_interceptor.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package mw import ( diff --git a/pkg/common/mw/specialerror/error.go b/pkg/common/mw/specialerror/error.go index a29ea505a..b8b8d27ae 100644 --- a/pkg/common/mw/specialerror/error.go +++ b/pkg/common/mw/specialerror/error.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package specialerror import "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" diff --git a/pkg/common/network/ip.go b/pkg/common/network/ip.go index 2d5a5b09c..bf062429d 100644 --- a/pkg/common/network/ip.go +++ b/pkg/common/network/ip.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package network import ( diff --git a/pkg/common/prome/gather.go b/pkg/common/prome/gather.go index 271488966..910df71d9 100644 --- a/pkg/common/prome/gather.go +++ b/pkg/common/prome/gather.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package prome import ( diff --git a/pkg/common/prome/prometheus.go b/pkg/common/prome/prometheus.go index 299f07d43..d66b1df70 100644 --- a/pkg/common/prome/prometheus.go +++ b/pkg/common/prome/prometheus.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package prome import ( diff --git a/pkg/common/tokenverify/jwt_token.go b/pkg/common/tokenverify/jwt_token.go index 9f4088eff..30e9ac9d1 100644 --- a/pkg/common/tokenverify/jwt_token.go +++ b/pkg/common/tokenverify/jwt_token.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package tokenverify import ( diff --git a/pkg/common/tokenverify/jwt_token_test.go b/pkg/common/tokenverify/jwt_token_test.go index b8390b45d..4e8e98c41 100644 --- a/pkg/common/tokenverify/jwt_token_test.go +++ b/pkg/common/tokenverify/jwt_token_test.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package tokenverify import ( diff --git a/pkg/discoveryregistry/discovery_register.go b/pkg/discoveryregistry/discovery_register.go index 01ed88d5b..50e720f97 100644 --- a/pkg/discoveryregistry/discovery_register.go +++ b/pkg/discoveryregistry/discovery_register.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package discoveryregistry import ( diff --git a/pkg/discoveryregistry/zookeeper/conf.go b/pkg/discoveryregistry/zookeeper/conf.go index 55532a370..034c525ba 100644 --- a/pkg/discoveryregistry/zookeeper/conf.go +++ b/pkg/discoveryregistry/zookeeper/conf.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package zookeeper import ( diff --git a/pkg/discoveryregistry/zookeeper/discover.go b/pkg/discoveryregistry/zookeeper/discover.go index 912c001d9..3966d315c 100644 --- a/pkg/discoveryregistry/zookeeper/discover.go +++ b/pkg/discoveryregistry/zookeeper/discover.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package zookeeper import ( diff --git a/pkg/discoveryregistry/zookeeper/register.go b/pkg/discoveryregistry/zookeeper/register.go index ab75f488b..1531980bb 100644 --- a/pkg/discoveryregistry/zookeeper/register.go +++ b/pkg/discoveryregistry/zookeeper/register.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package zookeeper import ( diff --git a/pkg/discoveryregistry/zookeeper/resolver.go b/pkg/discoveryregistry/zookeeper/resolver.go index 74ed04455..e395a8f34 100644 --- a/pkg/discoveryregistry/zookeeper/resolver.go +++ b/pkg/discoveryregistry/zookeeper/resolver.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package zookeeper import ( diff --git a/pkg/discoveryregistry/zookeeper/zk.go b/pkg/discoveryregistry/zookeeper/zk.go index d6287e47b..b9b68c528 100644 --- a/pkg/discoveryregistry/zookeeper/zk.go +++ b/pkg/discoveryregistry/zookeeper/zk.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package zookeeper import ( diff --git a/pkg/errs/code.go b/pkg/errs/code.go index 3104ed729..b153e4e40 100644 --- a/pkg/errs/code.go +++ b/pkg/errs/code.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package errs // UnknownCode 没有解析到code或解析的code=0 diff --git a/pkg/errs/coderr.go b/pkg/errs/coderr.go index 506a0ddcc..af88a6353 100644 --- a/pkg/errs/coderr.go +++ b/pkg/errs/coderr.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package errs import ( diff --git a/pkg/errs/predefine.go b/pkg/errs/predefine.go index 740875c6f..4890a0996 100644 --- a/pkg/errs/predefine.go +++ b/pkg/errs/predefine.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package errs var ( diff --git a/pkg/errs/relation.go b/pkg/errs/relation.go index 5d87ab59e..76578f4bb 100644 --- a/pkg/errs/relation.go +++ b/pkg/errs/relation.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package errs var Relation = &relation{m: make(map[int]map[int]struct{})} diff --git a/pkg/proto/auth/auth.go b/pkg/proto/auth/auth.go index e7189afab..45e935558 100644 --- a/pkg/proto/auth/auth.go +++ b/pkg/proto/auth/auth.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package auth import "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" diff --git a/pkg/proto/auth/auth.proto b/pkg/proto/auth/auth.proto index 6894db8db..29c008cbe 100644 --- a/pkg/proto/auth/auth.proto +++ b/pkg/proto/auth/auth.proto @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + syntax = "proto3"; package OpenIMServer.auth; option go_package = "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/auth"; diff --git a/pkg/proto/conversation/conversation.go b/pkg/proto/conversation/conversation.go index 7b4d8e242..64b467ddb 100644 --- a/pkg/proto/conversation/conversation.go +++ b/pkg/proto/conversation/conversation.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package conversation import "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" diff --git a/pkg/proto/conversation/conversation.proto b/pkg/proto/conversation/conversation.proto index 967109520..b08366af7 100644 --- a/pkg/proto/conversation/conversation.proto +++ b/pkg/proto/conversation/conversation.proto @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + syntax = "proto3"; package OpenIMServer.conversation; import "wrapperspb/wrapperspb.proto"; diff --git a/pkg/proto/errinfo/errinfo.proto b/pkg/proto/errinfo/errinfo.proto index 77182d1d5..11fddec34 100644 --- a/pkg/proto/errinfo/errinfo.proto +++ b/pkg/proto/errinfo/errinfo.proto @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + syntax = "proto3"; package OpenIMServer.protobuf; diff --git a/pkg/proto/friend/friend.go b/pkg/proto/friend/friend.go index 8b9879406..f2ad6a7a4 100644 --- a/pkg/proto/friend/friend.go +++ b/pkg/proto/friend/friend.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package friend import "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" diff --git a/pkg/proto/friend/friend.proto b/pkg/proto/friend/friend.proto index 30b30f752..98a9e81e9 100644 --- a/pkg/proto/friend/friend.proto +++ b/pkg/proto/friend/friend.proto @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + syntax = "proto3"; package OpenIMServer.friend; import "sdkws/sdkws.proto"; diff --git a/pkg/proto/gen.sh b/pkg/proto/gen.sh index 930a636ec..3c06bbfa7 100644 --- a/pkg/proto/gen.sh +++ b/pkg/proto/gen.sh @@ -1,3 +1,17 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + protoc --go_out=plugins=grpc:./auth --go_opt=module=github.com/OpenIMSDK/Open-IM-Server/pkg/proto/auth auth/auth.proto protoc --go_out=plugins=grpc:./conversation --go_opt=module=github.com/OpenIMSDK/Open-IM-Server/pkg/proto/conversation conversation/conversation.proto protoc --go_out=plugins=grpc:./errinfo --go_opt=module=github.com/OpenIMSDK/Open-IM-Server/pkg/proto/errinfo errinfo/errinfo.proto diff --git a/pkg/proto/group/group.go b/pkg/proto/group/group.go index 24e4a47c7..64e7f0f61 100644 --- a/pkg/proto/group/group.go +++ b/pkg/proto/group/group.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package group import "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" diff --git a/pkg/proto/group/group.proto b/pkg/proto/group/group.proto index 432da16db..9d02f58f7 100644 --- a/pkg/proto/group/group.proto +++ b/pkg/proto/group/group.proto @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + syntax = "proto3"; package OpenIMServer.group; import "sdkws/sdkws.proto"; diff --git a/pkg/proto/msg/msg.go b/pkg/proto/msg/msg.go index 38f503321..a221639ee 100644 --- a/pkg/proto/msg/msg.go +++ b/pkg/proto/msg/msg.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msg import "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" diff --git a/pkg/proto/msg/msg.proto b/pkg/proto/msg/msg.proto index fc623e1b6..9fa093edf 100644 --- a/pkg/proto/msg/msg.proto +++ b/pkg/proto/msg/msg.proto @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + syntax = "proto3"; package OpenIMServer.msg; import "sdkws/sdkws.proto"; diff --git a/pkg/proto/msggateway/msggateway.go b/pkg/proto/msggateway/msggateway.go index 3d638d5d8..5e0c85b45 100644 --- a/pkg/proto/msggateway/msggateway.go +++ b/pkg/proto/msggateway/msggateway.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msggateway import "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" diff --git a/pkg/proto/msggateway/msggateway.proto b/pkg/proto/msggateway/msggateway.proto index 2127daeba..0f6c0bd5f 100644 --- a/pkg/proto/msggateway/msggateway.proto +++ b/pkg/proto/msggateway/msggateway.proto @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + syntax = "proto3"; package OpenIMServer.msggateway; import "sdkws/sdkws.proto"; diff --git a/pkg/proto/push/push.go b/pkg/proto/push/push.go index d7c1b5b5a..2f117acda 100644 --- a/pkg/proto/push/push.go +++ b/pkg/proto/push/push.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package push import "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" diff --git a/pkg/proto/push/push.proto b/pkg/proto/push/push.proto index a5c0ad8a7..1f511b225 100644 --- a/pkg/proto/push/push.proto +++ b/pkg/proto/push/push.proto @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + syntax = "proto3"; package OpenIMServer.push; import "sdkws/sdkws.proto"; diff --git a/pkg/proto/sdkws/sdkws.go b/pkg/proto/sdkws/sdkws.go index 094cb823b..1c0888745 100644 --- a/pkg/proto/sdkws/sdkws.go +++ b/pkg/proto/sdkws/sdkws.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package sdkws import ( diff --git a/pkg/proto/sdkws/sdkws.proto b/pkg/proto/sdkws/sdkws.proto index 4db0b1648..4e81502d3 100644 --- a/pkg/proto/sdkws/sdkws.proto +++ b/pkg/proto/sdkws/sdkws.proto @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + syntax = "proto3"; package OpenIMServer.sdkws; import "wrapperspb/wrapperspb.proto"; diff --git a/pkg/proto/statistics/statistics.proto b/pkg/proto/statistics/statistics.proto index 70980edd3..160ad1dcd 100644 --- a/pkg/proto/statistics/statistics.proto +++ b/pkg/proto/statistics/statistics.proto @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + syntax = "proto3"; package OpenIMServer.statistics; option go_package = "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/statistics"; diff --git a/pkg/proto/third/third.go b/pkg/proto/third/third.go index 88ee316bd..9562255fc 100644 --- a/pkg/proto/third/third.go +++ b/pkg/proto/third/third.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package third import "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" diff --git a/pkg/proto/third/third.proto b/pkg/proto/third/third.proto index 7021dfaef..8cdad7562 100644 --- a/pkg/proto/third/third.proto +++ b/pkg/proto/third/third.proto @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + syntax = "proto3"; package OpenIMServer.third; option go_package = "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/third"; diff --git a/pkg/proto/user/user.go b/pkg/proto/user/user.go index 26b0b1b31..09ae39c7e 100644 --- a/pkg/proto/user/user.go +++ b/pkg/proto/user/user.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package user import "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" diff --git a/pkg/proto/user/user.proto b/pkg/proto/user/user.proto index 6cdbf53d9..e63959aff 100644 --- a/pkg/proto/user/user.proto +++ b/pkg/proto/user/user.proto @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + syntax = "proto3"; package OpenIMServer.user; import "sdkws/sdkws.proto"; diff --git a/pkg/proto/wrapperspb/wrapperspb.go b/pkg/proto/wrapperspb/wrapperspb.go index 229d07ede..7579c64f0 100644 --- a/pkg/proto/wrapperspb/wrapperspb.go +++ b/pkg/proto/wrapperspb/wrapperspb.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package wrapperspb import ( diff --git a/pkg/proto/wrapperspb/wrapperspb.proto b/pkg/proto/wrapperspb/wrapperspb.proto index 768b9fe86..ec1bdca20 100644 --- a/pkg/proto/wrapperspb/wrapperspb.proto +++ b/pkg/proto/wrapperspb/wrapperspb.proto @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + syntax = "proto3"; package OpenIMServer.protobuf; diff --git a/pkg/rpcclient/auth.go b/pkg/rpcclient/auth.go index fc435aa24..aa13ed898 100644 --- a/pkg/rpcclient/auth.go +++ b/pkg/rpcclient/auth.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package rpcclient import ( diff --git a/pkg/rpcclient/conversation.go b/pkg/rpcclient/conversation.go index cc781e449..5cab1d689 100644 --- a/pkg/rpcclient/conversation.go +++ b/pkg/rpcclient/conversation.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package rpcclient import ( diff --git a/pkg/rpcclient/friend.go b/pkg/rpcclient/friend.go index 56875dc83..51b63700c 100644 --- a/pkg/rpcclient/friend.go +++ b/pkg/rpcclient/friend.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package rpcclient import ( diff --git a/pkg/rpcclient/group.go b/pkg/rpcclient/group.go index 399be6a77..06333234f 100644 --- a/pkg/rpcclient/group.go +++ b/pkg/rpcclient/group.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package rpcclient import ( diff --git a/pkg/rpcclient/msg.go b/pkg/rpcclient/msg.go index 43f30361a..aabc3768a 100644 --- a/pkg/rpcclient/msg.go +++ b/pkg/rpcclient/msg.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package rpcclient import ( diff --git a/pkg/rpcclient/notification/common.go b/pkg/rpcclient/notification/common.go index ea0174000..09d8b8798 100644 --- a/pkg/rpcclient/notification/common.go +++ b/pkg/rpcclient/notification/common.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package notification type CommonUser interface { diff --git a/pkg/rpcclient/notification/conevrsation.go b/pkg/rpcclient/notification/conevrsation.go index 8fb11ba97..12e851375 100644 --- a/pkg/rpcclient/notification/conevrsation.go +++ b/pkg/rpcclient/notification/conevrsation.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package notification import ( diff --git a/pkg/rpcclient/notification/extend_msg.go b/pkg/rpcclient/notification/extend_msg.go index 1bef5c9b4..85e5b1903 100644 --- a/pkg/rpcclient/notification/extend_msg.go +++ b/pkg/rpcclient/notification/extend_msg.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package notification import ( diff --git a/pkg/rpcclient/notification/friend.go b/pkg/rpcclient/notification/friend.go index 495238b03..e7df2e6cd 100644 --- a/pkg/rpcclient/notification/friend.go +++ b/pkg/rpcclient/notification/friend.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package notification import ( diff --git a/pkg/rpcclient/notification/group.go b/pkg/rpcclient/notification/group.go index f93f0786a..fcc32b092 100644 --- a/pkg/rpcclient/notification/group.go +++ b/pkg/rpcclient/notification/group.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package notification import ( diff --git a/pkg/rpcclient/push.go b/pkg/rpcclient/push.go index 5359c31ee..8e19782bd 100644 --- a/pkg/rpcclient/push.go +++ b/pkg/rpcclient/push.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package rpcclient import ( diff --git a/pkg/rpcclient/third.go b/pkg/rpcclient/third.go index c657c02a1..4f57cbd69 100644 --- a/pkg/rpcclient/third.go +++ b/pkg/rpcclient/third.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package rpcclient import ( diff --git a/pkg/rpcclient/user.go b/pkg/rpcclient/user.go index 383d0cc67..17dec167c 100644 --- a/pkg/rpcclient/user.go +++ b/pkg/rpcclient/user.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package rpcclient import ( diff --git a/pkg/startrpc/start.go b/pkg/startrpc/start.go index de5be74e5..3eeaaa3a2 100644 --- a/pkg/startrpc/start.go +++ b/pkg/startrpc/start.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package startrpc import ( diff --git a/pkg/statistics/statistics.go b/pkg/statistics/statistics.go index 87cba474c..2a6ae01ae 100644 --- a/pkg/statistics/statistics.go +++ b/pkg/statistics/statistics.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package statistics import ( diff --git a/pkg/utils/base64.go b/pkg/utils/base64.go index 3f01c1488..9502dcde8 100644 --- a/pkg/utils/base64.go +++ b/pkg/utils/base64.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package utils import "encoding/base64" diff --git a/pkg/utils/callback.go b/pkg/utils/callback.go index 139d166c0..7eed52409 100644 --- a/pkg/utils/callback.go +++ b/pkg/utils/callback.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package utils import ( diff --git a/pkg/utils/encryption.go b/pkg/utils/encryption.go index a4862de2e..23266d8f7 100644 --- a/pkg/utils/encryption.go +++ b/pkg/utils/encryption.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package utils import ( diff --git a/pkg/utils/file.go b/pkg/utils/file.go index 7aa7719bd..79528661d 100644 --- a/pkg/utils/file.go +++ b/pkg/utils/file.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package utils import ( diff --git a/pkg/utils/get_server_ip.go b/pkg/utils/get_server_ip.go index b8056af9d..aad0bf2cc 100644 --- a/pkg/utils/get_server_ip.go +++ b/pkg/utils/get_server_ip.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package utils import ( diff --git a/pkg/utils/id.go b/pkg/utils/id.go index 509578df7..25e942f36 100644 --- a/pkg/utils/id.go +++ b/pkg/utils/id.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package utils import ( diff --git a/pkg/utils/id_test.go b/pkg/utils/id_test.go index 3c0d5938e..d6ab3e665 100644 --- a/pkg/utils/id_test.go +++ b/pkg/utils/id_test.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package utils import "testing" diff --git a/pkg/utils/image.go b/pkg/utils/image.go index 26060f1de..f93591f9e 100644 --- a/pkg/utils/image.go +++ b/pkg/utils/image.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package utils import ( diff --git a/pkg/utils/image_test.go b/pkg/utils/image_test.go index f28cfc89d..87d78620f 100644 --- a/pkg/utils/image_test.go +++ b/pkg/utils/image_test.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package utils import ( diff --git a/pkg/utils/map.go b/pkg/utils/map.go index 7a5fb2d6b..cd7f3f1de 100644 --- a/pkg/utils/map.go +++ b/pkg/utils/map.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package utils import ( diff --git a/pkg/utils/md5_test.go b/pkg/utils/md5_test.go index 63eec8077..1add67f32 100644 --- a/pkg/utils/md5_test.go +++ b/pkg/utils/md5_test.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package utils import ( diff --git a/pkg/utils/options.go b/pkg/utils/options.go index a3b265e14..0ade70dfe 100644 --- a/pkg/utils/options.go +++ b/pkg/utils/options.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package utils import "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" diff --git a/pkg/utils/page.go b/pkg/utils/page.go index 61942a5d7..ad19decb0 100644 --- a/pkg/utils/page.go +++ b/pkg/utils/page.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package utils import "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" diff --git a/pkg/utils/platform_number_id_to_name_test.go b/pkg/utils/platform_number_id_to_name_test.go index 78569aed4..47c4016ed 100644 --- a/pkg/utils/platform_number_id_to_name_test.go +++ b/pkg/utils/platform_number_id_to_name_test.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package utils import ( diff --git a/pkg/utils/retry/retry.go b/pkg/utils/retry/retry.go index 8877dd0a5..eb2a387cb 100644 --- a/pkg/utils/retry/retry.go +++ b/pkg/utils/retry/retry.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package retry import ( diff --git a/pkg/utils/retry/stratey.go b/pkg/utils/retry/stratey.go index e045684e8..8dca54161 100644 --- a/pkg/utils/retry/stratey.go +++ b/pkg/utils/retry/stratey.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package retry import "time" diff --git a/pkg/utils/splitter/tools.go b/pkg/utils/splitter/tools.go index 40bd7dee2..d36b9c3be 100644 --- a/pkg/utils/splitter/tools.go +++ b/pkg/utils/splitter/tools.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package splitter type SplitResult struct { diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 1138313d2..418e65d34 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package utils import ( diff --git a/pkg/utils/utils_v2.go b/pkg/utils/utils_v2.go index 777308fd7..f4cc44871 100644 --- a/pkg/utils/utils_v2.go +++ b/pkg/utils/utils_v2.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package utils import ( diff --git a/pkg/utils/utils_v2_test.go b/pkg/utils/utils_v2_test.go index f5aeef538..ba55b8941 100644 --- a/pkg/utils/utils_v2_test.go +++ b/pkg/utils/utils_v2_test.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package utils import ( diff --git a/scripts/batch_build_all_service.sh b/scripts/batch_build_all_service.sh index bf780008f..6c74e1957 100755 --- a/scripts/batch_build_all_service.sh +++ b/scripts/batch_build_all_service.sh @@ -1,4 +1,18 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + source ./style_info.cfg source ./path_info.cfg diff --git a/scripts/batch_start_all.sh b/scripts/batch_start_all.sh index eebf96796..e917d7003 100755 --- a/scripts/batch_start_all.sh +++ b/scripts/batch_start_all.sh @@ -1,4 +1,18 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + #fixme This scripts is the total startup scripts #fixme The full name of the shell scripts that needs to be started is placed in the need_to_start_server_shell array diff --git a/scripts/build_all_service.sh b/scripts/build_all_service.sh index 0d9814bec..a6495939d 100755 --- a/scripts/build_all_service.sh +++ b/scripts/build_all_service.sh @@ -1,4 +1,18 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + source ./style_info.cfg source ./path_info.cfg diff --git a/scripts/build_images.sh b/scripts/build_images.sh index ad37e24e4..ad47cdb24 100755 --- a/scripts/build_images.sh +++ b/scripts/build_images.sh @@ -1,4 +1,18 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + image=openim/open_im_server:v1.0.5 rm Open-IM-Server -rf git clone https://github.com/OpenIMSDK/Open-IM-Server.git --recursive diff --git a/scripts/build_push_k8s_images.sh b/scripts/build_push_k8s_images.sh index 432457298..ff9887c56 100755 --- a/scripts/build_push_k8s_images.sh +++ b/scripts/build_push_k8s_images.sh @@ -1,4 +1,18 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + version=errcode repository=${1} if [[ -z ${repository} ]] diff --git a/scripts/check_all.sh b/scripts/check_all.sh index e4feb7f93..16fec32ff 100755 --- a/scripts/check_all.sh +++ b/scripts/check_all.sh @@ -1,4 +1,18 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + source ./style_info.cfg source ./path_info.cfg diff --git a/scripts/coverage.sh b/scripts/coverage.sh index 9fb77b59a..d4605ee3f 100644 --- a/scripts/coverage.sh +++ b/scripts/coverage.sh @@ -1,4 +1,18 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # http://stackoverflow.com/a/21142256/2055281 diff --git a/scripts/docker_check_service.sh b/scripts/docker_check_service.sh index 4e7d52af3..4acbde407 100755 --- a/scripts/docker_check_service.sh +++ b/scripts/docker_check_service.sh @@ -1,4 +1,18 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + echo "docker-compose ps..........................." cd .. diff --git a/scripts/docker_start_all.sh b/scripts/docker_start_all.sh index d4ded7121..8ed198cae 100755 --- a/scripts/docker_start_all.sh +++ b/scripts/docker_start_all.sh @@ -1,4 +1,18 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + #fixme This scripts is the total startup scripts #fixme The full name of the shell scripts that needs to be started is placed in the need_to_start_server_shell array diff --git a/scripts/ensure_tag.sh b/scripts/ensure_tag.sh new file mode 100644 index 000000000..2da3c404f --- /dev/null +++ b/scripts/ensure_tag.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +version="${VERSION}" +if [ "${version}" == "" ];then + version=v`gsemver bump` +fi + +if [ -z "`git tag -l ${version}`" ];then + git tag -a -m "release version ${version}" ${version} +fi diff --git a/scripts/enterprise/check_all.sh b/scripts/enterprise/check_all.sh index 0f6cb0d5c..f439404f1 100755 --- a/scripts/enterprise/check_all.sh +++ b/scripts/enterprise/check_all.sh @@ -1,4 +1,18 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + source ./style_info.cfg source ./enterprise/path_info.cfg diff --git a/scripts/enterprise/function.sh b/scripts/enterprise/function.sh index 97f19187a..e2dc96a25 100755 --- a/scripts/enterprise/function.sh +++ b/scripts/enterprise/function.sh @@ -1,4 +1,18 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + #input:[10023,2323,3434] #output:10023 2323 3434 list_to_string(){ diff --git a/scripts/env_check.sh b/scripts/env_check.sh index f51dca451..c6cfaf4bb 100755 --- a/scripts/env_check.sh +++ b/scripts/env_check.sh @@ -1,4 +1,18 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + source ./style_info.cfg echo -e "check time synchronize.................................." diff --git a/scripts/function.sh b/scripts/function.sh index 97f19187a..e2dc96a25 100755 --- a/scripts/function.sh +++ b/scripts/function.sh @@ -1,4 +1,18 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + #input:[10023,2323,3434] #output:10023 2323 3434 list_to_string(){ diff --git a/scripts/githooks/pre-commit b/scripts/githooks/pre-commit index db9d7cf17..dbd84edef 100644 --- a/scripts/githooks/pre-commit +++ b/scripts/githooks/pre-commit @@ -28,7 +28,6 @@ LC_ALL=C local_branch="$(git rev-parse --abbrev-ref HEAD)" valid_branch_regex="^(main|master|develop)$|(feature|feat|release|hotfix|test|bug|ci|style|)\/[a-z0-9._-]+$|^HEAD$" - YELLOW="\e[93m" GREEN="\e[32m" RED="\e[31m" diff --git a/scripts/init_pwd.sh b/scripts/init_pwd.sh index d4b1caf48..fa8296724 100755 --- a/scripts/init_pwd.sh +++ b/scripts/init_pwd.sh @@ -1,3 +1,17 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + source ../.env diff --git a/scripts/lib/color.sh b/scripts/lib/color.sh index bdbe0e843..52caa78f8 100755 --- a/scripts/lib/color.sh +++ b/scripts/lib/color.sh @@ -1,8 +1,18 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -# Copyright 2020 Lingfei Kong . All rights reserved. -# Use of this source code is governed by a MIT style -# license that can be found in the LICENSE file. #Define color variables #Feature @@ -22,7 +32,7 @@ COLOR_BBLUE='\033[44m';COLOR_BMAGENTA='\033[45m'; COLOR_BCYAN='\033[46m';COLOR_BWHITE='\033[47m'; # Print colors you can use -iam::color::print_color() +openim::color::print_color() { echo echo -e ${bmagenta}--back-color:${normal} diff --git a/scripts/lib/golang.sh b/scripts/lib/golang.sh index f58e08649..1353ec2d2 100755 --- a/scripts/lib/golang.sh +++ b/scripts/lib/golang.sh @@ -1,152 +1,162 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -# Copyright 2020 Lingfei Kong . All rights reserved. -# Use of this source code is governed by a MIT style -# license that can be found in the LICENSE file. # shellcheck disable=SC2034 # Variables sourced in other scripts. # The server platform we are building on. -readonly IAM_SUPPORTED_SERVER_PLATFORMS=( +readonly OPENIM_SUPPORTED_SERVER_PLATFORMS=( linux/amd64 linux/arm64 ) # If we update this we should also update the set of platforms whose standard # library is precompiled for in build/build-image/cross/Dockerfile -readonly IAM_SUPPORTED_CLIENT_PLATFORMS=( +readonly OPENIM_SUPPORTED_CLIENT_PLATFORMS=( linux/amd64 linux/arm64 ) # The set of server targets that we are only building for Linux # If you update this list, please also update build/BUILD. -iam::golang::server_targets() { +openim::golang::server_targets() { local targets=( - iam-apiserver - iam-authz-server - iam-pump - iam-watcher + openim-apiserver + openim-authz-server + openim-pump + openim-watcher ) echo "${targets[@]}" } -IFS=" " read -ra IAM_SERVER_TARGETS <<< "$(iam::golang::server_targets)" -readonly IAM_SERVER_TARGETS -readonly IAM_SERVER_BINARIES=("${IAM_SERVER_TARGETS[@]##*/}") +IFS=" " read -ra OPENIM_SERVER_TARGETS <<< "$(openim::golang::server_targets)" +readonly OPENIM_SERVER_TARGETS +readonly OPENIM_SERVER_BINARIES=("${OPENIM_SERVER_TARGETS[@]##*/}") # The set of server targets we build docker images for -iam::golang::server_image_targets() { - # NOTE: this contains cmd targets for iam::build::get_docker_wrapped_binaries +openim::golang::server_image_targets() { + # NOTE: this contains cmd targets for openim::build::get_docker_wrapped_binaries local targets=( - cmd/iam-apiserver - cmd/iam-authz-server - cmd/iam-pump - cmd/iam-watcher + cmd/openim-apiserver + cmd/openim-authz-server + cmd/openim-pump + cmd/openim-watcher ) echo "${targets[@]}" } -IFS=" " read -ra IAM_SERVER_IMAGE_TARGETS <<< "$(iam::golang::server_image_targets)" -readonly IAM_SERVER_IMAGE_TARGETS -readonly IAM_SERVER_IMAGE_BINARIES=("${IAM_SERVER_IMAGE_TARGETS[@]##*/}") +IFS=" " read -ra OPENIM_SERVER_IMAGE_TARGETS <<< "$(openim::golang::server_image_targets)" +readonly OPENIM_SERVER_IMAGE_TARGETS +readonly OPENIM_SERVER_IMAGE_BINARIES=("${OPENIM_SERVER_IMAGE_TARGETS[@]##*/}") # ------------ # NOTE: All functions that return lists should use newlines. # bash functions can't return arrays, and spaces are tricky, so newline # separators are the preferred pattern. -# To transform a string of newline-separated items to an array, use iam::util::read-array: -# iam::util::read-array FOO < <(iam::golang::dups a b c a) +# To transform a string of newline-separated items to an array, use openim::util::read-array: +# openim::util::read-array FOO < <(openim::golang::dups a b c a) # # ALWAYS remember to quote your subshells. Not doing so will break in # bash 4.3, and potentially cause other issues. # ------------ # Returns a sorted newline-separated list containing only duplicated items. -iam::golang::dups() { +openim::golang::dups() { # We use printf to insert newlines, which are required by sort. printf "%s\n" "$@" | sort | uniq -d } # Returns a sorted newline-separated list with duplicated items removed. -iam::golang::dedup() { +openim::golang::dedup() { # We use printf to insert newlines, which are required by sort. printf "%s\n" "$@" | sort -u } -# Depends on values of user-facing IAM_BUILD_PLATFORMS, IAM_FASTBUILD, -# and IAM_BUILDER_OS. -# Configures IAM_SERVER_PLATFORMS and IAM_CLIENT_PLATFORMS, then sets them +# Depends on values of user-facing OPENIM_BUILD_PLATFORMS, OPENIM_FASTBUILD, +# and OPENIM_BUILDER_OS. +# Configures OPENIM_SERVER_PLATFORMS and OPENIM_CLIENT_PLATFORMS, then sets them # to readonly. # The configured vars will only contain platforms allowed by the -# IAM_SUPPORTED* vars at the top of this file. -declare -a IAM_SERVER_PLATFORMS -declare -a IAM_CLIENT_PLATFORMS -iam::golang::setup_platforms() { - if [[ -n "${IAM_BUILD_PLATFORMS:-}" ]]; then - # IAM_BUILD_PLATFORMS needs to be read into an array before the next +# OPENIM_SUPPORTED* vars at the top of this file. +declare -a OPENIM_SERVER_PLATFORMS +declare -a OPENIM_CLIENT_PLATFORMS +openim::golang::setup_platforms() { + if [[ -n "${OPENIM_BUILD_PLATFORMS:-}" ]]; then + # OPENIM_BUILD_PLATFORMS needs to be read into an array before the next # step, or quoting treats it all as one element. local -a platforms - IFS=" " read -ra platforms <<< "${IAM_BUILD_PLATFORMS}" + IFS=" " read -ra platforms <<< "${OPENIM_BUILD_PLATFORMS}" - # Deduplicate to ensure the intersection trick with iam::golang::dups + # Deduplicate to ensure the intersection trick with openim::golang::dups # is not defeated by duplicates in user input. - iam::util::read-array platforms < <(iam::golang::dedup "${platforms[@]}") + openim::util::read-array platforms < <(openim::golang::dedup "${platforms[@]}") - # Use iam::golang::dups to restrict the builds to the platforms in - # IAM_SUPPORTED_*_PLATFORMS. Items should only appear at most once in each + # Use openim::golang::dups to restrict the builds to the platforms in + # OPENIM_SUPPORTED_*_PLATFORMS. Items should only appear at most once in each # set, so if they appear twice after the merge they are in the intersection. - iam::util::read-array IAM_SERVER_PLATFORMS < <(iam::golang::dups \ + openim::util::read-array OPENIM_SERVER_PLATFORMS < <(openim::golang::dups \ "${platforms[@]}" \ - "${IAM_SUPPORTED_SERVER_PLATFORMS[@]}" \ + "${OPENIM_SUPPORTED_SERVER_PLATFORMS[@]}" \ ) - readonly IAM_SERVER_PLATFORMS + readonly OPENIM_SERVER_PLATFORMS - iam::util::read-array IAM_CLIENT_PLATFORMS < <(iam::golang::dups \ + openim::util::read-array OPENIM_CLIENT_PLATFORMS < <(openim::golang::dups \ "${platforms[@]}" \ - "${IAM_SUPPORTED_CLIENT_PLATFORMS[@]}" \ + "${OPENIM_SUPPORTED_CLIENT_PLATFORMS[@]}" \ ) - readonly IAM_CLIENT_PLATFORMS + readonly OPENIM_CLIENT_PLATFORMS - elif [[ "${IAM_FASTBUILD:-}" == "true" ]]; then - IAM_SERVER_PLATFORMS=(linux/amd64) - readonly IAM_SERVER_PLATFORMS - IAM_CLIENT_PLATFORMS=(linux/amd64) - readonly IAM_CLIENT_PLATFORMS + elif [[ "${OPENIM_FASTBUILD:-}" == "true" ]]; then + OPENIM_SERVER_PLATFORMS=(linux/amd64) + readonly OPENIM_SERVER_PLATFORMS + OPENIM_CLIENT_PLATFORMS=(linux/amd64) + readonly OPENIM_CLIENT_PLATFORMS else - IAM_SERVER_PLATFORMS=("${IAM_SUPPORTED_SERVER_PLATFORMS[@]}") - readonly IAM_SERVER_PLATFORMS + OPENIM_SERVER_PLATFORMS=("${OPENIM_SUPPORTED_SERVER_PLATFORMS[@]}") + readonly OPENIM_SERVER_PLATFORMS - IAM_CLIENT_PLATFORMS=("${IAM_SUPPORTED_CLIENT_PLATFORMS[@]}") - readonly IAM_CLIENT_PLATFORMS + OPENIM_CLIENT_PLATFORMS=("${OPENIM_SUPPORTED_CLIENT_PLATFORMS[@]}") + readonly OPENIM_CLIENT_PLATFORMS fi } -iam::golang::setup_platforms +openim::golang::setup_platforms # The set of client targets that we are building for all platforms # If you update this list, please also update build/BUILD. -readonly IAM_CLIENT_TARGETS=( +readonly OPENIM_CLIENT_TARGETS=( iamctl ) -readonly IAM_CLIENT_BINARIES=("${IAM_CLIENT_TARGETS[@]##*/}") +readonly OPENIM_CLIENT_BINARIES=("${OPENIM_CLIENT_TARGETS[@]##*/}") -readonly IAM_ALL_TARGETS=( - "${IAM_SERVER_TARGETS[@]}" - "${IAM_CLIENT_TARGETS[@]}" +readonly OPENIM_ALL_TARGETS=( + "${OPENIM_SERVER_TARGETS[@]}" + "${OPENIM_CLIENT_TARGETS[@]}" ) -readonly IAM_ALL_BINARIES=("${IAM_ALL_TARGETS[@]##*/}") +readonly OPENIM_ALL_BINARIES=("${OPENIM_ALL_TARGETS[@]##*/}") # Asks golang what it thinks the host platform is. The go tool chain does some # slightly different things when the target platform matches the host platform. -iam::golang::host_platform() { +openim::golang::host_platform() { echo "$(go env GOHOSTOS)/$(go env GOHOSTARCH)" } # Ensure the go tool exists and is a viable version. -iam::golang::verify_go_version() { +openim::golang::verify_go_version() { if [[ -z "$(command -v go)" ]]; then - iam::log::usage_from_stdin <. All rights reserved. -# Use of this source code is governed by a MIT style -# license that can be found in the LICENSE file. set -o errexit set +o nounset @@ -16,13 +26,13 @@ unset CDPATH export GO111MODULE=on # The root of the build/dist directory -IAM_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd -P)" +OPENIM_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd -P)" -source "${IAM_ROOT}/scripts/lib/util.sh" -source "${IAM_ROOT}/scripts/lib/logging.sh" -source "${IAM_ROOT}/scripts/lib/color.sh" +source "${OPENIM_ROOT}/scripts/lib/util.sh" +source "${OPENIM_ROOT}/scripts/lib/logging.sh" +source "${OPENIM_ROOT}/scripts/lib/color.sh" -iam::log::install_errexit +openim::log::install_errexit -source "${IAM_ROOT}/scripts/lib/version.sh" -source "${IAM_ROOT}/scripts/lib/golang.sh" +source "${OPENIM_ROOT}/scripts/lib/version.sh" +source "${OPENIM_ROOT}/scripts/lib/golang.sh" diff --git a/scripts/lib/logging.sh b/scripts/lib/logging.sh index 39b805c11..092db5513 100755 --- a/scripts/lib/logging.sh +++ b/scripts/lib/logging.sh @@ -1,15 +1,25 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -# Copyright 2020 Lingfei Kong . All rights reserved. -# Use of this source code is governed by a MIT style -# license that can be found in the LICENSE file. # Controls verbosity of the script output and logging. -IAM_VERBOSE="${IAM_VERBOSE:-5}" +OPENIM_VERBOSE="${OPENIM_VERBOSE:-5}" # Handler for when we exit automatically on an error. # Borrowed from https://gist.github.com/ahendrix/7030300 -iam::log::errexit() { +openim::log::errexit() { local err="${PIPESTATUS[*]}" # If the shell we are in doesn't have errexit set (common in subshells) then @@ -21,19 +31,19 @@ iam::log::errexit() { # Print out the stack trace described by $function_stack if [ ${#FUNCNAME[@]} -gt 2 ] then - iam::log::error "Call tree:" + openim::log::error "Call tree:" for ((i=1;i<${#FUNCNAME[@]}-1;i++)) do - iam::log::error " ${i}: ${BASH_SOURCE[${i}+1]}:${BASH_LINENO[${i}]} ${FUNCNAME[${i}]}(...)" + openim::log::error " ${i}: ${BASH_SOURCE[${i}+1]}:${BASH_LINENO[${i}]} ${FUNCNAME[${i}]}(...)" done fi - iam::log::error_exit "Error in ${BASH_SOURCE[1]}:${BASH_LINENO[0]}. '${BASH_COMMAND}' exited with status ${err}" "${1:-1}" 1 + openim::log::error_exit "Error in ${BASH_SOURCE[1]}:${BASH_LINENO[0]}. '${BASH_COMMAND}' exited with status ${err}" "${1:-1}" 1 } -iam::log::install_errexit() { +openim::log::install_errexit() { # trap ERR to provide an error handler whenever a command exits nonzero this # is a more verbose version of set -o errexit - trap 'iam::log::errexit' ERR + trap 'openim::log::errexit' ERR # setting errtrace allows our ERR trap handler to be propagated to functions, # expansions and subshells @@ -44,7 +54,7 @@ iam::log::install_errexit() { # # Args: # $1 The number of stack frames to skip when printing. -iam::log::stack() { +openim::log::stack() { local stack_skip=${1:-0} stack_skip=$((stack_skip + 1)) if [[ ${#FUNCNAME[@]} -gt ${stack_skip} ]]; then @@ -66,13 +76,13 @@ iam::log::stack() { # $1 Message to log with the error # $2 The error code to return # $3 The number of stack frames to skip when printing. -iam::log::error_exit() { +openim::log::error_exit() { local message="${1:-}" local code="${2:-1}" local stack_skip="${3:-0}" stack_skip=$((stack_skip + 1)) - if [[ ${IAM_VERBOSE} -ge 4 ]]; then + if [[ ${OPENIM_VERBOSE} -ge 4 ]]; then local source_file=${BASH_SOURCE[${stack_skip}]} local source_line=${BASH_LINENO[$((stack_skip - 1))]} echo "!!! Error in ${source_file}:${source_line}" >&2 @@ -80,7 +90,7 @@ iam::log::error_exit() { echo " ${1}" >&2 } - iam::log::stack ${stack_skip} + openim::log::stack ${stack_skip} echo "Exiting with status ${code}" >&2 fi @@ -89,7 +99,7 @@ iam::log::error_exit() { } # Log an error but keep going. Don't dump the stack or exit. -iam::log::error() { +openim::log::error() { timestamp=$(date +"[%m%d %H:%M:%S]") echo "!!! ${timestamp} ${1-}" >&2 shift @@ -99,7 +109,7 @@ iam::log::error() { } # Print an usage message to stderr. The arguments are printed directly. -iam::log::usage() { +openim::log::usage() { echo >&2 local message for message; do @@ -108,19 +118,19 @@ iam::log::usage() { echo >&2 } -iam::log::usage_from_stdin() { +openim::log::usage_from_stdin() { local messages=() while read -r line; do messages+=("${line}") done - iam::log::usage "${messages[@]}" + openim::log::usage "${messages[@]}" } # Print out some info that isn't a top level status line -iam::log::info() { +openim::log::info() { local V="${V:-0}" - if [[ ${IAM_VERBOSE} < ${V} ]]; then + if [[ ${OPENIM_VERBOSE} < ${V} ]]; then return fi @@ -129,26 +139,26 @@ iam::log::info() { done } -# Just like iam::log::info, but no \n, so you can make a progress bar -iam::log::progress() { +# Just like openim::log::info, but no \n, so you can make a progress bar +openim::log::progress() { for message; do echo -e -n "${message}" done } -iam::log::info_from_stdin() { +openim::log::info_from_stdin() { local messages=() while read -r line; do messages+=("${line}") done - iam::log::info "${messages[@]}" + openim::log::info "${messages[@]}" } # Print a status line. Formatted to show up in a stream of output. -iam::log::status() { +openim::log::status() { local V="${V:-0}" - if [[ ${IAM_VERBOSE} < ${V} ]]; then + if [[ ${OPENIM_VERBOSE} < ${V} ]]; then return fi diff --git a/scripts/lib/release.sh b/scripts/lib/release.sh index acc3730ef..1817e4ff2 100755 --- a/scripts/lib/release.sh +++ b/scripts/lib/release.sh @@ -1,22 +1,33 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -# Copyright 2020 Lingfei Kong . All rights reserved. -# Use of this source code is governed by a MIT style -# license that can be found in the LICENSE file. - -# This file creates release artifacts (tar files, container images) that are -# ready to distribute to install or distribute to end users. ############################################################################### # Most of the ::release:: namespace functions have been moved to -# github.com/iam/release. Have a look in that repo and specifically in +# github.com/openim/release. Have a look in that repo and specifically in # lib/releaselib.sh for ::release::-related functionality. ############################################################################### +# example: ./coscli cp/sync -r /home/off-line/docker-off-line/ cos://openim-1306374445/openim/image/amd/off-line/off-line/ -e cos.ap-guangzhou.myqcloud.com +# https://cloud.tencent.com/document/product/436/71763 # Tencent cos configuration -readonly BUCKET="marmotedu-1254073058" +readonly BUCKET="openim-1306374445" readonly REGION="ap-beijing" -readonly COS_RELEASE_DIR="iam-release" +readonly COS_RELEASE_DIR="openim-release" + +# default cos command tool readonly COSTOOL="coscmd" # This is where the final release artifacts are created locally @@ -24,15 +35,15 @@ readonly RELEASE_STAGE="${LOCAL_OUTPUT_ROOT}/release-stage" readonly RELEASE_TARS="${LOCAL_OUTPUT_ROOT}/release-tars" readonly RELEASE_IMAGES="${LOCAL_OUTPUT_ROOT}/release-images" -# IAM github account info -readonly IAM_GITHUB_ORG=marmotedu -readonly IAM_GITHUB_REPO=iam +# OpenIM github account info +readonly OPENIM_GITHUB_ORG=OpenIMSDK +readonly OPENIM_GITHUB_REPO=openim -readonly ARTIFACT=iam.tar.gz +readonly ARTIFACT=openim.tar.gz readonly CHECKSUM=${ARTIFACT}.sha1sum -IAM_BUILD_CONFORMANCE=${IAM_BUILD_CONFORMANCE:-y} -IAM_BUILD_PULL_LATEST_IMAGES=${IAM_BUILD_PULL_LATEST_IMAGES:-y} +OPENIM_BUILD_CONFORMANCE=${OPENIM_BUILD_CONFORMANCE:-y} +OPENIM_BUILD_PULL_LATEST_IMAGES=${OPENIM_BUILD_PULL_LATEST_IMAGES:-y} # Validate a ci version # @@ -50,12 +61,12 @@ IAM_BUILD_PULL_LATEST_IMAGES=${IAM_BUILD_PULL_LATEST_IMAGES:-y} # VERSION_PRERELEASE_REV (e.g. '4') # VERSION_BUILD_INFO (e.g. '.56+abcdef12345678') # VERSION_COMMITS (e.g. '56') -function iam::release::parse_and_validate_ci_version() { +function openim::release::parse_and_validate_ci_version() { # Accept things like "v1.2.3-alpha.4.56+abcdef12345678" or "v1.2.3-beta.4" local -r version_regex="^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)-([a-zA-Z0-9]+)\\.(0|[1-9][0-9]*)(\\.(0|[1-9][0-9]*)\\+[0-9a-f]{7,40})?$" local -r version="${1-}" [[ "${version}" =~ ${version_regex} ]] || { - iam::log::error "Invalid ci version: '${version}', must match regex ${version_regex}" + openim::log::error "Invalid ci version: '${version}', must match regex ${version_regex}" return 1 } @@ -80,73 +91,73 @@ function iam::release::parse_and_validate_ci_version() { # --------------------------------------------------------------------------- # Build final release artifacts -function iam::release::clean_cruft() { +function openim::release::clean_cruft() { # Clean out cruft find "${RELEASE_STAGE}" -name '*~' -exec rm {} \; find "${RELEASE_STAGE}" -name '#*#' -exec rm {} \; find "${RELEASE_STAGE}" -name '.DS*' -exec rm {} \; } -function iam::release::package_tarballs() { +function openim::release::package_tarballs() { # Clean out any old releases rm -rf "${RELEASE_STAGE}" "${RELEASE_TARS}" "${RELEASE_IMAGES}" mkdir -p "${RELEASE_TARS}" - iam::release::package_src_tarball & - iam::release::package_client_tarballs & - iam::release::package_iam_manifests_tarball & - iam::release::package_server_tarballs & - iam::util::wait-for-jobs || { iam::log::error "previous tarball phase failed"; return 1; } - - iam::release::package_final_tarball & # _final depends on some of the previous phases - iam::util::wait-for-jobs || { iam::log::error "previous tarball phase failed"; return 1; } + openim::release::package_src_tarball & + openim::release::package_client_tarballs & + openim::release::package_iam_manifests_tarball & + openim::release::package_server_tarballs & + openim::util::wait-for-jobs || { openim::log::error "previous tarball phase failed"; return 1; } + + openim::release::package_final_tarball & # _final depends on some of the previous phases + openim::util::wait-for-jobs || { openim::log::error "previous tarball phase failed"; return 1; } } -function iam::release::updload_tarballs() { - iam::log::info "upload ${RELEASE_TARS}/* to cos bucket ${BUCKET}." +function openim::release::updload_tarballs() { + openim::log::info "upload ${RELEASE_TARS}/* to cos bucket ${BUCKET}." for file in $(ls ${RELEASE_TARS}/*) do if [ "${COSTOOL}" == "coscli" ];then - coscli cp "${file}" "cos://${BUCKET}/${COS_RELEASE_DIR}/${IAM_GIT_VERSION}/${file##*/}" + coscli cp "${file}" "cos://${BUCKET}/${COS_RELEASE_DIR}/${OPENIM_GIT_VERSION}/${file##*/}" coscli cp "${file}" "cos://${BUCKET}/${COS_RELEASE_DIR}/latest/${file##*/}" else - coscmd upload "${file}" "${COS_RELEASE_DIR}/${IAM_GIT_VERSION}/" + coscmd upload "${file}" "${COS_RELEASE_DIR}/${OPENIM_GIT_VERSION}/" coscmd upload "${file}" "${COS_RELEASE_DIR}/latest/" fi done } # Package the source code we built, for compliance/licensing/audit/yadda. -function iam::release::package_src_tarball() { - local -r src_tarball="${RELEASE_TARS}/iam-src.tar.gz" - iam::log::status "Building tarball: src" - if [[ "${IAM_GIT_TREE_STATE-}" = 'clean' ]]; then +function openim::release::package_src_tarball() { + local -r src_tarball="${RELEASE_TARS}/openim-src.tar.gz" + openim::log::status "Building tarball: src" + if [[ "${OPENIM_GIT_TREE_STATE-}" = 'clean' ]]; then git archive -o "${src_tarball}" HEAD else - find "${IAM_ROOT}" -mindepth 1 -maxdepth 1 \ + find "${OPENIM_ROOT}" -mindepth 1 -maxdepth 1 \ ! \( \ - \( -path "${IAM_ROOT}"/_\* -o \ - -path "${IAM_ROOT}"/.git\* -o \ - -path "${IAM_ROOT}"/.gitignore\* -o \ - -path "${IAM_ROOT}"/.gsemver.yaml\* -o \ - -path "${IAM_ROOT}"/.config\* -o \ - -path "${IAM_ROOT}"/.chglog\* -o \ - -path "${IAM_ROOT}"/.gitlint -o \ - -path "${IAM_ROOT}"/.golangci.yaml -o \ - -path "${IAM_ROOT}"/.goreleaser.yml -o \ - -path "${IAM_ROOT}"/.note.md -o \ - -path "${IAM_ROOT}"/.todo.md \ + \( -path "${OPENIM_ROOT}"/_\* -o \ + -path "${OPENIM_ROOT}"/.git\* -o \ + -path "${OPENIM_ROOT}"/.gitignore\* -o \ + -path "${OPENIM_ROOT}"/.gsemver.yaml\* -o \ + -path "${OPENIM_ROOT}"/.config\* -o \ + -path "${OPENIM_ROOT}"/.chglog\* -o \ + -path "${OPENIM_ROOT}"/.gitlint -o \ + -path "${OPENIM_ROOT}"/.golangci.yaml -o \ + -path "${OPENIM_ROOT}"/.goreleaser.yml -o \ + -path "${OPENIM_ROOT}"/.note.md -o \ + -path "${OPENIM_ROOT}"/.todo.md \ \) -prune \ \) -print0 \ - | "${TAR}" czf "${src_tarball}" --transform "s|${IAM_ROOT#/*}|iam|" --null -T - + | "${TAR}" czf "${src_tarball}" --transform "s|${OPENIM_ROOT#/*}|openim|" --null -T - fi } # Package up all of the server binaries -function iam::release::package_server_tarballs() { +function openim::release::package_server_tarballs() { # Find all of the built client binaries local long_platforms=("${LOCAL_OUTPUT_BINPATH}"/*/*) - if [[ -n ${IAM_BUILD_PLATFORMS-} ]]; then - read -ra long_platforms <<< "${IAM_BUILD_PLATFORMS}" + if [[ -n ${OPENIM_BUILD_PLATFORMS-} ]]; then + read -ra long_platforms <<< "${OPENIM_BUILD_PLATFORMS}" fi for platform_long in "${long_platforms[@]}"; do @@ -154,14 +165,14 @@ function iam::release::package_server_tarballs() { local platform_tag platform=${platform_long##${LOCAL_OUTPUT_BINPATH}/} # Strip LOCAL_OUTPUT_BINPATH platform_tag=${platform/\//-} # Replace a "/" for a "-" - iam::log::status "Starting tarball: server $platform_tag" + openim::log::status "Starting tarball: server $platform_tag" ( - local release_stage="${RELEASE_STAGE}/server/${platform_tag}/iam" + local release_stage="${RELEASE_STAGE}/server/${platform_tag}/openim" rm -rf "${release_stage}" mkdir -p "${release_stage}/server/bin" - local server_bins=("${IAM_SERVER_BINARIES[@]}") + local server_bins=("${OPENIM_SERVER_BINARIES[@]}") # This fancy expression will expand to prepend a path # (${LOCAL_OUTPUT_BINPATH}/${platform}/) to every item in the @@ -169,24 +180,24 @@ function iam::release::package_server_tarballs() { cp "${server_bins[@]/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}" \ "${release_stage}/server/bin/" - iam::release::clean_cruft + openim::release::clean_cruft - local package_name="${RELEASE_TARS}/iam-server-${platform_tag}.tar.gz" - iam::release::create_tarball "${package_name}" "${release_stage}/.." + local package_name="${RELEASE_TARS}/openim-server-${platform_tag}.tar.gz" + openim::release::create_tarball "${package_name}" "${release_stage}/.." ) & done - iam::log::status "Waiting on tarballs" - iam::util::wait-for-jobs || { iam::log::error "server tarball creation failed"; exit 1; } + openim::log::status "Waiting on tarballs" + openim::util::wait-for-jobs || { openim::log::error "server tarball creation failed"; exit 1; } } # Package up all of the cross compiled clients. Over time this should grow into # a full SDK -function iam::release::package_client_tarballs() { +function openim::release::package_client_tarballs() { # Find all of the built client binaries local long_platforms=("${LOCAL_OUTPUT_BINPATH}"/*/*) - if [[ -n ${IAM_BUILD_PLATFORMS-} ]]; then - read -ra long_platforms <<< "${IAM_BUILD_PLATFORMS}" + if [[ -n ${OPENIM_BUILD_PLATFORMS-} ]]; then + read -ra long_platforms <<< "${OPENIM_BUILD_PLATFORMS}" fi for platform_long in "${long_platforms[@]}"; do @@ -194,14 +205,14 @@ function iam::release::package_client_tarballs() { local platform_tag platform=${platform_long##${LOCAL_OUTPUT_BINPATH}/} # Strip LOCAL_OUTPUT_BINPATH platform_tag=${platform/\//-} # Replace a "/" for a "-" - iam::log::status "Starting tarball: client $platform_tag" + openim::log::status "Starting tarball: client $platform_tag" ( - local release_stage="${RELEASE_STAGE}/client/${platform_tag}/iam" + local release_stage="${RELEASE_STAGE}/client/${platform_tag}/openim" rm -rf "${release_stage}" mkdir -p "${release_stage}/client/bin" - local client_bins=("${IAM_CLIENT_BINARIES[@]}") + local client_bins=("${OPENIM_CLIENT_BINARIES[@]}") # This fancy expression will expand to prepend a path # (${LOCAL_OUTPUT_BINPATH}/${platform}/) to every item in the @@ -209,45 +220,45 @@ function iam::release::package_client_tarballs() { cp "${client_bins[@]/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}" \ "${release_stage}/client/bin/" - iam::release::clean_cruft + openim::release::clean_cruft - local package_name="${RELEASE_TARS}/iam-client-${platform_tag}.tar.gz" - iam::release::create_tarball "${package_name}" "${release_stage}/.." + local package_name="${RELEASE_TARS}/openim-client-${platform_tag}.tar.gz" + openim::release::create_tarball "${package_name}" "${release_stage}/.." ) & done - iam::log::status "Waiting on tarballs" - iam::util::wait-for-jobs || { iam::log::error "client tarball creation failed"; exit 1; } + openim::log::status "Waiting on tarballs" + openim::util::wait-for-jobs || { openim::log::error "client tarball creation failed"; exit 1; } } # Package up all of the server binaries in docker images -function iam::release::build_server_images() { +function openim::release::build_server_images() { # Clean out any old images rm -rf "${RELEASE_IMAGES}" local platform - for platform in "${IAM_SERVER_PLATFORMS[@]}"; do + for platform in "${OPENIM_SERVER_PLATFORMS[@]}"; do local platform_tag local arch platform_tag=${platform/\//-} # Replace a "/" for a "-" arch=$(basename "${platform}") - iam::log::status "Building images: $platform_tag" + openim::log::status "Building images: $platform_tag" local release_stage - release_stage="${RELEASE_STAGE}/server/${platform_tag}/iam" + release_stage="${RELEASE_STAGE}/server/${platform_tag}/openim" rm -rf "${release_stage}" mkdir -p "${release_stage}/server/bin" # This fancy expression will expand to prepend a path # (${LOCAL_OUTPUT_BINPATH}/${platform}/) to every item in the - # IAM_SERVER_IMAGE_BINARIES array. - cp "${IAM_SERVER_IMAGE_BINARIES[@]/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}" \ + # OPENIM_SERVER_IMAGE_BINARIES array. + cp "${OPENIM_SERVER_IMAGE_BINARIES[@]/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}" \ "${release_stage}/server/bin/" - iam::release::create_docker_images_for_server "${release_stage}/server/bin" "${arch}" + openim::release::create_docker_images_for_server "${release_stage}/server/bin" "${arch}" done } -function iam::release::md5() { +function openim::release::md5() { if which md5 >/dev/null 2>&1; then md5 -q "$1" else @@ -255,7 +266,7 @@ function iam::release::md5() { fi } -function iam::release::sha1() { +function openim::release::sha1() { if which sha1sum >/dev/null 2>&1; then sha1sum "$1" | awk '{ print $1 }' else @@ -263,12 +274,12 @@ function iam::release::sha1() { fi } -function iam::release::build_conformance_image() { +function openim::release::build_conformance_image() { local -r arch="$1" local -r registry="$2" local -r version="$3" local -r save_dir="${4-}" - iam::log::status "Building conformance image for arch: ${arch}" + openim::log::status "Building conformance image for arch: ${arch}" ARCH="${arch}" REGISTRY="${registry}" VERSION="${version}" \ make -C cluster/images/conformance/ build >/dev/null @@ -277,7 +288,7 @@ function iam::release::build_conformance_image() { if [[ -n "${save_dir}" ]]; then "${DOCKER[@]}" save "${conformance_tag}" > "${save_dir}/conformance-${arch}.tar" fi - iam::log::status "Deleting conformance image ${conformance_tag}" + openim::log::status "Deleting conformance image ${conformance_tag}" "${DOCKER[@]}" rmi "${conformance_tag}" &>/dev/null || true } @@ -285,7 +296,7 @@ function iam::release::build_conformance_image() { # Args: # $1 - binary_dir, the directory to save the tared images to. # $2 - arch, architecture for which we are building docker images. -function iam::release::create_docker_images_for_server() { +function openim::release::create_docker_images_for_server() { # Create a sub-shell so that we don't pollute the outer environment ( local binary_dir @@ -294,27 +305,27 @@ function iam::release::create_docker_images_for_server() { local images_dir binary_dir="$1" arch="$2" - binaries=$(iam::build::get_docker_wrapped_binaries "${arch}") + binaries=$(openim::build::get_docker_wrapped_binaries "${arch}") images_dir="${RELEASE_IMAGES}/${arch}" mkdir -p "${images_dir}" # k8s.gcr.io is the constant tag in the docker archives, this is also the default for config scripts in GKE. - # We can use IAM_DOCKER_REGISTRY to include and extra registry in the docker archive. - # If we use IAM_DOCKER_REGISTRY="k8s.gcr.io", then the extra tag (same) is ignored, see release_docker_image_tag below. + # We can use OPENIM_DOCKER_REGISTRY to include and extra registry in the docker archive. + # If we use OPENIM_DOCKER_REGISTRY="k8s.gcr.io", then the extra tag (same) is ignored, see release_docker_image_tag below. local -r docker_registry="k8s.gcr.io" # Docker tags cannot contain '+' - local docker_tag="${IAM_GIT_VERSION/+/_}" + local docker_tag="${OPENIM_GIT_VERSION/+/_}" if [[ -z "${docker_tag}" ]]; then - iam::log::error "git version information missing; cannot create Docker tag" + openim::log::error "git version information missing; cannot create Docker tag" return 1 fi - # provide `--pull` argument to `docker build` if `IAM_BUILD_PULL_LATEST_IMAGES` + # provide `--pull` argument to `docker build` if `OPENIM_BUILD_PULL_LATEST_IMAGES` # is set to y or Y; otherwise try to build the image without forcefully # pulling the latest base image. local docker_build_opts docker_build_opts= - if [[ "${IAM_BUILD_PULL_LATEST_IMAGES}" =~ [yY] ]]; then + if [[ "${OPENIM_BUILD_PULL_LATEST_IMAGES}" =~ [yY] ]]; then docker_build_opts='--pull' fi @@ -327,12 +338,12 @@ function iam::release::create_docker_images_for_server() { local docker_file_path="${docker_build_path}/Dockerfile" local docker_image_tag="${docker_registry}/${binary_name}-${arch}:${docker_tag}" - iam::log::status "Starting docker build for image: ${binary_name}-${arch}" + openim::log::status "Starting docker build for image: ${binary_name}-${arch}" ( rm -rf "${docker_build_path}" mkdir -p "${docker_build_path}" ln "${binary_file_path}" "${docker_build_path}/${binary_name}" - ln "${IAM_ROOT}/build/nsswitch.conf" "${docker_build_path}/nsswitch.conf" + ln "${OPENIM_ROOT}/build/nsswitch.conf" "${docker_build_path}/nsswitch.conf" chmod 0644 "${docker_build_path}/nsswitch.conf" cat < "${docker_file_path}" FROM ${base_image} @@ -346,9 +357,9 @@ EOF "${DOCKER[@]}" build ${docker_build_opts:+"${docker_build_opts}"} -q -t "${docker_image_tag}" "${docker_build_path}" >/dev/null # If we are building an official/alpha/beta release we want to keep # docker images and tag them appropriately. - local -r release_docker_image_tag="${IAM_DOCKER_REGISTRY-$docker_registry}/${binary_name}-${arch}:${IAM_DOCKER_IMAGE_TAG-$docker_tag}" + local -r release_docker_image_tag="${OPENIM_DOCKER_REGISTRY-$docker_registry}/${binary_name}-${arch}:${OPENIM_DOCKER_IMAGE_TAG-$docker_tag}" if [[ "${release_docker_image_tag}" != "${docker_image_tag}" ]]; then - iam::log::status "Tagging docker image ${docker_image_tag} as ${release_docker_image_tag}" + openim::log::status "Tagging docker image ${docker_image_tag} as ${release_docker_image_tag}" "${DOCKER[@]}" rmi "${release_docker_image_tag}" 2>/dev/null || true "${DOCKER[@]}" tag "${docker_image_tag}" "${release_docker_image_tag}" 2>/dev/null fi @@ -357,121 +368,121 @@ EOF rm -rf "${docker_build_path}" ln "${binary_file_path}.tar" "${images_dir}/" - iam::log::status "Deleting docker image ${docker_image_tag}" + openim::log::status "Deleting docker image ${docker_image_tag}" "${DOCKER[@]}" rmi "${docker_image_tag}" &>/dev/null || true ) & done - if [[ "${IAM_BUILD_CONFORMANCE}" =~ [yY] ]]; then - iam::release::build_conformance_image "${arch}" "${docker_registry}" \ + if [[ "${OPENIM_BUILD_CONFORMANCE}" =~ [yY] ]]; then + openim::release::build_conformance_image "${arch}" "${docker_registry}" \ "${docker_tag}" "${images_dir}" & fi - iam::util::wait-for-jobs || { iam::log::error "previous Docker build failed"; return 1; } - iam::log::status "Docker builds done" + openim::util::wait-for-jobs || { openim::log::error "previous Docker build failed"; return 1; } + openim::log::status "Docker builds done" ) } -# This will pack iam-system manifests files for distros such as COS. -function iam::release::package_iam_manifests_tarball() { - iam::log::status "Building tarball: manifests" +# This will pack openim-system manifests files for distros such as COS. +function openim::release::package_iam_manifests_tarball() { + openim::log::status "Building tarball: manifests" - local src_dir="${IAM_ROOT}/deployments" + local src_dir="${OPENIM_ROOT}/deployments" - local release_stage="${RELEASE_STAGE}/manifests/iam" + local release_stage="${RELEASE_STAGE}/manifests/openim" rm -rf "${release_stage}" local dst_dir="${release_stage}" mkdir -p "${dst_dir}" cp -r ${src_dir}/* "${dst_dir}" - #cp "${src_dir}/iam-apiserver.yaml" "${dst_dir}" - #cp "${src_dir}/iam-authz-server.yaml" "${dst_dir}" - #cp "${src_dir}/iam-pump.yaml" "${dst_dir}" - #cp "${src_dir}/iam-watcher.yaml" "${dst_dir}" - #cp "${IAM_ROOT}/cluster/gce/gci/health-monitor.sh" "${dst_dir}/health-monitor.sh" + #cp "${src_dir}/openim-apiserver.yaml" "${dst_dir}" + #cp "${src_dir}/openim-authz-server.yaml" "${dst_dir}" + #cp "${src_dir}/openim-pump.yaml" "${dst_dir}" + #cp "${src_dir}/openim-watcher.yaml" "${dst_dir}" + #cp "${OPENIM_ROOT}/cluster/gce/gci/health-monitor.sh" "${dst_dir}/health-monitor.sh" - iam::release::clean_cruft + openim::release::clean_cruft - local package_name="${RELEASE_TARS}/iam-manifests.tar.gz" - iam::release::create_tarball "${package_name}" "${release_stage}/.." + local package_name="${RELEASE_TARS}/openim-manifests.tar.gz" + openim::release::create_tarball "${package_name}" "${release_stage}/.." } -# This is all the platform-independent stuff you need to run/install iam. +# This is all the platform-independent stuff you need to run/install openim. # Arch-specific binaries will need to be downloaded separately (possibly by -# using the bundled cluster/get-iam-binaries.sh script). +# using the bundled cluster/get-openim-binaries.sh script). # Included in this tarball: # - Cluster spin up/down scripts and configs for various cloud providers # - Tarballs for manifest configs that are ready to be uploaded # - Examples (which may or may not still work) # - The remnants of the docs/ directory -function iam::release::package_final_tarball() { - iam::log::status "Building tarball: final" +function openim::release::package_final_tarball() { + openim::log::status "Building tarball: final" # This isn't a "full" tarball anymore, but the release lib still expects - # artifacts under "full/iam/" - local release_stage="${RELEASE_STAGE}/full/iam" + # artifacts under "full/openim/" + local release_stage="${RELEASE_STAGE}/full/openim" rm -rf "${release_stage}" mkdir -p "${release_stage}" mkdir -p "${release_stage}/client" cat < "${release_stage}/client/README" -Client binaries are no longer included in the IAM final tarball. +Client binaries are no longer included in the OpenIM final tarball. -Run release/get-iam-binaries.sh to download client and server binaries. +Run release/get-openim-binaries.sh to download client and server binaries. EOF # We want everything in /scripts. mkdir -p "${release_stage}/release" - cp -R "${IAM_ROOT}/scripts/release" "${release_stage}/" - cat < "${release_stage}/release/get-iam-binaries.sh" + cp -R "${OPENIM_ROOT}/scripts/release" "${release_stage}/" + cat < "${release_stage}/release/get-openim-binaries.sh" #!/usr/bin/env bash # Copyright 2020 Lingfei Kong . All rights reserved. # Use of this source code is governed by a MIT style # license that can be found in the LICENSE file. -# This file download iam client and server binaries from tencent cos bucket. +# This file download openim client and server binaries from tencent cos bucket. -os=linux arch=amd64 version=${IAM_GIT_VERSION} && wget https://${BUCKET}.cos.${REGION}.myqcloud.com/${COS_RELEASE_DIR}/\$version/{iam-client-\$os-\$arch.tar.gz,iam-server-\$os-\$arch.tar.gz} +os=linux arch=amd64 version=${OPENIM_GIT_VERSION} && wget https://${BUCKET}.cos.${REGION}.myqcloud.com/${COS_RELEASE_DIR}/\$version/{openim-client-\$os-\$arch.tar.gz,openim-server-\$os-\$arch.tar.gz} EOF - chmod +x ${release_stage}/release/get-iam-binaries.sh + chmod +x ${release_stage}/release/get-openim-binaries.sh mkdir -p "${release_stage}/server" - cp "${RELEASE_TARS}/iam-manifests.tar.gz" "${release_stage}/server/" + cp "${RELEASE_TARS}/openim-manifests.tar.gz" "${release_stage}/server/" cat < "${release_stage}/server/README" -Server binary tarballs are no longer included in the IAM final tarball. +Server binary tarballs are no longer included in the OpenIM final tarball. -Run release/get-iam-binaries.sh to download client and server binaries. +Run release/get-openim-binaries.sh to download client and server binaries. EOF # Include hack/lib as a dependency for the cluster/ scripts #mkdir -p "${release_stage}/hack" - #cp -R "${IAM_ROOT}/hack/lib" "${release_stage}/hack/" + #cp -R "${OPENIM_ROOT}/hack/lib" "${release_stage}/hack/" - cp -R ${IAM_ROOT}/{docs,configs,scripts,deployments,init,README.md,LICENSE} "${release_stage}/" + cp -R ${OPENIM_ROOT}/{docs,configs,scripts,deployments,init,README.md,LICENSE} "${release_stage}/" - echo "${IAM_GIT_VERSION}" > "${release_stage}/version" + echo "${OPENIM_GIT_VERSION}" > "${release_stage}/version" - iam::release::clean_cruft + openim::release::clean_cruft local package_name="${RELEASE_TARS}/${ARTIFACT}" - iam::release::create_tarball "${package_name}" "${release_stage}/.." + openim::release::create_tarball "${package_name}" "${release_stage}/.." } # Build a release tarball. $1 is the output tar name. $2 is the base directory # of the files to be packaged. This assumes that ${2}/iamis what is # being packaged. -function iam::release::create_tarball() { - iam::build::ensure_tar +function openim::release::create_tarball() { + openim::build::ensure_tar local tarfile=$1 local stagingdir=$2 - "${TAR}" czf "${tarfile}" -C "${stagingdir}" iam --owner=0 --group=0 + "${TAR}" czf "${tarfile}" -C "${stagingdir}" openim --owner=0 --group=0 } -function iam::release::install_github_release(){ +function openim::release::install_github_release(){ GO111MODULE=on go install github.com/github-release/github-release@latest } @@ -480,46 +491,46 @@ function iam::release::install_github_release(){ # - gsemver # - git-chglog # - coscmd or coscli -function iam::release::verify_prereqs(){ +function openim::release::verify_prereqs(){ if [ -z "$(which github-release 2>/dev/null)" ]; then - iam::log::info "'github-release' tool not installed, try to install it." + openim::log::info "'github-release' tool not installed, try to install it." - if ! iam::release::install_github_release; then - iam::log::error "failed to install 'github-release'" + if ! openim::release::install_github_release; then + openim::log::error "failed to install 'github-release'" return 1 fi fi if [ -z "$(which git-chglog 2>/dev/null)" ]; then - iam::log::info "'git-chglog' tool not installed, try to install it." + openim::log::info "'git-chglog' tool not installed, try to install it." if ! go install github.com/git-chglog/git-chglog/cmd/git-chglog@latest &>/dev/null; then - iam::log::error "failed to install 'git-chglog'" + openim::log::error "failed to install 'git-chglog'" return 1 fi fi if [ -z "$(which gsemver 2>/dev/null)" ]; then - iam::log::info "'gsemver' tool not installed, try to install it." + openim::log::info "'gsemver' tool not installed, try to install it." if ! go install github.com/arnaud-deprez/gsemver@latest &>/dev/null; then - iam::log::error "failed to install 'gsemver'" + openim::log::error "failed to install 'gsemver'" return 1 fi fi if [ -z "$(which ${COSTOOL} 2>/dev/null)" ]; then - iam::log::info "${COSTOOL} tool not installed, try to install it." + openim::log::info "${COSTOOL} tool not installed, try to install it." - if ! make -C "${IAM_ROOT}" tools.install.${COSTOOL}; then - iam::log::error "failed to install ${COSTOOL}" + if ! make -C "${OPENIM_ROOT}" tools.install.${COSTOOL}; then + openim::log::error "failed to install ${COSTOOL}" return 1 fi fi if [ -z "${TENCENT_SECRET_ID}" -o -z "${TENCENT_SECRET_KEY}" ];then - iam::log::error "can not find env: TENCENT_SECRET_ID and TENCENT_SECRET_KEY" + openim::log::error "can not find env: TENCENT_SECRET_ID and TENCENT_SECRET_KEY" return 1 fi @@ -556,42 +567,41 @@ EOF # Create a github release with specified tarballs. # NOTICE: Must export 'GITHUB_TOKEN' env in the shell, details: # https://github.com/github-release/github-release -function iam::release::github_release() { +function openim::release::github_release() { # create a github release - iam::log::info "create a new github release with tag ${IAM_GIT_VERSION}" + openim::log::info "create a new github release with tag ${OPENIM_GIT_VERSION}" github-release release \ - --user ${IAM_GITHUB_ORG} \ - --repo ${IAM_GITHUB_REPO} \ - --tag ${IAM_GIT_VERSION} \ + --user ${OPENIM_GITHUB_ORG} \ + --repo ${OPENIM_GITHUB_REPO} \ + --tag ${OPENIM_GIT_VERSION} \ --description "" \ --pre-release - # update iam tarballs - iam::log::info "upload ${ARTIFACT} to release ${IAM_GIT_VERSION}" + # update openim tarballs + openim::log::info "upload ${ARTIFACT} to release ${OPENIM_GIT_VERSION}" github-release upload \ - --user ${IAM_GITHUB_ORG} \ - --repo ${IAM_GITHUB_REPO} \ - --tag ${IAM_GIT_VERSION} \ + --user ${OPENIM_GITHUB_ORG} \ + --repo ${OPENIM_GITHUB_REPO} \ + --tag ${OPENIM_GIT_VERSION} \ --name ${ARTIFACT} \ --file ${RELEASE_TARS}/${ARTIFACT} - iam::log::info "upload iam-src.tar.gz to release ${IAM_GIT_VERSION}" + openim::log::info "upload openim-src.tar.gz to release ${OPENIM_GIT_VERSION}" github-release upload \ - --user ${IAM_GITHUB_ORG} \ - --repo ${IAM_GITHUB_REPO} \ - --tag ${IAM_GIT_VERSION} \ - --name "iam-src.tar.gz" \ - --file ${RELEASE_TARS}/iam-src.tar.gz + --user ${OPENIM_GITHUB_ORG} \ + --repo ${OPENIM_GITHUB_REPO} \ + --tag ${OPENIM_GIT_VERSION} \ + --name "openim-src.tar.gz" \ + --file ${RELEASE_TARS}/openim-src.tar.gz } -function iam::release::generate_changelog() { - iam::log::info "generate CHANGELOG-${IAM_GIT_VERSION#v}.md and commit it" +function openim::release::generate_changelog() { + openim::log::info "generate CHANGELOG-${OPENIM_GIT_VERSION#v}.md and commit it" - git-chglog ${IAM_GIT_VERSION} > ${IAM_ROOT}/CHANGELOG/CHANGELOG-${IAM_GIT_VERSION#v}.md + git-chglog ${OPENIM_GIT_VERSION} > ${OPENIM_ROOT}/CHANGELOG/CHANGELOG-${OPENIM_GIT_VERSION#v}.md set +o errexit - git add ${IAM_ROOT}/CHANGELOG/CHANGELOG-${IAM_GIT_VERSION#v}.md - git commit -a -m "docs(changelog): add CHANGELOG-${IAM_GIT_VERSION#v}.md" + git add ${OPENIM_ROOT}/CHANGELOG/CHANGELOG-${OPENIM_GIT_VERSION#v}.md + git commit -a -m "docs(changelog): add CHANGELOG-${OPENIM_GIT_VERSION#v}.md" git push -f origin master # 最后将 CHANGELOG 也 push 上去 } - diff --git a/scripts/lib/util.sh b/scripts/lib/util.sh index 86c32a69e..2f32dac99 100755 --- a/scripts/lib/util.sh +++ b/scripts/lib/util.sh @@ -1,23 +1,33 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -# Copyright 2020 Lingfei Kong . All rights reserved. -# Use of this source code is governed by a MIT style -# license that can be found in the LICENSE file. -function iam::util::sourced_variable { +function openim::util::sourced_variable { # Call this function to tell shellcheck that a variable is supposed to # be used from other calling context. This helps quiet an "unused # variable" warning from shellcheck and also document your code. true } -iam::util::sortable_date() { +openim::util::sortable_date() { date "+%Y%m%d-%H%M%S" } # arguments: target, item1, item2, item3, ... # returns 0 if target is in the given items, 1 otherwise. -iam::util::array_contains() { +openim::util::array_contains() { local search="$1" local element shift @@ -29,7 +39,7 @@ iam::util::array_contains() { return 1 } -iam::util::wait_for_url() { +openim::util::wait_for_url() { local url=$1 local prefix=${2:-} local wait=${3:-1} @@ -37,7 +47,7 @@ iam::util::wait_for_url() { local maxtime=${5:-1} command -v curl >/dev/null || { - iam::log::usage "curl must be installed" + openim::log::usage "curl must be installed" exit 1 } @@ -45,19 +55,19 @@ iam::util::wait_for_url() { for i in $(seq 1 "${times}"); do local out if out=$(curl --max-time "${maxtime}" -gkfs "${url}" 2>/dev/null); then - iam::log::status "On try ${i}, ${prefix}: ${out}" + openim::log::status "On try ${i}, ${prefix}: ${out}" return 0 fi sleep "${wait}" done - iam::log::error "Timed out waiting for ${prefix} to answer at ${url}; tried ${times} waiting ${wait} between each" + openim::log::error "Timed out waiting for ${prefix} to answer at ${url}; tried ${times} waiting ${wait} between each" return 1 } -# Example: iam::util::wait_for_success 120 5 "iamctl get nodes|grep localhost" +# Example: openim::util::wait_for_success 120 5 "iamctl get nodes|grep localhost" # arguments: wait time, sleep time, shell command # returns 0 if the shell command get output, 1 otherwise. -iam::util::wait_for_success(){ +openim::util::wait_for_success(){ local wait_time="$1" local sleep_time="$2" local cmd="$3" @@ -72,9 +82,9 @@ iam::util::wait_for_success(){ return 1 } -# Example: iam::util::trap_add 'echo "in trap DEBUG"' DEBUG +# Example: openim::util::trap_add 'echo "in trap DEBUG"' DEBUG # See: http://stackoverflow.com/questions/3338030/multiple-bash-traps-for-the-same-signal -iam::util::trap_add() { +openim::util::trap_add() { local trap_add_cmd trap_add_cmd=$1 shift @@ -101,23 +111,23 @@ iam::util::trap_add() { done } -# Opposite of iam::util::ensure-temp-dir() -iam::util::cleanup-temp-dir() { - rm -rf "${IAM_TEMP}" +# Opposite of openim::util::ensure-temp-dir() +openim::util::cleanup-temp-dir() { + rm -rf "${OPENIM_TEMP}" } # Create a temp dir that'll be deleted at the end of this bash session. # # Vars set: -# IAM_TEMP -iam::util::ensure-temp-dir() { - if [[ -z ${IAM_TEMP-} ]]; then - IAM_TEMP=$(mktemp -d 2>/dev/null || mktemp -d -t iamrnetes.XXXXXX) - iam::util::trap_add iam::util::cleanup-temp-dir EXIT +# OPENIM_TEMP +openim::util::ensure-temp-dir() { + if [[ -z ${OPENIM_TEMP-} ]]; then + OPENIM_TEMP=$(mktemp -d 2>/dev/null || mktemp -d -t iamrnetes.XXXXXX) + openim::util::trap_add openim::util::cleanup-temp-dir EXIT fi } -iam::util::host_os() { +openim::util::host_os() { local host_os case "$(uname -s)" in Darwin) @@ -127,14 +137,14 @@ iam::util::host_os() { host_os=linux ;; *) - iam::log::error "Unsupported host OS. Must be Linux or Mac OS X." + openim::log::error "Unsupported host OS. Must be Linux or Mac OS X." exit 1 ;; esac echo "${host_os}" } -iam::util::host_arch() { +openim::util::host_arch() { local host_arch case "$(uname -m)" in x86_64*) @@ -165,7 +175,7 @@ iam::util::host_arch() { host_arch=ppc64le ;; *) - iam::log::error "Unsupported host arch. Must be x86_64, 386, arm, arm64, s390x or ppc64le." + openim::log::error "Unsupported host arch. Must be x86_64, 386, arm, arm64, s390x or ppc64le." exit 1 ;; esac @@ -175,20 +185,20 @@ iam::util::host_arch() { # This figures out the host platform without relying on golang. We need this as # we don't want a golang install to be a prerequisite to building yet we need # this info to figure out where the final binaries are placed. -iam::util::host_platform() { - echo "$(iam::util::host_os)/$(iam::util::host_arch)" +openim::util::host_platform() { + echo "$(openim::util::host_os)/$(openim::util::host_arch)" } # looks for $1 in well-known output locations for the platform ($2) -# $IAM_ROOT must be set -iam::util::find-binary-for-platform() { +# $OPENIM_ROOT must be set +openim::util::find-binary-for-platform() { local -r lookfor="$1" local -r platform="$2" local locations=( - "${IAM_ROOT}/_output/bin/${lookfor}" - "${IAM_ROOT}/_output/${platform}/${lookfor}" - "${IAM_ROOT}/_output/local/bin/${platform}/${lookfor}" - "${IAM_ROOT}/_output/platforms/${platform}/${lookfor}" + "${OPENIM_ROOT}/_output/bin/${lookfor}" + "${OPENIM_ROOT}/_output/${platform}/${lookfor}" + "${OPENIM_ROOT}/_output/local/bin/${platform}/${lookfor}" + "${OPENIM_ROOT}/_output/platforms/${platform}/${lookfor}" ) # List most recently-updated location. @@ -197,42 +207,42 @@ iam::util::find-binary-for-platform() { } # looks for $1 in well-known output locations for the host platform -# $IAM_ROOT must be set -iam::util::find-binary() { - iam::util::find-binary-for-platform "$1" "$(iam::util::host_platform)" +# $OPENIM_ROOT must be set +openim::util::find-binary() { + openim::util::find-binary-for-platform "$1" "$(openim::util::host_platform)" } # Run all known doc generators (today gendocs and genman for iamctl) # $1 is the directory to put those generated documents -iam::util::gen-docs() { +openim::util::gen-docs() { local dest="$1" # Find binary - gendocs=$(iam::util::find-binary "gendocs") - geniamdocs=$(iam::util::find-binary "geniamdocs") - genman=$(iam::util::find-binary "genman") - genyaml=$(iam::util::find-binary "genyaml") - genfeddocs=$(iam::util::find-binary "genfeddocs") + gendocs=$(openim::util::find-binary "gendocs") + geniamdocs=$(openim::util::find-binary "geniamdocs") + genman=$(openim::util::find-binary "genman") + genyaml=$(openim::util::find-binary "genyaml") + genfeddocs=$(openim::util::find-binary "genfeddocs") # TODO: If ${genfeddocs} is not used from anywhere (it isn't used at # least from k/k tree), remove it completely. - iam::util::sourced_variable "${genfeddocs}" + openim::util::sourced_variable "${genfeddocs}" mkdir -p "${dest}/docs/guide/en-US/cmd/iamctl/" "${gendocs}" "${dest}/docs/guide/en-US/cmd/iamctl/" mkdir -p "${dest}/docs/guide/en-US/cmd/" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "iam-apiserver" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "iam-authz-server" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "iam-pump" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "iam-watcher" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-apiserver" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-authz-server" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-pump" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-watcher" "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/iamctl" "iamctl" mkdir -p "${dest}/docs/man/man1/" - "${genman}" "${dest}/docs/man/man1/" "iam-apiserver" - "${genman}" "${dest}/docs/man/man1/" "iam-authz-server" - "${genman}" "${dest}/docs/man/man1/" "iam-pump" - "${genman}" "${dest}/docs/man/man1/" "iam-watcher" + "${genman}" "${dest}/docs/man/man1/" "openim-apiserver" + "${genman}" "${dest}/docs/man/man1/" "openim-authz-server" + "${genman}" "${dest}/docs/man/man1/" "openim-pump" + "${genman}" "${dest}/docs/man/man1/" "openim-watcher" "${genman}" "${dest}/docs/man/man1/" "iamctl" mkdir -p "${dest}/docs/guide/en-US/yaml/iamctl/" @@ -245,14 +255,14 @@ iam::util::gen-docs() { popd > /dev/null || return 1 } -# Removes previously generated docs-- we don't want to check them in. $IAM_ROOT +# Removes previously generated docs-- we don't want to check them in. $OPENIM_ROOT # must be set. -iam::util::remove-gen-docs() { - if [ -e "${IAM_ROOT}/docs/.generated_docs" ]; then +openim::util::remove-gen-docs() { + if [ -e "${OPENIM_ROOT}/docs/.generated_docs" ]; then # remove all of the old docs; we don't want to check them in. while read -r file; do - rm "${IAM_ROOT}/${file}" 2>/dev/null || true - done <"${IAM_ROOT}/docs/.generated_docs" + rm "${OPENIM_ROOT}/${file}" 2>/dev/null || true + done <"${OPENIM_ROOT}/docs/.generated_docs" # The docs/.generated_docs file lists itself, so we don't need to explicitly # delete it. fi @@ -260,15 +270,15 @@ iam::util::remove-gen-docs() { # Returns the name of the upstream remote repository name for the local git # repo, e.g. "upstream" or "origin". -iam::util::git_upstream_remote_name() { +openim::util::git_upstream_remote_name() { git remote -v | grep fetch |\ - grep -E 'github.com[/:]marmotedu/iam|marmotedu.io/iam' |\ + grep -E 'github.com[/:]marmotedu/openim|marmotedu.io/openim' |\ head -n 1 | awk '{print $1}' } # Exits script if working directory is dirty. If it's run interactively in the terminal # the user can commit changes in a second terminal. This script will wait. -iam::util::ensure_clean_working_dir() { +openim::util::ensure_clean_working_dir() { while ! git diff HEAD --exit-code &>/dev/null; do echo -e "\nUnexpected dirty working directory:\n" if tty -s; then @@ -285,7 +295,7 @@ iam::util::ensure_clean_working_dir() { # Find the base commit using: # $PULL_BASE_SHA if set (from Prow) # current ref from the remote upstream branch -iam::util::base_ref() { +openim::util::base_ref() { local -r git_branch=$1 if [[ -n ${PULL_BASE_SHA:-} ]]; then @@ -293,7 +303,7 @@ iam::util::base_ref() { return fi - full_branch="$(iam::util::git_upstream_remote_name)/${git_branch}" + full_branch="$(openim::util::git_upstream_remote_name)/${git_branch}" # make sure the branch is valid, otherwise the check will pass erroneously. if ! git describe "${full_branch}" >/dev/null; then @@ -308,13 +318,13 @@ iam::util::base_ref() { # current branch and upstream branch named by $1. # Returns 1 (false) if there are no changes # 0 (true) if there are changes detected. -iam::util::has_changes() { +openim::util::has_changes() { local -r git_branch=$1 local -r pattern=$2 local -r not_pattern=${3:-totallyimpossiblepattern} local base_ref - base_ref=$(iam::util::base_ref "${git_branch}") + base_ref=$(openim::util::base_ref "${git_branch}") echo "Checking for '${pattern}' changes against '${base_ref}'" # notice this uses ... to find the first shared ancestor @@ -330,7 +340,7 @@ iam::util::has_changes() { return 1 } -iam::util::download_file() { +openim::util::download_file() { local -r url=$1 local -r destination_file=$2 @@ -352,7 +362,7 @@ iam::util::download_file() { # Test whether openssl is installed. # Sets: # OPENSSL_BIN: The path to the openssl binary to use -function iam::util::test_openssl_installed { +function openim::util::test_openssl_installed { if ! openssl version >& /dev/null; then echo "Failed to run openssl. Please ensure openssl is installed" exit 1 @@ -366,7 +376,7 @@ function iam::util::test_openssl_installed { # '"client auth"' # '"server auth"' # '"client auth","server auth"' -function iam::util::create_signing_certkey { +function openim::util::create_signing_certkey { local sudo=$1 local dest_dir=$2 local id=$3 @@ -380,7 +390,7 @@ EOF } # signs a client certificate: args are sudo, dest-dir, CA, filename (roughly), username, groups... -function iam::util::create_client_certkey { +function openim::util::create_client_certkey { local sudo=$1 local dest_dir=$2 local ca=$3 @@ -404,7 +414,7 @@ EOF } # signs a serving certificate: args are sudo, dest-dir, ca, filename (roughly), subject, hosts... -function iam::util::create_serving_certkey { +function openim::util::create_serving_certkey { local sudo=$1 local dest_dir=$2 local ca=$3 @@ -428,7 +438,7 @@ EOF } # creates a self-contained iamconfig: args are sudo, dest-dir, ca file, host, port, client id, token(optional) -function iam::util::write_client_iamconfig { +function openim::util::write_client_iamconfig { local sudo=$1 local dest_dir=$2 local ca_file=$3 @@ -461,14 +471,14 @@ EOF # flatten the iamconfig files to make them self contained username=$(whoami) ${sudo} /usr/bin/env bash -e < "/tmp/${client_id}.iamconfig" + $(openim::util::find-binary iamctl) --iamconfig="${dest_dir}/${client_id}.iamconfig" config view --minify --flatten > "/tmp/${client_id}.iamconfig" mv -f "/tmp/${client_id}.iamconfig" "${dest_dir}/${client_id}.iamconfig" chown ${username} "${dest_dir}/${client_id}.iamconfig" EOF } # Determines if docker can be run, failures may simply require that the user be added to the docker group. -function iam::util::ensure_docker_daemon_connectivity { +function openim::util::ensure_docker_daemon_connectivity { IFS=" " read -ra DOCKER <<< "${DOCKER_OPTS}" # Expand ${DOCKER[@]} only if it's not unset. This is to work around # Bash 3 issue with unbound variable. @@ -497,7 +507,7 @@ EOF # Wait for background jobs to finish. Return with # an error status if any of the jobs failed. -iam::util::wait-for-jobs() { +openim::util::wait-for-jobs() { local fail=0 local job for job in $(jobs -p); do @@ -506,12 +516,12 @@ iam::util::wait-for-jobs() { return ${fail} } -# iam::util::join +# openim::util::join # Concatenates the list elements with the delimiter passed as first parameter # -# Ex: iam::util::join , a b c +# Ex: openim::util::join , a b c # -> a,b,c -function iam::util::join { +function openim::util::join { local IFS="$1" shift echo "$*" @@ -527,7 +537,7 @@ function iam::util::join { # CFSSLJSON_BIN: The path of the installed cfssljson binary # CFSSLCERTINFO_BIN: The path of the installed cfssl-certinfo binary # -function iam::util::ensure-cfssl { +function openim::util::ensure-cfssl { if command -v cfssl &>/dev/null && command -v cfssljson &>/dev/null && command -v cfssl-certinfo &>/dev/null; then CFSSL_BIN=$(command -v cfssl) CFSSLJSON_BIN=$(command -v cfssljson) @@ -535,7 +545,7 @@ function iam::util::ensure-cfssl { return 0 fi - host_arch=$(iam::util::host_arch) + host_arch=$(openim::util::host_arch) if [[ "${host_arch}" != "amd64" ]]; then echo "Cannot download cfssl on non-amd64 hosts and cfssl does not appear to be installed." @@ -588,13 +598,13 @@ function iam::util::ensure-cfssl { popd > /dev/null || return 1 } -# iam::util::ensure-gnu-sed +# openim::util::ensure-gnu-sed # Determines which sed binary is gnu-sed on linux/darwin # # Sets: # SED: The name of the gnu-sed binary # -function iam::util::ensure-gnu-sed { +function openim::util::ensure-gnu-sed { # NOTE: the echo below is a workaround to ensure sed is executed before the grep. # see: https://github.com/iamrnetes/iamrnetes/issues/87251 sed_help="$(LANG=C sed --help 2>&1 || true)" @@ -603,16 +613,16 @@ function iam::util::ensure-gnu-sed { elif command -v gsed &>/dev/null; then SED="gsed" else - iam::log::error "Failed to find GNU sed as sed or gsed. If you are on Mac: brew install gnu-sed." >&2 + openim::log::error "Failed to find GNU sed as sed or gsed. If you are on Mac: brew install gnu-sed." >&2 return 1 fi - iam::util::sourced_variable "${SED}" + openim::util::sourced_variable "${SED}" } -# iam::util::check-file-in-alphabetical-order +# openim::util::check-file-in-alphabetical-order # Check that the file is in alphabetical order # -function iam::util::check-file-in-alphabetical-order { +function openim::util::check-file-in-alphabetical-order { local failure_file="$1" if ! diff -u "${failure_file}" <(LC_ALL=C sort "${failure_file}"); then { @@ -626,9 +636,9 @@ function iam::util::check-file-in-alphabetical-order { fi } -# iam::util::require-jq +# openim::util::require-jq # Checks whether jq is installed. -function iam::util::require-jq { +function openim::util::require-jq { if ! command -v jq &>/dev/null; then echo "jq not found. Please install." 1>&2 return 1 @@ -636,7 +646,7 @@ function iam::util::require-jq { } # outputs md5 hash of $1, works on macOS and Linux -function iam::util::md5() { +function openim::util::md5() { if which md5 >/dev/null 2>&1; then md5 -q "$1" else @@ -644,7 +654,7 @@ function iam::util::md5() { fi } -# iam::util::read-array +# openim::util::read-array # Reads in stdin and adds it line by line to the array provided. This can be # used instead of "mapfile -t", and is bash 3 compatible. # @@ -652,9 +662,9 @@ function iam::util::md5() { # $1 (name of array to create/modify) # # Example usage: -# iam::util::read-array files < <(ls -1) +# openim::util::read-array files < <(ls -1) # -function iam::util::read-array { +function openim::util::read-array { local i=0 unset -v "$1" while IFS= read -r "$1[i++]"; do :; done @@ -671,13 +681,13 @@ if [[ -z "${color_start-}" ]]; then declare -r color_cyan="${color_start}1;36m" declare -r color_norm="${color_start}0m" - iam::util::sourced_variable "${color_start}" - iam::util::sourced_variable "${color_red}" - iam::util::sourced_variable "${color_yellow}" - iam::util::sourced_variable "${color_green}" - iam::util::sourced_variable "${color_blue}" - iam::util::sourced_variable "${color_cyan}" - iam::util::sourced_variable "${color_norm}" + openim::util::sourced_variable "${color_start}" + openim::util::sourced_variable "${color_red}" + openim::util::sourced_variable "${color_yellow}" + openim::util::sourced_variable "${color_green}" + openim::util::sourced_variable "${color_blue}" + openim::util::sourced_variable "${color_cyan}" + openim::util::sourced_variable "${color_norm}" fi # ex: ts=2 sw=2 et filetype=sh diff --git a/scripts/lib/version.sh b/scripts/lib/version.sh index 0edeac114..2a59bf63a 100755 --- a/scripts/lib/version.sh +++ b/scripts/lib/version.sh @@ -1,29 +1,39 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -# Copyright 2020 Lingfei Kong . All rights reserved. -# Use of this source code is governed by a MIT style -# license that can be found in the LICENSE file. # ----------------------------------------------------------------------------- # Version management helpers. These functions help to set, save and load the # following variables: # -# IAM_GIT_COMMIT - The git commit id corresponding to this +# OPENIM_GIT_COMMIT - The git commit id corresponding to this # source code. -# IAM_GIT_TREE_STATE - "clean" indicates no changes since the git commit id +# OPENIM_GIT_TREE_STATE - "clean" indicates no changes since the git commit id # "dirty" indicates source code changes after the git commit id # "archive" indicates the tree was produced by 'git archive' -# IAM_GIT_VERSION - "vX.Y" used to indicate the last release version. -# IAM_GIT_MAJOR - The major part of the version -# IAM_GIT_MINOR - The minor component of the version +# OPENIM_GIT_VERSION - "vX.Y" used to indicate the last release version. +# OPENIM_GIT_MAJOR - The major part of the version +# OPENIM_GIT_MINOR - The minor component of the version # Grovels through git to set a set of env variables. # -# If IAM_GIT_VERSION_FILE, this function will load from that file instead of +# If OPENIM_GIT_VERSION_FILE, this function will load from that file instead of # querying git. -iam::version::get_version_vars() { - if [[ -n ${IAM_GIT_VERSION_FILE-} ]]; then - iam::version::load_version_vars "${IAM_GIT_VERSION_FILE}" +openim::version::get_version_vars() { + if [[ -n ${OPENIM_GIT_VERSION_FILE-} ]]; then + openim::version::load_version_vars "${OPENIM_GIT_VERSION_FILE}" return fi @@ -33,30 +43,30 @@ iam::version::get_version_vars() { # Disabled as we're not expanding these at runtime, but rather expecting # that another tool may have expanded these and rewritten the source (!) if [[ '$Format:%%$' == "%" ]]; then - IAM_GIT_COMMIT='$Format:%H$' - IAM_GIT_TREE_STATE="archive" + OPENIM_GIT_COMMIT='$Format:%H$' + OPENIM_GIT_TREE_STATE="archive" # When a 'git archive' is exported, the '$Format:%D$' below will look # something like 'HEAD -> release-1.8, tag: v1.8.3' where then 'tag: ' # can be extracted from it. if [[ '$Format:%D$' =~ tag:\ (v[^ ,]+) ]]; then - IAM_GIT_VERSION="${BASH_REMATCH[1]}" + OPENIM_GIT_VERSION="${BASH_REMATCH[1]}" fi fi - local git=(git --work-tree "${IAM_ROOT}") + local git=(git --work-tree "${OPENIM_ROOT}") - if [[ -n ${IAM_GIT_COMMIT-} ]] || IAM_GIT_COMMIT=$("${git[@]}" rev-parse "HEAD^{commit}" 2>/dev/null); then - if [[ -z ${IAM_GIT_TREE_STATE-} ]]; then + if [[ -n ${OPENIM_GIT_COMMIT-} ]] || OPENIM_GIT_COMMIT=$("${git[@]}" rev-parse "HEAD^{commit}" 2>/dev/null); then + if [[ -z ${OPENIM_GIT_TREE_STATE-} ]]; then # Check if the tree is dirty. default to dirty if git_status=$("${git[@]}" status --porcelain 2>/dev/null) && [[ -z ${git_status} ]]; then - IAM_GIT_TREE_STATE="clean" + OPENIM_GIT_TREE_STATE="clean" else - IAM_GIT_TREE_STATE="dirty" + OPENIM_GIT_TREE_STATE="dirty" fi fi # Use git describe to find the version based on tags. - if [[ -n ${IAM_GIT_VERSION-} ]] || IAM_GIT_VERSION=$("${git[@]}" describe --tags --always --match='v*' 2>/dev/null); then + if [[ -n ${OPENIM_GIT_VERSION-} ]] || OPENIM_GIT_VERSION=$("${git[@]}" describe --tags --always --match='v*' 2>/dev/null); then # This translates the "git describe" to an actual semver.org # compatible semantic version that looks something like this: # v1.1.0-alpha.0.6+84c76d1142ea4d @@ -67,22 +77,22 @@ iam::version::get_version_vars() { # These regexes are painful enough in sed... # We don't want to do them in pure shell, so disable SC2001 # shellcheck disable=SC2001 - DASHES_IN_VERSION=$(echo "${IAM_GIT_VERSION}" | sed "s/[^-]//g") + DASHES_IN_VERSION=$(echo "${OPENIM_GIT_VERSION}" | sed "s/[^-]//g") if [[ "${DASHES_IN_VERSION}" == "---" ]] ; then # shellcheck disable=SC2001 # We have distance to subversion (v1.1.0-subversion-1-gCommitHash) - IAM_GIT_VERSION=$(echo "${IAM_GIT_VERSION}" | sed "s/-\([0-9]\{1,\}\)-g\([0-9a-f]\{14\}\)$/.\1\+\2/") + OPENIM_GIT_VERSION=$(echo "${OPENIM_GIT_VERSION}" | sed "s/-\([0-9]\{1,\}\)-g\([0-9a-f]\{14\}\)$/.\1\+\2/") elif [[ "${DASHES_IN_VERSION}" == "--" ]] ; then # shellcheck disable=SC2001 # We have distance to base tag (v1.1.0-1-gCommitHash) - IAM_GIT_VERSION=$(echo "${IAM_GIT_VERSION}" | sed "s/-g\([0-9a-f]\{14\}\)$/+\1/") + OPENIM_GIT_VERSION=$(echo "${OPENIM_GIT_VERSION}" | sed "s/-g\([0-9a-f]\{14\}\)$/+\1/") fi - if [[ "${IAM_GIT_TREE_STATE}" == "dirty" ]]; then + if [[ "${OPENIM_GIT_TREE_STATE}" == "dirty" ]]; then # git describe --dirty only considers changes to existing files, but # that is problematic since new untracked .go files affect the build, # so use our idea of "dirty" from git status instead. # TODO? - #IAM_GIT_VERSION+="-dirty" + #OPENIM_GIT_VERSION+="-dirty" : fi @@ -90,17 +100,17 @@ iam::version::get_version_vars() { # Try to match the "git describe" output to a regex to try to extract # the "major" and "minor" versions and whether this is the exact tagged # version or whether the tree is between two tagged versions. - if [[ "${IAM_GIT_VERSION}" =~ ^v([0-9]+)\.([0-9]+)(\.[0-9]+)?([-].*)?([+].*)?$ ]]; then - IAM_GIT_MAJOR=${BASH_REMATCH[1]} - IAM_GIT_MINOR=${BASH_REMATCH[2]} + if [[ "${OPENIM_GIT_VERSION}" =~ ^v([0-9]+)\.([0-9]+)(\.[0-9]+)?([-].*)?([+].*)?$ ]]; then + OPENIM_GIT_MAJOR=${BASH_REMATCH[1]} + OPENIM_GIT_MINOR=${BASH_REMATCH[2]} if [[ -n "${BASH_REMATCH[4]}" ]]; then - IAM_GIT_MINOR+="+" + OPENIM_GIT_MINOR+="+" fi fi - # If IAM_GIT_VERSION is not a valid Semantic Version, then refuse to build. - if ! [[ "${IAM_GIT_VERSION}" =~ ^v([0-9]+)\.([0-9]+)(\.[0-9]+)?(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then - echo "IAM_GIT_VERSION should be a valid Semantic Version. Current value: ${IAM_GIT_VERSION}" + # If OPENIM_GIT_VERSION is not a valid Semantic Version, then refuse to build. + if ! [[ "${OPENIM_GIT_VERSION}" =~ ^v([0-9]+)\.([0-9]+)(\.[0-9]+)?(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then + echo "OPENIM_GIT_VERSION should be a valid Semantic Version. Current value: ${OPENIM_GIT_VERSION}" echo "Please see more details here: https://semver.org" exit 1 fi @@ -109,27 +119,27 @@ iam::version::get_version_vars() { } # Saves the environment flags to $1 -iam::version::save_version_vars() { +openim::version::save_version_vars() { local version_file=${1-} [[ -n ${version_file} ]] || { - echo "!!! Internal error. No file specified in iam::version::save_version_vars" + echo "!!! Internal error. No file specified in openim::version::save_version_vars" return 1 } cat <"${version_file}" -IAM_GIT_COMMIT='${IAM_GIT_COMMIT-}' -IAM_GIT_TREE_STATE='${IAM_GIT_TREE_STATE-}' -IAM_GIT_VERSION='${IAM_GIT_VERSION-}' -IAM_GIT_MAJOR='${IAM_GIT_MAJOR-}' -IAM_GIT_MINOR='${IAM_GIT_MINOR-}' +OPENIM_GIT_COMMIT='${OPENIM_GIT_COMMIT-}' +OPENIM_GIT_TREE_STATE='${OPENIM_GIT_TREE_STATE-}' +OPENIM_GIT_VERSION='${OPENIM_GIT_VERSION-}' +OPENIM_GIT_MAJOR='${OPENIM_GIT_MAJOR-}' +OPENIM_GIT_MINOR='${OPENIM_GIT_MINOR-}' EOF } # Loads up the version variables from file $1 -iam::version::load_version_vars() { +openim::version::load_version_vars() { local version_file=${1-} [[ -n ${version_file} ]] || { - echo "!!! Internal error. No file specified in iam::version::load_version_vars" + echo "!!! Internal error. No file specified in openim::version::load_version_vars" return 1 } diff --git a/scripts/make-rules/gen.mk b/scripts/make-rules/gen.mk index a0d44befa..8306c1756 100644 --- a/scripts/make-rules/gen.mk +++ b/scripts/make-rules/gen.mk @@ -31,7 +31,7 @@ gen.errcode: gen.errcode.code gen.errcode.doc .PHONY: gen.errcode.code gen.errcode.code: tools.verify.codegen - @echo "===========> Generating iam error code go source files" + @echo "===========> Generating openim error code go source files" @codegen -type=int ${ROOT_DIR}/internal/pkg/code .PHONY: gen.errcode.doc @@ -44,7 +44,7 @@ gen.errcode.doc: tools.verify.codegen gen.ca.%: $(eval CA := $(word 1,$(subst ., ,$*))) @echo "===========> Generating CA files for $(CA)" - @${ROOT_DIR}/scripts/gencerts.sh generate-iam-cert $(OUTPUT_DIR)/cert $(CA) + @${ROOT_DIR}/scripts/gencerts.sh generate-openim-cert $(OUTPUT_DIR)/cert $(CA) .PHONY: gen.ca gen.ca: $(addprefix gen.ca., $(CERTIFICATES)) diff --git a/scripts/make-rules/tools.mk b/scripts/make-rules/tools.mk index f91ef2231..c0e65ff94 100644 --- a/scripts/make-rules/tools.mk +++ b/scripts/make-rules/tools.mk @@ -189,7 +189,7 @@ install.protoc-gen-go: ## install.cfssl: Install cfssl, used to generate certificates .PHONY: install.cfssl install.cfssl: - @$(ROOT_DIR)/scripts/install/install.sh iam::install::install_cfssl + @$(ROOT_DIR)/scripts/install/install.sh openim::install::install_cfssl ## install.depth: Install depth, used to check dependency tree .PHONY: install.depth diff --git a/scripts/mongo-init.sh b/scripts/mongo-init.sh index 3d1faa60b..07d0e3d03 100755 --- a/scripts/mongo-init.sh +++ b/scripts/mongo-init.sh @@ -1,3 +1,17 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + mongo -- "$MONGO_INITDB_DATABASE" < Date: Tue, 4 Jul 2023 11:44:47 +0800 Subject: [PATCH 10/73] fix: fix scripts Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- scripts/githooks/commit-msg | 3 ++- scripts/lib/init.sh | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/githooks/commit-msg b/scripts/githooks/commit-msg index 66b40d016..bf00bfc31 100644 --- a/scripts/githooks/commit-msg +++ b/scripts/githooks/commit-msg @@ -56,7 +56,8 @@ test "" = "$(grep '^Signed-off-by: ' "$1" | } # TODO: go-gitlint dir set -GITLINT_DIR="./_output/tools/go-gitlint" +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/../.. +GITLINT_DIR="$OPENIM_ROOT/_output/tools/go-gitlint" $GITLINT_DIR \ --msg-file=$1 \ diff --git a/scripts/lib/init.sh b/scripts/lib/init.sh index edaca357d..294696087 100755 --- a/scripts/lib/init.sh +++ b/scripts/lib/init.sh @@ -19,7 +19,6 @@ set +o nounset set -o pipefail # Unset CDPATH so that path interpolation can work correctly -# https://github.com/iamrnetes/iamrnetes/issues/52255 unset CDPATH # Default use go modules From 2dcbfde3bff3834fd01adcf0f0cc42d0f8c806e6 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Tue, 4 Jul 2023 12:16:42 +0800 Subject: [PATCH 11/73] test: add chmod file in scripts Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- scripts/coverage.awk | 0 scripts/coverage.sh | 0 scripts/ensure_tag.sh | 0 scripts/release.sh | 0 4 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/coverage.awk mode change 100644 => 100755 scripts/coverage.sh mode change 100644 => 100755 scripts/ensure_tag.sh mode change 100644 => 100755 scripts/release.sh diff --git a/scripts/coverage.awk b/scripts/coverage.awk old mode 100644 new mode 100755 diff --git a/scripts/coverage.sh b/scripts/coverage.sh old mode 100644 new mode 100755 diff --git a/scripts/ensure_tag.sh b/scripts/ensure_tag.sh old mode 100644 new mode 100755 diff --git a/scripts/release.sh b/scripts/release.sh old mode 100644 new mode 100755 From edcb065636483af9126f51a145ebc00d5e381576 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Tue, 4 Jul 2023 12:29:31 +0800 Subject: [PATCH 12/73] feat: add common scripts Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- scripts/common.sh | 493 +++++++++++++++++++++++++++++++++++++++++ scripts/lib/version.sh | 2 +- 2 files changed, 494 insertions(+), 1 deletion(-) create mode 100755 scripts/common.sh diff --git a/scripts/common.sh b/scripts/common.sh new file mode 100755 index 000000000..a254412de --- /dev/null +++ b/scripts/common.sh @@ -0,0 +1,493 @@ +#!/usr/bin/env bash + +# shellcheck disable=SC2034 # Variables sourced in other scripts. + +# Common utilities, variables and checks for all build scripts. +set -o errexit +set -o nounset +set -o pipefail + +# Unset CDPATH, having it set messes up with script import paths +unset CDPATH + +USER_ID=$(id -u) +GROUP_ID=$(id -g) + +DOCKER_OPTS=${DOCKER_OPTS:-""} +IFS=" " read -r -a DOCKER <<< "docker ${DOCKER_OPTS}" +DOCKER_HOST=${DOCKER_HOST:-""} +DOCKER_MACHINE_NAME=${DOCKER_MACHINE_NAME:-"openim-dev"} +readonly DOCKER_MACHINE_DRIVER=${DOCKER_MACHINE_DRIVER:-"virtualbox --virtualbox-cpu-count -1"} + +# This will canonicalize the path +OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd -P) + +source "${OPENIM_ROOT}/scripts/lib/init.sh" + +# Constants +readonly OPENIM_BUILD_IMAGE_REPO=openim-build +#readonly OPENIM_BUILD_IMAGE_CROSS_TAG="$(cat "${OPENIM_ROOT}/build/build-image/cross/VERSION")" + +readonly OPENIM_DOCKER_REGISTRY="${OPENIM_DOCKER_REGISTRY:-k8s.gcr.io}" +readonly OPENIM_BASE_IMAGE_REGISTRY="${OPENIM_BASE_IMAGE_REGISTRY:-us.gcr.io/k8s-artifacts-prod/build-image}" + +# This version number is used to cause everyone to rebuild their data containers +# and build image. This is especially useful for automated build systems like +# Jenkins. +# +# Increment/change this number if you change the build image (anything under +# build/build-image) or change the set of volumes in the data container. +#readonly OPENIM_BUILD_IMAGE_VERSION_BASE="$(cat "${OPENIM_ROOT}/build/build-image/VERSION")" +#readonly OPENIM_BUILD_IMAGE_VERSION="${OPENIM_BUILD_IMAGE_VERSION_BASE}-${OPENIM_BUILD_IMAGE_CROSS_TAG}" + +# Here we map the output directories across both the local and remote _output +# directories: +# +# *_OUTPUT_ROOT - the base of all output in that environment. +# *_OUTPUT_SUBPATH - location where golang stuff is built/cached. Also +# persisted across docker runs with a volume mount. +# *_OUTPUT_BINPATH - location where final binaries are placed. If the remote +# is really remote, this is the stuff that has to be copied +# back. +# OUT_DIR can come in from the Makefile, so honor it. +readonly LOCAL_OUTPUT_ROOT="${OPENIM_ROOT}/${OUT_DIR:-_output}" +readonly LOCAL_OUTPUT_SUBPATH="${LOCAL_OUTPUT_ROOT}/platforms" +readonly LOCAL_OUTPUT_BINPATH="${LOCAL_OUTPUT_SUBPATH}" +readonly LOCAL_OUTPUT_GOPATH="${LOCAL_OUTPUT_SUBPATH}/go" +readonly LOCAL_OUTPUT_IMAGE_STAGING="${LOCAL_OUTPUT_ROOT}/images" + +# This is the port on the workstation host to expose RSYNC on. Set this if you +# are doing something fancy with ssh tunneling. +readonly OPENIM_RSYNC_PORT="${OPENIM_RSYNC_PORT:-}" + +# This is the port that rsync is running on *inside* the container. This may be +# mapped to OPENIM_RSYNC_PORT via docker networking. +readonly OPENIM_CONTAINER_RSYNC_PORT=8730 + +# Get the set of master binaries that run in Docker (on Linux) +# Entry format is ",". +# Binaries are placed in /usr/local/bin inside the image. +# +# $1 - server architecture +openim::build::get_docker_wrapped_binaries() { + local arch=$1 + local debian_base_version=v2.1.0 + local debian_iptables_version=v12.1.0 + ### If you change any of these lists, please also update DOCKERIZED_BINARIES + ### in build/BUILD. And openim::golang::server_image_targets + local targets=( + "openim-apiserver,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-controller-manager,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-scheduler,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-proxy,${OPENIM_BASE_IMAGE_REGISTRY}/debian-iptables-${arch}:${debian_iptables_version}" + ) + + echo "${targets[@]}" +} + +# --------------------------------------------------------------------------- +# Basic setup functions + +# Verify that the right utilities and such are installed for building openim. Set +# up some dynamic constants. +# Args: +# $1 - boolean of whether to require functioning docker (default true) +# +# Vars set: +# OPENIM_ROOT_HASH +# OPENIM_BUILD_IMAGE_TAG_BASE +# OPENIM_BUILD_IMAGE_TAG +# OPENIM_BUILD_IMAGE +# OPENIM_BUILD_CONTAINER_NAME_BASE +# OPENIM_BUILD_CONTAINER_NAME +# OPENIM_DATA_CONTAINER_NAME_BASE +# OPENIM_DATA_CONTAINER_NAME +# OPENIM_RSYNC_CONTAINER_NAME_BASE +# OPENIM_RSYNC_CONTAINER_NAME +# DOCKER_MOUNT_ARGS +# LOCAL_OUTPUT_BUILD_CONTEXT +function openim::build::verify_prereqs() { + local -r require_docker=${1:-true} + openim::log::status "Verifying Prerequisites...." + openim::build::ensure_tar || return 1 + openim::build::ensure_rsync || return 1 + if ${require_docker}; then + openim::build::ensure_docker_in_path || return 1 + openim::util::ensure_docker_daemon_connectivity || return 1 + + if (( OPENIM_VERBOSE > 6 )); then + openim::log::status "Docker Version:" + "${DOCKER[@]}" version | openim::log::info_from_stdin + fi + fi + + OPENIM_GIT_BRANCH=$(git symbolic-ref --short -q HEAD 2>/dev/null || true) + OPENIM_ROOT_HASH=$(openim::build::short_hash "${HOSTNAME:-}:${OPENIM_ROOT}:${OPENIM_GIT_BRANCH}") + OPENIM_BUILD_IMAGE_TAG_BASE="build-${OPENIM_ROOT_HASH}" + #OPENIM_BUILD_IMAGE_TAG="${OPENIM_BUILD_IMAGE_TAG_BASE}-${OPENIM_BUILD_IMAGE_VERSION}" + #OPENIM_BUILD_IMAGE="${OPENIM_BUILD_IMAGE_REPO}:${OPENIM_BUILD_IMAGE_TAG}" + OPENIM_BUILD_CONTAINER_NAME_BASE="openim-build-${OPENIM_ROOT_HASH}" + #OPENIM_BUILD_CONTAINER_NAME="${OPENIM_BUILD_CONTAINER_NAME_BASE}-${OPENIM_BUILD_IMAGE_VERSION}" + OPENIM_RSYNC_CONTAINER_NAME_BASE="openim-rsync-${OPENIM_ROOT_HASH}" + #OPENIM_RSYNC_CONTAINER_NAME="${OPENIM_RSYNC_CONTAINER_NAME_BASE}-${OPENIM_BUILD_IMAGE_VERSION}" + OPENIM_DATA_CONTAINER_NAME_BASE="openim-build-data-${OPENIM_ROOT_HASH}" + #OPENIM_DATA_CONTAINER_NAME="${OPENIM_DATA_CONTAINER_NAME_BASE}-${OPENIM_BUILD_IMAGE_VERSION}" + #DOCKER_MOUNT_ARGS=(--volumes-from "${OPENIM_DATA_CONTAINER_NAME}") + #LOCAL_OUTPUT_BUILD_CONTEXT="${LOCAL_OUTPUT_IMAGE_STAGING}/${OPENIM_BUILD_IMAGE}" + + openim::version::get_version_vars + #openim::version::save_version_vars "${OPENIM_ROOT}/.dockerized-openim-version-defs" +} + +# --------------------------------------------------------------------------- +# Utility functions + +function openim::build::docker_available_on_osx() { + if [[ -z "${DOCKER_HOST}" ]]; then + if [[ -S "/var/run/docker.sock" ]]; then + openim::log::status "Using Docker for MacOS" + return 0 + fi + + openim::log::status "No docker host is set. Checking options for setting one..." + if [[ -z "$(which docker-machine)" ]]; then + openim::log::status "It looks like you're running Mac OS X, yet neither Docker for Mac nor docker-machine can be found." + openim::log::status "See: https://docs.docker.com/engine/installation/mac/ for installation instructions." + return 1 + elif [[ -n "$(which docker-machine)" ]]; then + openim::build::prepare_docker_machine + fi + fi +} + +function openim::build::prepare_docker_machine() { + openim::log::status "docker-machine was found." + + local available_memory_bytes + available_memory_bytes=$(sysctl -n hw.memsize 2>/dev/null) + + local bytes_in_mb=1048576 + + # Give virtualbox 1/2 the system memory. Its necessary to divide by 2, instead + # of multiple by .5, because bash can only multiply by ints. + local memory_divisor=2 + + local virtualbox_memory_mb=$(( available_memory_bytes / (bytes_in_mb * memory_divisor) )) + + docker-machine inspect "${DOCKER_MACHINE_NAME}" &> /dev/null || { + openim::log::status "Creating a machine to build IAM" + docker-machine create --driver "${DOCKER_MACHINE_DRIVER}" \ + --virtualbox-memory "${virtualbox_memory_mb}" \ + --engine-env HTTP_PROXY="${IAMRNETES_HTTP_PROXY:-}" \ + --engine-env HTTPS_PROXY="${IAMRNETES_HTTPS_PROXY:-}" \ + --engine-env NO_PROXY="${IAMRNETES_NO_PROXY:-127.0.0.1}" \ + "${DOCKER_MACHINE_NAME}" > /dev/null || { + openim::log::error "Something went wrong creating a machine." + openim::log::error "Try the following: " + openim::log::error "docker-machine create -d ${DOCKER_MACHINE_DRIVER} --virtualbox-memory ${virtualbox_memory_mb} ${DOCKER_MACHINE_NAME}" + return 1 + } + } + docker-machine start "${DOCKER_MACHINE_NAME}" &> /dev/null + # it takes `docker-machine env` a few seconds to work if the machine was just started + local docker_machine_out + while ! docker_machine_out=$(docker-machine env "${DOCKER_MACHINE_NAME}" 2>&1); do + if [[ ${docker_machine_out} =~ "Error checking TLS connection" ]]; then + echo "${docker_machine_out}" + docker-machine regenerate-certs "${DOCKER_MACHINE_NAME}" + else + sleep 1 + fi + done + eval "$(docker-machine env "${DOCKER_MACHINE_NAME}")" + openim::log::status "A Docker host using docker-machine named '${DOCKER_MACHINE_NAME}' is ready to go!" + return 0 +} + +function openim::build::is_gnu_sed() { + [[ $(sed --version 2>&1) == *GNU* ]] +} + +function openim::build::ensure_rsync() { + if [[ -z "$(which rsync)" ]]; then + openim::log::error "Can't find 'rsync' in PATH, please fix and retry." + return 1 + fi +} + +function openim::build::update_dockerfile() { + if openim::build::is_gnu_sed; then + sed_opts=(-i) + else + sed_opts=(-i '') + fi + sed "${sed_opts[@]}" "s/OPENIM_BUILD_IMAGE_CROSS_TAG/${OPENIM_BUILD_IMAGE_CROSS_TAG}/" "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" +} + +function openim::build::set_proxy() { + if [[ -n "${IAMRNETES_HTTPS_PROXY:-}" ]]; then + echo "ENV https_proxy $IAMRNETES_HTTPS_PROXY" >> "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" + fi + if [[ -n "${IAMRNETES_HTTP_PROXY:-}" ]]; then + echo "ENV http_proxy $IAMRNETES_HTTP_PROXY" >> "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" + fi + if [[ -n "${IAMRNETES_NO_PROXY:-}" ]]; then + echo "ENV no_proxy $IAMRNETES_NO_PROXY" >> "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" + fi +} + +function openim::build::ensure_docker_in_path() { + if [[ -z "$(which docker)" ]]; then + openim::log::error "Can't find 'docker' in PATH, please fix and retry." + openim::log::error "See https://docs.docker.com/installation/#installation for installation instructions." + return 1 + fi +} + +function openim::build::ensure_tar() { + if [[ -n "${TAR:-}" ]]; then + return + fi + + # Find gnu tar if it is available, bomb out if not. + TAR=tar + if which gtar &>/dev/null; then + TAR=gtar + else + if which gnutar &>/dev/null; then + TAR=gnutar + fi + fi + if ! "${TAR}" --version | grep -q GNU; then + echo " !!! Cannot find GNU tar. Build on Linux or install GNU tar" + echo " on Mac OS X (brew install gnu-tar)." + return 1 + fi +} + +function openim::build::has_docker() { + which docker &> /dev/null +} + +function openim::build::has_ip() { + which ip &> /dev/null && ip -Version | grep 'iproute2' &> /dev/null +} + +# Detect if a specific image exists +# +# $1 - image repo name +# $2 - image tag +function openim::build::docker_image_exists() { + [[ -n $1 && -n $2 ]] || { + openim::log::error "Internal error. Image not specified in docker_image_exists." + exit 2 + } + + [[ $("${DOCKER[@]}" images -q "${1}:${2}") ]] +} + +# Delete all images that match a tag prefix except for the "current" version +# +# $1: The image repo/name +# $2: The tag base. We consider any image that matches $2* +# $3: The current image not to delete if provided +function openim::build::docker_delete_old_images() { + # In Docker 1.12, we can replace this with + # docker images "$1" --format "{{.Tag}}" + for tag in $("${DOCKER[@]}" images "${1}" | tail -n +2 | awk '{print $2}') ; do + if [[ "${tag}" != "${2}"* ]] ; then + V=3 openim::log::status "Keeping image ${1}:${tag}" + continue + fi + + if [[ -z "${3:-}" || "${tag}" != "${3}" ]] ; then + V=2 openim::log::status "Deleting image ${1}:${tag}" + "${DOCKER[@]}" rmi "${1}:${tag}" >/dev/null + else + V=3 openim::log::status "Keeping image ${1}:${tag}" + fi + done +} + +# Stop and delete all containers that match a pattern +# +# $1: The base container prefix +# $2: The current container to keep, if provided +function openim::build::docker_delete_old_containers() { + # In Docker 1.12 we can replace this line with + # docker ps -a --format="{{.Names}}" + for container in $("${DOCKER[@]}" ps -a | tail -n +2 | awk '{print $NF}') ; do + if [[ "${container}" != "${1}"* ]] ; then + V=3 openim::log::status "Keeping container ${container}" + continue + fi + if [[ -z "${2:-}" || "${container}" != "${2}" ]] ; then + V=2 openim::log::status "Deleting container ${container}" + openim::build::destroy_container "${container}" + else + V=3 openim::log::status "Keeping container ${container}" + fi + done +} + +# Takes $1 and computes a short has for it. Useful for unique tag generation +function openim::build::short_hash() { + [[ $# -eq 1 ]] || { + openim::log::error "Internal error. No data based to short_hash." + exit 2 + } + + local short_hash + if which md5 >/dev/null 2>&1; then + short_hash=$(md5 -q -s "$1") + else + short_hash=$(echo -n "$1" | md5sum) + fi + echo "${short_hash:0:10}" +} + +# Pedantically kill, wait-on and remove a container. The -f -v options +# to rm don't actually seem to get the job done, so force kill the +# container, wait to ensure it's stopped, then try the remove. This is +# a workaround for bug https://github.com/docker/docker/issues/3968. +function openim::build::destroy_container() { + "${DOCKER[@]}" kill "$1" >/dev/null 2>&1 || true + if [[ $("${DOCKER[@]}" version --format '{{.Server.Version}}') = 17.06.0* ]]; then + # Workaround https://github.com/moby/moby/issues/33948. + # TODO: remove when 17.06.0 is not relevant anymore + DOCKER_API_VERSION=v1.29 "${DOCKER[@]}" wait "$1" >/dev/null 2>&1 || true + else + "${DOCKER[@]}" wait "$1" >/dev/null 2>&1 || true + fi + "${DOCKER[@]}" rm -f -v "$1" >/dev/null 2>&1 || true +} + +# --------------------------------------------------------------------------- +# Building + + +function openim::build::clean() { + if openim::build::has_docker ; then + openim::build::docker_delete_old_containers "${OPENIM_BUILD_CONTAINER_NAME_BASE}" + openim::build::docker_delete_old_containers "${OPENIM_RSYNC_CONTAINER_NAME_BASE}" + openim::build::docker_delete_old_containers "${OPENIM_DATA_CONTAINER_NAME_BASE}" + openim::build::docker_delete_old_images "${OPENIM_BUILD_IMAGE_REPO}" "${OPENIM_BUILD_IMAGE_TAG_BASE}" + + V=2 openim::log::status "Cleaning all untagged docker images" + "${DOCKER[@]}" rmi "$("${DOCKER[@]}" images -q --filter 'dangling=true')" 2> /dev/null || true + fi + + if [[ -d "${LOCAL_OUTPUT_ROOT}" ]]; then + openim::log::status "Removing _output directory" + rm -rf "${LOCAL_OUTPUT_ROOT}" + fi +} + +# Set up the context directory for the openim-build image and build it. +function openim::build::build_image() { + mkdir -p "${LOCAL_OUTPUT_BUILD_CONTEXT}" + # Make sure the context directory owned by the right user for syncing sources to container. + chown -R "${USER_ID}":"${GROUP_ID}" "${LOCAL_OUTPUT_BUILD_CONTEXT}" + + cp /etc/localtime "${LOCAL_OUTPUT_BUILD_CONTEXT}/" + + cp "${OPENIM_ROOT}/build/build-image/Dockerfile" "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" + cp "${OPENIM_ROOT}/build/build-image/rsyncd.sh" "${LOCAL_OUTPUT_BUILD_CONTEXT}/" + dd if=/dev/urandom bs=512 count=1 2>/dev/null | LC_ALL=C tr -dc 'A-Za-z0-9' | dd bs=32 count=1 2>/dev/null > "${LOCAL_OUTPUT_BUILD_CONTEXT}/rsyncd.password" + chmod go= "${LOCAL_OUTPUT_BUILD_CONTEXT}/rsyncd.password" + + openim::build::update_dockerfile + openim::build::set_proxy + openim::build::docker_build "${OPENIM_BUILD_IMAGE}" "${LOCAL_OUTPUT_BUILD_CONTEXT}" 'false' + + # Clean up old versions of everything + openim::build::docker_delete_old_containers "${OPENIM_BUILD_CONTAINER_NAME_BASE}" "${OPENIM_BUILD_CONTAINER_NAME}" + openim::build::docker_delete_old_containers "${OPENIM_RSYNC_CONTAINER_NAME_BASE}" "${OPENIM_RSYNC_CONTAINER_NAME}" + openim::build::docker_delete_old_containers "${OPENIM_DATA_CONTAINER_NAME_BASE}" "${OPENIM_DATA_CONTAINER_NAME}" + openim::build::docker_delete_old_images "${OPENIM_BUILD_IMAGE_REPO}" "${OPENIM_BUILD_IMAGE_TAG_BASE}" "${OPENIM_BUILD_IMAGE_TAG}" + + openim::build::ensure_data_container + openim::build::sync_to_container +} + +# Build a docker image from a Dockerfile. +# $1 is the name of the image to build +# $2 is the location of the "context" directory, with the Dockerfile at the root. +# $3 is the value to set the --pull flag for docker build; true by default +function openim::build::docker_build() { + local -r image=$1 + local -r context_dir=$2 + local -r pull="${3:-true}" + local -ra build_cmd=("${DOCKER[@]}" build -t "${image}" "--pull=${pull}" "${context_dir}") + + openim::log::status "Building Docker image ${image}" + local docker_output + docker_output=$("${build_cmd[@]}" 2>&1) || { + cat <&2 ++++ Docker build command failed for ${image} + +${docker_output} + +To retry manually, run: + +${build_cmd[*]} + +EOF + return 1 + } +} + +function openim::build::ensure_data_container() { + # If the data container exists AND exited successfully, we can use it. + # Otherwise nuke it and start over. + local ret=0 + local code=0 + + code=$(docker inspect \ + -f '{{.State.ExitCode}}' \ + "${OPENIM_DATA_CONTAINER_NAME}" 2>/dev/null) || ret=$? + if [[ "${ret}" == 0 && "${code}" != 0 ]]; then + openim::build::destroy_container "${OPENIM_DATA_CONTAINER_NAME}" + ret=1 + fi + if [[ "${ret}" != 0 ]]; then + openim::log::status "Creating data container ${OPENIM_DATA_CONTAINER_NAME}" + # We have to ensure the directory exists, or else the docker run will + # create it as root. + mkdir -p "${LOCAL_OUTPUT_GOPATH}" + # We want this to run as root to be able to chown, so non-root users can + # later use the result as a data container. This run both creates the data + # container and chowns the GOPATH. + # + # The data container creates volumes for all of the directories that store + # intermediates for the Go build. This enables incremental builds across + # Docker sessions. The *_cgo paths are re-compiled versions of the go std + # libraries for true static building. + local -ra docker_cmd=( + "${DOCKER[@]}" run + --volume "${REMOTE_ROOT}" # white-out the whole output dir + --volume /usr/local/go/pkg/linux_386_cgo + --volume /usr/local/go/pkg/linux_amd64_cgo + --volume /usr/local/go/pkg/linux_arm_cgo + --volume /usr/local/go/pkg/linux_arm64_cgo + --volume /usr/local/go/pkg/linux_ppc64le_cgo + --volume /usr/local/go/pkg/darwin_amd64_cgo + --volume /usr/local/go/pkg/darwin_386_cgo + --volume /usr/local/go/pkg/windows_amd64_cgo + --volume /usr/local/go/pkg/windows_386_cgo + --name "${OPENIM_DATA_CONTAINER_NAME}" + --hostname "${HOSTNAME}" + "${OPENIM_BUILD_IMAGE}" + chown -R "${USER_ID}":"${GROUP_ID}" + "${REMOTE_ROOT}" + /usr/local/go/pkg/ + ) + "${docker_cmd[@]}" + fi +} + +# Build all openim commands. +function openim::build::build_command() { + openim::log::status "Running build command..." + make -C "${OPENIM_ROOT}" build.multiarch BINS="openimctl openim-apiserver openim-authz-server openim-pump openim-watcher" +} diff --git a/scripts/lib/version.sh b/scripts/lib/version.sh index 2a59bf63a..9e3e055fd 100755 --- a/scripts/lib/version.sh +++ b/scripts/lib/version.sh @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. - + # ----------------------------------------------------------------------------- # Version management helpers. These functions help to set, save and load the # following variables: From 4d8fafe029380af09f1bdf7b88b4c134ee5ea81b Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Tue, 4 Jul 2023 14:26:24 +0800 Subject: [PATCH 13/73] feat: add release Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- Makefile | 5 +++++ scripts/coverage.sh | 2 -- scripts/ensure_tag.sh | 1 + scripts/lib/version.sh | 1 - scripts/make-rules/release.mk | 4 ++-- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 8e697535e..c210edbe3 100644 --- a/Makefile +++ b/Makefile @@ -158,6 +158,11 @@ verify-copyright: add-copyright: @$(MAKE) copyright.add +## release: release the project +.PHONY: release +release: release.verify release.ensure-tag + @scripts/release.sh + ## help: Show this help info. .PHONY: help help: Makefile diff --git a/scripts/coverage.sh b/scripts/coverage.sh index d4605ee3f..fbb2b36fc 100755 --- a/scripts/coverage.sh +++ b/scripts/coverage.sh @@ -13,7 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. - # http://stackoverflow.com/a/21142256/2055281 echo "mode: atomic" > coverage.txt @@ -27,4 +26,3 @@ for d in $(find ./* -maxdepth 10 -type d); do fi fi done - diff --git a/scripts/ensure_tag.sh b/scripts/ensure_tag.sh index 2da3c404f..c6fea7ca0 100755 --- a/scripts/ensure_tag.sh +++ b/scripts/ensure_tag.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash + # Copyright © 2023 OpenIM. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/scripts/lib/version.sh b/scripts/lib/version.sh index 9e3e055fd..65f9bb353 100755 --- a/scripts/lib/version.sh +++ b/scripts/lib/version.sh @@ -96,7 +96,6 @@ openim::version::get_version_vars() { : fi - # Try to match the "git describe" output to a regex to try to extract # the "major" and "minor" versions and whether this is the exact tagged # version or whether the tree is between two tagged versions. diff --git a/scripts/make-rules/release.mk b/scripts/make-rules/release.mk index 06b7b7e47..f3fd3d5f3 100644 --- a/scripts/make-rules/release.mk +++ b/scripts/make-rules/release.mk @@ -17,7 +17,7 @@ # Versions are used after merging # -## release: release the project +## release.run: release the project .PHONY: release.run release.run: release.verify release.ensure-tag @scripts/release.sh @@ -39,4 +39,4 @@ release.ensure-tag: tools.verify.gsemver ## release.help: Display help information about the release package .PHONY: release.help release.help: scripts/make-rules/release.mk - $(call smallhelp) \ No newline at end of file + $(call smallhelp) From 667d9e33d7cf2bcbc60006aa38349bec374d789c Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Tue, 4 Jul 2023 14:44:30 +0800 Subject: [PATCH 14/73] feat: add codeowners file Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- docs/CODEOWNERS | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 docs/CODEOWNERS diff --git a/docs/CODEOWNERS b/docs/CODEOWNERS new file mode 100644 index 000000000..3cfc940e3 --- /dev/null +++ b/docs/CODEOWNERS @@ -0,0 +1,88 @@ +# This is a comment. +# Each line is a file pattern followed by one or more owners. + +# README files +README.md @cubxxw @kubbot @Bloomingg @FGadvancer @hrxiang @Oliver-WJ @skiffer-git @std-s @wangchuxiao-dev @withchao + +# Contributing guidelines +CONTRIBUTING.md @cubxxw @kubbot @Bloomingg @FGadvancer @hrxiang @Oliver-WJ @skiffer-git @std-s @wangchuxiao-dev @withchao + +# License files +LICENSE @cubxxw @kubbot @Bloomingg @FGadvancer @hrxiang @Oliver-WJ @skiffer-git @std-s @wangchuxiao-dev @withchao + +# Makefile +Makefile @cubxxw @kubbot @Bloomingg @FGadvancer @hrxiang @Oliver-WJ @skiffer-git @std-s @wangchuxiao-dev @withchao + +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence, +# @cubxxw and @kubbot will be requested for +# review when someone opens a pull request. +* @cubxxw @kubbot @Bloomingg @FGadvancer @hrxiang @Oliver-WJ @skiffer-git @std-s @wangchuxiao-dev @withchao + +# Order is important; the last matching pattern takes the most +# precedence. When someone opens a pull request that only +# modifies JS files, only @js-owner and not the global +# owner(s) will be requested for a review. +*.js @cubxxw @kubbot @Bloomingg @FGadvancer @hrxiang @Oliver-WJ @skiffer-git @std-s @wangchuxiao-dev @withchao + +# You can also use email addresses if you prefer. They'll be +# used to look up users just like we do for commit author +# emails. +*.go 3293172751nss@gmail.com +*.py 3293172751nss@gmail.com + +# Teams can be specified as code owners as well. Teams should +# be identified in the format @org/team-name. Teams must have +# explicit write access to the repository. In this example, +# the OpenIMSDK team in the github organization owns all .txt files. +*.txt @cubxxw @kubbot @Bloomingg @FGadvancer @hrxiang @Oliver-WJ @skiffer-git @std-s @wangchuxiao-dev @withchao + +# The `docs/*` pattern will match files like +# `docs/getting-started.md` but not further nested files like +# `docs/build-app/troubleshooting.md`. +docs/* 3293172751nss@gmail.com @kubbot @skiffer-git + +# In this example, @octocat owns any file in an apps directory +# anywhere in your repository. +api/ @cubxxw @IRONICBo @skiffer-git + +# This is a comment. +# Each line is a file pattern followed by one or more owners. + +# CHANGELOG file +CHANGELOG/* @cubxxw @skiffer-git + +# _output directory +_output/* @skiffer-git + +# bin directory +bin/* @skiffer-git @FGadvancer + +# cmd directory +cmd/* + +# config directory +config/* @skiffer-git + +# db directory +db/sdk @77caleb @BanTanger @cubxxw @Gordon + +# internal directory +internal/ @skiffer-git @FGadvancer + +# logs directory +logs/* @skiffer-git @FGadvancer + +# pkg directory +pkg/a2r @skiffer-git + +# scripts directory +scripts/LICENSE/* @cubxxw @skiffer-git @FGadvancer +scripts/enterprise/* @FGadvancer @cubxxw @skiffer-git @kubbot +scripts/githooks/* @cubxxw @skiffer-git @FGadvancer +scripts/lib/* @FGadvancer @cubxxw @skiffer-git @kubbot +scripts/make-rules/* @FGadvancer @cubxxw @skiffer-git @kubbot + +# test directory +test/mongo @FGadvancer @cubxxw @skiffer-git @kubbot +test/mysql @FGadvancer @cubxxw @skiffer-git @kubbot From ada9acf21870e81a9d72e9046e07089227eb85c9 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Tue, 4 Jul 2023 16:17:18 +0800 Subject: [PATCH 15/73] feat: add git hook sign Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- .github/workflows/openim-ci.yml | 2 +- scripts/githooks/commit-msg | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/openim-ci.yml b/.github/workflows/openim-ci.yml index e063c6e3b..d58211444 100644 --- a/.github/workflows/openim-ci.yml +++ b/.github/workflows/openim-ci.yml @@ -19,7 +19,7 @@ jobs: strategy: matrix: - go_version: [1.18, 1.19, 1.20] + go_version: ['1.18', '1.19', '1.20'] os: [ubuntu-latest, macOS-latest] steps: diff --git a/scripts/githooks/commit-msg b/scripts/githooks/commit-msg index bf00bfc31..99f4067b9 100644 --- a/scripts/githooks/commit-msg +++ b/scripts/githooks/commit-msg @@ -75,4 +75,18 @@ then printError "Please fix your commit message to match kubecub coding standards" printError "https://gist.github.com/cubxxw/126b72104ac0b0ca484c9db09c3e5694#file-githook-md" exit 1 +fi + +### Add Sign-off-by line to the end of the commit message +# Get local git config +NAME=$(git config user.name) +EMAIL=$(git config user.email) + +# Check if the commit message contains a sign-off line +grep -qs "^Signed-off-by: " "$1" +SIGNED_OFF_BY_EXISTS=$? + +# Add "Signed-off-by" line if it doesn't exist +if [ $SIGNED_OFF_BY_EXISTS -ne 0 ]; then + echo -e "\nSigned-off-by: $NAME <$EMAIL>" >> "$1" fi \ No newline at end of file From 12695337e9099eb0379dd300fb1f2555b46366fb Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Tue, 4 Jul 2023 17:09:49 +0800 Subject: [PATCH 16/73] feat: add more feature in scripts Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- .github/weekly-digest.yml | 7 +++++++ .github/workflows/openim-ci.yml | 4 ++-- scripts/lib/release.sh | 6 +++--- scripts/make-rules/release.mk | 2 +- scripts/make-rules/tools.mk | 1 + 5 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 .github/weekly-digest.yml diff --git a/.github/weekly-digest.yml b/.github/weekly-digest.yml new file mode 100644 index 000000000..9deb72e7b --- /dev/null +++ b/.github/weekly-digest.yml @@ -0,0 +1,7 @@ +# https://github.com/apps/weekly-digest/installations/new +publishDay: sun +canPublishIssues: true +canPublishPullRequests: true +canPublishContributors: true +canPublishStargazers: true +canPublishCommits: true \ No newline at end of file diff --git a/.github/workflows/openim-ci.yml b/.github/workflows/openim-ci.yml index d58211444..c8b95eb8e 100644 --- a/.github/workflows/openim-ci.yml +++ b/.github/workflows/openim-ci.yml @@ -1,4 +1,4 @@ -name: OpenIMCI +name: OpenIM CI on: # main branch @@ -64,7 +64,7 @@ jobs: - name: Login to DockerHub uses: docker/login-action@v1 with: - username: ${{ secrets.DOCKERHUB_USERNAME }} + username: ${{ env.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build docker images for host arch and push images to registry diff --git a/scripts/lib/release.sh b/scripts/lib/release.sh index 1817e4ff2..e596afaff 100755 --- a/scripts/lib/release.sh +++ b/scripts/lib/release.sh @@ -27,8 +27,8 @@ readonly BUCKET="openim-1306374445" readonly REGION="ap-beijing" readonly COS_RELEASE_DIR="openim-release" -# default cos command tool -readonly COSTOOL="coscmd" +# default cos command tool coscli or coscmd +readonly COSTOOL="coscli" # This is where the final release artifacts are created locally readonly RELEASE_STAGE="${LOCAL_OUTPUT_ROOT}/release-stage" @@ -37,7 +37,7 @@ readonly RELEASE_IMAGES="${LOCAL_OUTPUT_ROOT}/release-images" # OpenIM github account info readonly OPENIM_GITHUB_ORG=OpenIMSDK -readonly OPENIM_GITHUB_REPO=openim +readonly OPENIM_GITHUB_REPO=Open-IM-Server readonly ARTIFACT=openim.tar.gz readonly CHECKSUM=${ARTIFACT}.sha1sum diff --git a/scripts/make-rules/release.mk b/scripts/make-rules/release.mk index f3fd3d5f3..862aa3065 100644 --- a/scripts/make-rules/release.mk +++ b/scripts/make-rules/release.mk @@ -24,7 +24,7 @@ release.run: release.verify release.ensure-tag ## release.verify: Check if a tool is installed and install it .PHONY: release.verify -release.verify: tools.verify.git-chglog tools.verify.github-release tools.verify.coscmd +release.verify: tools.verify.git-chglog tools.verify.github-release tools.verify.coscmd tools.verify.coscli ## release.tag: release the project .PHONY: release.tag diff --git a/scripts/make-rules/tools.mk b/scripts/make-rules/tools.mk index c0e65ff94..468cf109f 100644 --- a/scripts/make-rules/tools.mk +++ b/scripts/make-rules/tools.mk @@ -133,6 +133,7 @@ install.github-release: ## install.coscli: Install coscli, used to upload files to cos # example: ./coscli cp/sync -r /home/off-line/docker-off-line/ cos://openim-1306374445/openim/image/amd/off-line/off-line/ -e cos.ap-guangzhou.myqcloud.com # https://cloud.tencent.com/document/product/436/71763 +# amd64 .PHONY: install.coscli install.coscli: @wget -q https://ghproxy.com/https://github.com/tencentyun/coscli/releases/download/v0.13.0-beta/coscli-linux -O ${TOOLS_DIR}/coscli From aedd16a33adce824dfedbba579ac8c92f3f33de5 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Tue, 4 Jul 2023 18:02:46 +0800 Subject: [PATCH 17/73] fix: fix cmd filename Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- cmd/{api => openim_api}/Makefile | 0 cmd/{api => openim_api}/deploy.Dockerfile | 0 cmd/{api => openim_api}/main.go | 0 cmd/{cmdutils => openim_cmdutils}/Makefile | 0 cmd/{cmdutils => openim_cmdutils}/main.go | 0 cmd/{crontask => openim_crontask}/Makefile | 0 cmd/{crontask => openim_crontask}/deploy.Dockerfile | 0 cmd/{crontask => openim_crontask}/main.go | 0 cmd/{msggateway => openim_msggateway}/Makefile | 0 cmd/{msggateway => openim_msggateway}/deploy.Dockerfile | 0 cmd/{msggateway => openim_msggateway}/main.go | 0 cmd/{msgtransfer => openim_msgtransfer}/Makefile | 0 cmd/{msgtransfer => openim_msgtransfer}/deploy.Dockerfile | 0 cmd/{msgtransfer => openim_msgtransfer}/main.go | 0 cmd/{push => openim_push}/Makefile | 0 cmd/{push => openim_push}/deploy.Dockerfile | 0 cmd/{push => openim_push}/main.go | 0 cmd/{rpc/auth => openim_rpc/openim_rpc_auth}/Makefile | 0 .../auth => openim_rpc/openim_rpc_auth}/deploy.Dockerfile | 0 cmd/{rpc/auth => openim_rpc/openim_rpc_auth}/main.go | 0 .../openim_rpc_conversation}/Makefile | 0 .../openim_rpc_conversation}/deploy.Dockerfile | 0 .../openim_rpc_conversation}/main.go | 0 cmd/{rpc/friend => openim_rpc/openim_rpc_friend}/Makefile | 0 .../friend => openim_rpc/openim_rpc_friend}/deploy.Dockerfile | 0 cmd/{rpc/friend => openim_rpc/openim_rpc_friend}/main.go | 0 cmd/{rpc/group => openim_rpc/openim_rpc_group}/Makefile | 0 .../group => openim_rpc/openim_rpc_group}/deploy.Dockerfile | 0 cmd/{rpc/group => openim_rpc/openim_rpc_group}/main.go | 0 cmd/{rpc/msg => openim_rpc/openim_rpc_msg}/Makefile | 0 cmd/{rpc/msg => openim_rpc/openim_rpc_msg}/deploy.Dockerfile | 0 cmd/{rpc/msg => openim_rpc/openim_rpc_msg}/main.go | 0 cmd/{rpc/third => openim_rpc/openim_rpc_third}/Makefile | 0 .../third => openim_rpc/openim_rpc_third}/deploy.Dockerfile | 0 cmd/{rpc/third => openim_rpc/openim_rpc_third}/main.go | 0 cmd/{rpc/user => openim_rpc/openim_rpc_user}/Makefile | 0 .../user => openim_rpc/openim_rpc_user}/deploy.Dockerfile | 0 cmd/{rpc/user => openim_rpc/openim_rpc_user}/main.go | 0 scripts/make-rules/common.mk | 4 ++-- scripts/make-rules/golang.mk | 4 ++-- 40 files changed, 4 insertions(+), 4 deletions(-) rename cmd/{api => openim_api}/Makefile (100%) rename cmd/{api => openim_api}/deploy.Dockerfile (100%) rename cmd/{api => openim_api}/main.go (100%) rename cmd/{cmdutils => openim_cmdutils}/Makefile (100%) rename cmd/{cmdutils => openim_cmdutils}/main.go (100%) rename cmd/{crontask => openim_crontask}/Makefile (100%) rename cmd/{crontask => openim_crontask}/deploy.Dockerfile (100%) rename cmd/{crontask => openim_crontask}/main.go (100%) rename cmd/{msggateway => openim_msggateway}/Makefile (100%) rename cmd/{msggateway => openim_msggateway}/deploy.Dockerfile (100%) rename cmd/{msggateway => openim_msggateway}/main.go (100%) rename cmd/{msgtransfer => openim_msgtransfer}/Makefile (100%) rename cmd/{msgtransfer => openim_msgtransfer}/deploy.Dockerfile (100%) rename cmd/{msgtransfer => openim_msgtransfer}/main.go (100%) rename cmd/{push => openim_push}/Makefile (100%) rename cmd/{push => openim_push}/deploy.Dockerfile (100%) rename cmd/{push => openim_push}/main.go (100%) rename cmd/{rpc/auth => openim_rpc/openim_rpc_auth}/Makefile (100%) rename cmd/{rpc/auth => openim_rpc/openim_rpc_auth}/deploy.Dockerfile (100%) rename cmd/{rpc/auth => openim_rpc/openim_rpc_auth}/main.go (100%) rename cmd/{rpc/conversation => openim_rpc/openim_rpc_conversation}/Makefile (100%) rename cmd/{rpc/conversation => openim_rpc/openim_rpc_conversation}/deploy.Dockerfile (100%) rename cmd/{rpc/conversation => openim_rpc/openim_rpc_conversation}/main.go (100%) rename cmd/{rpc/friend => openim_rpc/openim_rpc_friend}/Makefile (100%) rename cmd/{rpc/friend => openim_rpc/openim_rpc_friend}/deploy.Dockerfile (100%) rename cmd/{rpc/friend => openim_rpc/openim_rpc_friend}/main.go (100%) rename cmd/{rpc/group => openim_rpc/openim_rpc_group}/Makefile (100%) rename cmd/{rpc/group => openim_rpc/openim_rpc_group}/deploy.Dockerfile (100%) rename cmd/{rpc/group => openim_rpc/openim_rpc_group}/main.go (100%) rename cmd/{rpc/msg => openim_rpc/openim_rpc_msg}/Makefile (100%) rename cmd/{rpc/msg => openim_rpc/openim_rpc_msg}/deploy.Dockerfile (100%) rename cmd/{rpc/msg => openim_rpc/openim_rpc_msg}/main.go (100%) rename cmd/{rpc/third => openim_rpc/openim_rpc_third}/Makefile (100%) rename cmd/{rpc/third => openim_rpc/openim_rpc_third}/deploy.Dockerfile (100%) rename cmd/{rpc/third => openim_rpc/openim_rpc_third}/main.go (100%) rename cmd/{rpc/user => openim_rpc/openim_rpc_user}/Makefile (100%) rename cmd/{rpc/user => openim_rpc/openim_rpc_user}/deploy.Dockerfile (100%) rename cmd/{rpc/user => openim_rpc/openim_rpc_user}/main.go (100%) diff --git a/cmd/api/Makefile b/cmd/openim_api/Makefile similarity index 100% rename from cmd/api/Makefile rename to cmd/openim_api/Makefile diff --git a/cmd/api/deploy.Dockerfile b/cmd/openim_api/deploy.Dockerfile similarity index 100% rename from cmd/api/deploy.Dockerfile rename to cmd/openim_api/deploy.Dockerfile diff --git a/cmd/api/main.go b/cmd/openim_api/main.go similarity index 100% rename from cmd/api/main.go rename to cmd/openim_api/main.go diff --git a/cmd/cmdutils/Makefile b/cmd/openim_cmdutils/Makefile similarity index 100% rename from cmd/cmdutils/Makefile rename to cmd/openim_cmdutils/Makefile diff --git a/cmd/cmdutils/main.go b/cmd/openim_cmdutils/main.go similarity index 100% rename from cmd/cmdutils/main.go rename to cmd/openim_cmdutils/main.go diff --git a/cmd/crontask/Makefile b/cmd/openim_crontask/Makefile similarity index 100% rename from cmd/crontask/Makefile rename to cmd/openim_crontask/Makefile diff --git a/cmd/crontask/deploy.Dockerfile b/cmd/openim_crontask/deploy.Dockerfile similarity index 100% rename from cmd/crontask/deploy.Dockerfile rename to cmd/openim_crontask/deploy.Dockerfile diff --git a/cmd/crontask/main.go b/cmd/openim_crontask/main.go similarity index 100% rename from cmd/crontask/main.go rename to cmd/openim_crontask/main.go diff --git a/cmd/msggateway/Makefile b/cmd/openim_msggateway/Makefile similarity index 100% rename from cmd/msggateway/Makefile rename to cmd/openim_msggateway/Makefile diff --git a/cmd/msggateway/deploy.Dockerfile b/cmd/openim_msggateway/deploy.Dockerfile similarity index 100% rename from cmd/msggateway/deploy.Dockerfile rename to cmd/openim_msggateway/deploy.Dockerfile diff --git a/cmd/msggateway/main.go b/cmd/openim_msggateway/main.go similarity index 100% rename from cmd/msggateway/main.go rename to cmd/openim_msggateway/main.go diff --git a/cmd/msgtransfer/Makefile b/cmd/openim_msgtransfer/Makefile similarity index 100% rename from cmd/msgtransfer/Makefile rename to cmd/openim_msgtransfer/Makefile diff --git a/cmd/msgtransfer/deploy.Dockerfile b/cmd/openim_msgtransfer/deploy.Dockerfile similarity index 100% rename from cmd/msgtransfer/deploy.Dockerfile rename to cmd/openim_msgtransfer/deploy.Dockerfile diff --git a/cmd/msgtransfer/main.go b/cmd/openim_msgtransfer/main.go similarity index 100% rename from cmd/msgtransfer/main.go rename to cmd/openim_msgtransfer/main.go diff --git a/cmd/push/Makefile b/cmd/openim_push/Makefile similarity index 100% rename from cmd/push/Makefile rename to cmd/openim_push/Makefile diff --git a/cmd/push/deploy.Dockerfile b/cmd/openim_push/deploy.Dockerfile similarity index 100% rename from cmd/push/deploy.Dockerfile rename to cmd/openim_push/deploy.Dockerfile diff --git a/cmd/push/main.go b/cmd/openim_push/main.go similarity index 100% rename from cmd/push/main.go rename to cmd/openim_push/main.go diff --git a/cmd/rpc/auth/Makefile b/cmd/openim_rpc/openim_rpc_auth/Makefile similarity index 100% rename from cmd/rpc/auth/Makefile rename to cmd/openim_rpc/openim_rpc_auth/Makefile diff --git a/cmd/rpc/auth/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_auth/deploy.Dockerfile similarity index 100% rename from cmd/rpc/auth/deploy.Dockerfile rename to cmd/openim_rpc/openim_rpc_auth/deploy.Dockerfile diff --git a/cmd/rpc/auth/main.go b/cmd/openim_rpc/openim_rpc_auth/main.go similarity index 100% rename from cmd/rpc/auth/main.go rename to cmd/openim_rpc/openim_rpc_auth/main.go diff --git a/cmd/rpc/conversation/Makefile b/cmd/openim_rpc/openim_rpc_conversation/Makefile similarity index 100% rename from cmd/rpc/conversation/Makefile rename to cmd/openim_rpc/openim_rpc_conversation/Makefile diff --git a/cmd/rpc/conversation/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_conversation/deploy.Dockerfile similarity index 100% rename from cmd/rpc/conversation/deploy.Dockerfile rename to cmd/openim_rpc/openim_rpc_conversation/deploy.Dockerfile diff --git a/cmd/rpc/conversation/main.go b/cmd/openim_rpc/openim_rpc_conversation/main.go similarity index 100% rename from cmd/rpc/conversation/main.go rename to cmd/openim_rpc/openim_rpc_conversation/main.go diff --git a/cmd/rpc/friend/Makefile b/cmd/openim_rpc/openim_rpc_friend/Makefile similarity index 100% rename from cmd/rpc/friend/Makefile rename to cmd/openim_rpc/openim_rpc_friend/Makefile diff --git a/cmd/rpc/friend/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_friend/deploy.Dockerfile similarity index 100% rename from cmd/rpc/friend/deploy.Dockerfile rename to cmd/openim_rpc/openim_rpc_friend/deploy.Dockerfile diff --git a/cmd/rpc/friend/main.go b/cmd/openim_rpc/openim_rpc_friend/main.go similarity index 100% rename from cmd/rpc/friend/main.go rename to cmd/openim_rpc/openim_rpc_friend/main.go diff --git a/cmd/rpc/group/Makefile b/cmd/openim_rpc/openim_rpc_group/Makefile similarity index 100% rename from cmd/rpc/group/Makefile rename to cmd/openim_rpc/openim_rpc_group/Makefile diff --git a/cmd/rpc/group/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_group/deploy.Dockerfile similarity index 100% rename from cmd/rpc/group/deploy.Dockerfile rename to cmd/openim_rpc/openim_rpc_group/deploy.Dockerfile diff --git a/cmd/rpc/group/main.go b/cmd/openim_rpc/openim_rpc_group/main.go similarity index 100% rename from cmd/rpc/group/main.go rename to cmd/openim_rpc/openim_rpc_group/main.go diff --git a/cmd/rpc/msg/Makefile b/cmd/openim_rpc/openim_rpc_msg/Makefile similarity index 100% rename from cmd/rpc/msg/Makefile rename to cmd/openim_rpc/openim_rpc_msg/Makefile diff --git a/cmd/rpc/msg/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_msg/deploy.Dockerfile similarity index 100% rename from cmd/rpc/msg/deploy.Dockerfile rename to cmd/openim_rpc/openim_rpc_msg/deploy.Dockerfile diff --git a/cmd/rpc/msg/main.go b/cmd/openim_rpc/openim_rpc_msg/main.go similarity index 100% rename from cmd/rpc/msg/main.go rename to cmd/openim_rpc/openim_rpc_msg/main.go diff --git a/cmd/rpc/third/Makefile b/cmd/openim_rpc/openim_rpc_third/Makefile similarity index 100% rename from cmd/rpc/third/Makefile rename to cmd/openim_rpc/openim_rpc_third/Makefile diff --git a/cmd/rpc/third/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_third/deploy.Dockerfile similarity index 100% rename from cmd/rpc/third/deploy.Dockerfile rename to cmd/openim_rpc/openim_rpc_third/deploy.Dockerfile diff --git a/cmd/rpc/third/main.go b/cmd/openim_rpc/openim_rpc_third/main.go similarity index 100% rename from cmd/rpc/third/main.go rename to cmd/openim_rpc/openim_rpc_third/main.go diff --git a/cmd/rpc/user/Makefile b/cmd/openim_rpc/openim_rpc_user/Makefile similarity index 100% rename from cmd/rpc/user/Makefile rename to cmd/openim_rpc/openim_rpc_user/Makefile diff --git a/cmd/rpc/user/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_user/deploy.Dockerfile similarity index 100% rename from cmd/rpc/user/deploy.Dockerfile rename to cmd/openim_rpc/openim_rpc_user/deploy.Dockerfile diff --git a/cmd/rpc/user/main.go b/cmd/openim_rpc/openim_rpc_user/main.go similarity index 100% rename from cmd/rpc/user/main.go rename to cmd/openim_rpc/openim_rpc_user/main.go diff --git a/scripts/make-rules/common.mk b/scripts/make-rules/common.mk index 568b019ca..72b395e28 100644 --- a/scripts/make-rules/common.mk +++ b/scripts/make-rules/common.mk @@ -85,9 +85,9 @@ GOBIN=$(shell go env GOBIN) endif # The OS must be linux when building docker images -PLATFORMS ?= linux_amd64 linux_arm64 +# PLATFORMS ?= linux_amd64 linux_arm64 # The OS can be linux/windows/darwin when building binaries -# PLATFORMS ?= darwin_amd64 windows_amd64 linux_amd64 linux_arm64 +PLATFORMS ?= linux_s390x linux_mips64 linux_mips64le darwin_amd64 windows_amd64 linux_amd64 linux_arm64 # only support linux GOOS=linux diff --git a/scripts/make-rules/golang.mk b/scripts/make-rules/golang.mk index bf503db63..03be95a91 100644 --- a/scripts/make-rules/golang.mk +++ b/scripts/make-rules/golang.mk @@ -113,8 +113,8 @@ go.build.%: @mkdir -p $(BIN_DIR)/platforms/$(OS)/$(ARCH) @if [ "$(COMMAND)" == "openim-sdk-core" ]; then \ echo "===========> DEBUG: Compilation is not yet supported $(COMMAND)"; \ - elif [ "$(COMMAND)" == "rpc" ]; then \ - for d in $(wildcard $(ROOT_DIR)/cmd/rpc/*); do \ + elif [ "$(COMMAND)" == "openim_rpc" ]; then \ + for d in $(wildcard $(ROOT_DIR)/cmd/openim_rpc/*); do \ cd $${d} && CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) $(GO) build $(GO_BUILD_FLAGS) -o \ $(BIN_DIR)/platforms/$(OS)/$(ARCH)/$$(basename $${d})$(GO_OUT_EXT) $${d}/main.go; \ done; \ From 859d82fcd176a0cbb7f172795b59530b72b496dc Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Tue, 4 Jul 2023 18:25:51 +0800 Subject: [PATCH 18/73] feat: add makefile bug Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- pkg/common/db/controller/storage.go | 1 - scripts/common.sh | 14 ++++++++++++++ scripts/make-rules/swagger.mk | 12 ++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 scripts/make-rules/swagger.mk diff --git a/pkg/common/db/controller/storage.go b/pkg/common/db/controller/storage.go index ab20e7da8..c383eb8ec 100644 --- a/pkg/common/db/controller/storage.go +++ b/pkg/common/db/controller/storage.go @@ -14,7 +14,6 @@ package controller -import "C" import ( "bytes" "context" diff --git a/scripts/common.sh b/scripts/common.sh index a254412de..3a2090b6f 100755 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -1,4 +1,18 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # shellcheck disable=SC2034 # Variables sourced in other scripts. diff --git a/scripts/make-rules/swagger.mk b/scripts/make-rules/swagger.mk new file mode 100644 index 000000000..0598813c3 --- /dev/null +++ b/scripts/make-rules/swagger.mk @@ -0,0 +1,12 @@ +# ============================================================================== +# Makefile helper functions for swagger +# + +.PHONY: swagger.run +swagger.run: tools.verify.swagger + @echo "===========> Generating swagger API docs" + @swagger generate spec --scan-models -w $(ROOT_DIR)/cmd/genswaggertypedocs -o $(ROOT_DIR)/api/swagger/swagger.yaml + +.PHONY: swagger.serve +swagger.serve: tools.verify.swagger + @swagger serve -F=redoc --no-open --port 36666 $(ROOT_DIR)/api/swagger/swagger.yaml From dae7849393960c51ff5718ef2936797595fb6b4b Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Tue, 4 Jul 2023 18:35:10 +0800 Subject: [PATCH 19/73] feat: add license Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- .docker-compose_cfg/config.yaml | 14 ++++++++++++++ .docker-compose_cfg/datasource-compose.yaml | 14 ++++++++++++++ .docker-compose_cfg/prometheus-compose.yml | 14 ++++++++++++++ scripts/make-rules/common.mk | 2 +- scripts/make-rules/golang.mk | 2 +- 5 files changed, 44 insertions(+), 2 deletions(-) diff --git a/.docker-compose_cfg/config.yaml b/.docker-compose_cfg/config.yaml index 4ea03d854..b3b7d73d5 100644 --- a/.docker-compose_cfg/config.yaml +++ b/.docker-compose_cfg/config.yaml @@ -1,3 +1,17 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + #---------------Infrastructure configuration---------------------# etcd: etcdSchema: openim #默认即可 diff --git a/.docker-compose_cfg/datasource-compose.yaml b/.docker-compose_cfg/datasource-compose.yaml index ef9824eb0..2be73952d 100644 --- a/.docker-compose_cfg/datasource-compose.yaml +++ b/.docker-compose_cfg/datasource-compose.yaml @@ -1,3 +1,17 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + #more datasource-compose.yaml apiVersion: 1 diff --git a/.docker-compose_cfg/prometheus-compose.yml b/.docker-compose_cfg/prometheus-compose.yml index 55a439644..37a8c547e 100644 --- a/.docker-compose_cfg/prometheus-compose.yml +++ b/.docker-compose_cfg/prometheus-compose.yml @@ -1,3 +1,17 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + #more prometheus-compose.yml global: scrape_interval: 15s diff --git a/scripts/make-rules/common.mk b/scripts/make-rules/common.mk index 72b395e28..5019ebc77 100644 --- a/scripts/make-rules/common.mk +++ b/scripts/make-rules/common.mk @@ -116,7 +116,7 @@ FIND := find . ! -path './utils/*' ! -path './vendor/*' XARGS := xargs -r # Linux command settings-CODE DIRS Copyright -CODE_DIRS := $(ROOT_DIR)/pkg $(ROOT_DIR)/cmd $(ROOT_DIR)/config $(ROOT_DIR)/db $(ROOT_DIR)/deploy $(ROOT_DIR)/deploy_k8s $(ROOT_DIR)/docker-compose_cfg $(ROOT_DIR)/internal $(ROOT_DIR)/scripts $(ROOT_DIR)/test +CODE_DIRS := $(ROOT_DIR)/pkg $(ROOT_DIR)/cmd $(ROOT_DIR)/config $(ROOT_DIR)/db $(ROOT_DIR)/.docker-compose_cfg $(ROOT_DIR)/internal $(ROOT_DIR)/scripts $(ROOT_DIR)/test FINDS := find $(CODE_DIRS) # Makefile settings: Select different behaviors by determining whether V option is set diff --git a/scripts/make-rules/golang.mk b/scripts/make-rules/golang.mk index 03be95a91..81ff49f14 100644 --- a/scripts/make-rules/golang.mk +++ b/scripts/make-rules/golang.mk @@ -65,7 +65,7 @@ ifeq (${BINS},) endif # TODO: EXCLUDE_TESTS variable, which contains the name of the package to be excluded from the test -EXCLUDE_TESTS=github.com/OpenIMSDK/Open-IM-Server/test github.com/OpenIMSDK/Open-IM-Server/pkg/log github.com/OpenIMSDK/Open-IM-Server/db github.com/OpenIMSDK/Open-IM-Server/scripts github.com/OpenIMSDK/Open-IM-Server/deploy_k8s github.com/OpenIMSDK/Open-IM-Server/deploy github.com/OpenIMSDK/Open-IM-Server/config +EXCLUDE_TESTS=github.com/OpenIMSDK/Open-IM-Server/test github.com/OpenIMSDK/Open-IM-Server/pkg/log github.com/OpenIMSDK/Open-IM-Server/db github.com/OpenIMSDK/Open-IM-Server/scripts github.com/OpenIMSDK/Open-IM-Server/config # ============================================================================== # ❯ tree -L 1 cmd From 02a24aebfe3dd4ab370561503bcb5b6ac1daa1ff Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Tue, 4 Jul 2023 20:18:39 +0800 Subject: [PATCH 20/73] style: migrate image locations Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- Makefile | 11 +++++++ README.md | 6 ++-- docs/{ => images}/Architecture.jpg | Bin .../Open-IM-Servers-on-System.png | Bin .../Open-IM-Servers-on-docker.png | Bin docs/{ => images}/Open-IM.png | Bin docs/{ => images}/Wechat.jpg | Bin docs/{ => images}/open-im-logo.png | Bin docs/{ => images}/open-im-server.png | Bin scripts/make-rules/golang.mk | 2 +- scripts/make-rules/swagger.mk | 11 +++++-- scripts/make-rules/tools.mk | 5 ++++ scripts/start_all.sh | 27 +++++++++--------- 13 files changed, 43 insertions(+), 19 deletions(-) rename docs/{ => images}/Architecture.jpg (100%) rename docs/{ => images}/Open-IM-Servers-on-System.png (100%) rename docs/{ => images}/Open-IM-Servers-on-docker.png (100%) rename docs/{ => images}/Open-IM.png (100%) rename docs/{ => images}/Wechat.jpg (100%) rename docs/{ => images}/open-im-logo.png (100%) rename docs/{ => images}/open-im-server.png (100%) diff --git a/Makefile b/Makefile index c210edbe3..ceffba205 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,7 @@ include scripts/make-rules/gen.mk include scripts/make-rules/dependencies.mk include scripts/make-rules/tools.mk include scripts/make-rules/release.mk +include scripts/make-rules/swagger.mk # ============================================================================== # Usage @@ -148,6 +149,16 @@ tools: gen: @$(MAKE) gen.run +## swagger: Generate swagger document. +.PHONY: swagger +swagger: + @$(MAKE) swagger.run + +## serve-swagger: Serve swagger spec and docs. +.PHONY: swagger.serve +serve-swagger: + @$(MAKE) swagger.serve + ## verify-copyright: Verify the license headers for all files. .PHONY: verify-copyright verify-copyright: diff --git a/README.md b/README.md index 8f2713ccb..d3d880d41 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ By deployment of the Open-IM-Server on the customer's server, developers can int ./docker_check_service.sh./check_all.sh ``` - ![OpenIMServersonSystempng](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/Open-IM-Servers-on-System.png) + ![OpenIMServersonSystempng](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/images/Open-IM-Servers-on-System.png) #### Deploy using source code @@ -157,11 +157,11 @@ all services build success ## Authentication Clow Chart -![avatar](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/open-im-server.png) +![avatar](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/images/open-im-server.png) ## Architecture -![avatar](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/Architecture.jpg) +![avatar](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/images/Architecture.jpg) ## To start developing OpenIM The [community repository](https://github.com/OpenIMSDK/community) hosts all information about building Kubernetes from source, how to contribute code and documentation, who to contact about what, etc. diff --git a/docs/Architecture.jpg b/docs/images/Architecture.jpg similarity index 100% rename from docs/Architecture.jpg rename to docs/images/Architecture.jpg diff --git a/docs/Open-IM-Servers-on-System.png b/docs/images/Open-IM-Servers-on-System.png similarity index 100% rename from docs/Open-IM-Servers-on-System.png rename to docs/images/Open-IM-Servers-on-System.png diff --git a/docs/Open-IM-Servers-on-docker.png b/docs/images/Open-IM-Servers-on-docker.png similarity index 100% rename from docs/Open-IM-Servers-on-docker.png rename to docs/images/Open-IM-Servers-on-docker.png diff --git a/docs/Open-IM.png b/docs/images/Open-IM.png similarity index 100% rename from docs/Open-IM.png rename to docs/images/Open-IM.png diff --git a/docs/Wechat.jpg b/docs/images/Wechat.jpg similarity index 100% rename from docs/Wechat.jpg rename to docs/images/Wechat.jpg diff --git a/docs/open-im-logo.png b/docs/images/open-im-logo.png similarity index 100% rename from docs/open-im-logo.png rename to docs/images/open-im-logo.png diff --git a/docs/open-im-server.png b/docs/images/open-im-server.png similarity index 100% rename from docs/open-im-server.png rename to docs/images/open-im-server.png diff --git a/scripts/make-rules/golang.mk b/scripts/make-rules/golang.mk index 81ff49f14..de1e6b74e 100644 --- a/scripts/make-rules/golang.mk +++ b/scripts/make-rules/golang.mk @@ -141,6 +141,7 @@ go.test: ## go.test.junit-report: Run unit test .PHONY: go.test.junit-report go.test.junit-report: tools.verify.go-junit-report + @touch $(TMP_DIR)/coverage.out @echo "===========> Run unit test > $(TMP_DIR)/report.xml" # @$(GO) test -v -coverprofile=$(TMP_DIR)/coverage.out 2>&1 $(GO_BUILD_FLAGS) ./... | $(TOOLS_DIR)/go-junit-report -set-exit-code > $(TMP_DIR)/report.xml @$(GO) test -v -coverprofile=$(TMP_DIR)/coverage.out 2>&1 ./... | $(TOOLS_DIR)/go-junit-report -set-exit-code > $(TMP_DIR)/report.xml @@ -151,7 +152,6 @@ go.test.junit-report: tools.verify.go-junit-report ## go.test.cover: Run unit test with coverage .PHONY: go.test.cover go.test.cover: go.test.junit-report - @touch $(TMP_DIR)/coverage.out @$(GO) tool cover -func=$(TMP_DIR)/coverage.out | \ awk -v target=$(COVERAGE) -f $(ROOT_DIR)/scripts/coverage.awk diff --git a/scripts/make-rules/swagger.mk b/scripts/make-rules/swagger.mk index 0598813c3..991de43c6 100644 --- a/scripts/make-rules/swagger.mk +++ b/scripts/make-rules/swagger.mk @@ -2,11 +2,18 @@ # Makefile helper functions for swagger # +## swagger.run: Generate swagger document. .PHONY: swagger.run swagger.run: tools.verify.swagger @echo "===========> Generating swagger API docs" - @swagger generate spec --scan-models -w $(ROOT_DIR)/cmd/genswaggertypedocs -o $(ROOT_DIR)/api/swagger/swagger.yaml + @$(TOOLS_DIR)/swagger generate spec --scan-models -w $(ROOT_DIR)/cmd/genswaggertypedocs -o $(ROOT_DIR)/api/swagger/swagger.yaml +## swagger.serve: Serve swagger spec and docs. .PHONY: swagger.serve swagger.serve: tools.verify.swagger - @swagger serve -F=redoc --no-open --port 36666 $(ROOT_DIR)/api/swagger/swagger.yaml + @$(TOOLS_DIR)/swagger serve -F=redoc --no-open --port 36666 $(ROOT_DIR)/api/swagger/swagger.yaml + +## swagger.help: Display help information about the release package +.PHONY: swagger.help +swagger.help: scripts/make-rules/swagger.mk + $(call smallhelp) \ No newline at end of file diff --git a/scripts/make-rules/tools.mk b/scripts/make-rules/tools.mk index 468cf109f..e268cb264 100644 --- a/scripts/make-rules/tools.mk +++ b/scripts/make-rules/tools.mk @@ -101,6 +101,11 @@ install.go-gitlint: install.go-junit-report: @$(GO) install github.com/jstemmer/go-junit-report@latest +## install.gotests: Install gotests, used to generate go tests +.PHONY: install.swagger +install.swagger: + @$(GO) install github.com/go-swagger/go-swagger/cmd/swagger@latest + # ============================================================================== # Tools that might be used include go gvm # diff --git a/scripts/start_all.sh b/scripts/start_all.sh index eba94c4b7..1a86db7f3 100755 --- a/scripts/start_all.sh +++ b/scripts/start_all.sh @@ -13,10 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -#fixme This scripts is the total startup scripts -#fixme The full name of the shell scripts that needs to be started is placed in the need_to_start_server_shell array +#FIXME This script is the startup script for multiple servers. +#FIXME The full names of the shell scripts that need to be started are placed in the `need_to_start_server_shell` array. -#fixme Put the shell scripts name here +#FIXME Put the shell script names here need_to_start_server_shell=( start_rpc_service.sh push_start.sh @@ -25,20 +25,21 @@ need_to_start_server_shell=( msg_gateway_start.sh start_cron.sh ) + time=`date +"%Y-%m-%d %H:%M:%S"` -echo "==========================================================">>../logs/openIM.log 2>&1 & -echo "==========================================================">>../logs/openIM.log 2>&1 & -echo "==========================================================">>../logs/openIM.log 2>&1 & -echo "==========server start time:${time}===========">>../logs/openIM.log 2>&1 & -echo "==========================================================">>../logs/openIM.log 2>&1 & -echo "==========================================================">>../logs/openIM.log 2>&1 & -echo "==========================================================">>../logs/openIM.log 2>&1 & +echo "==========================================================" >> ../logs/openIM.log 2>&1 & +echo "==========================================================" >> ../logs/openIM.log 2>&1 & +echo "==========================================================" >> ../logs/openIM.log 2>&1 & +echo "==========server start time:${time}===========" >> ../logs/openIM.log 2>&1 & +echo "==========================================================" >> ../logs/openIM.log 2>&1 & +echo "==========================================================" >> ../logs/openIM.log 2>&1 & +echo "==========================================================" >> ../logs/openIM.log 2>&1 & for i in ${need_to_start_server_shell[*]}; do chmod +x $i - echo "=====================exec ${i}======================">>../logs/openIM.log + echo "=====================exec ${i}======================" >> ../logs/openIM.log ./$i - if [ $? -ne 0 ]; then - exit -1 + if [ $? -ne 0 ]; then + exit -1 fi done From 57fe05631617cd71484c9b503b8e1b39e0abe731 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Tue, 4 Jul 2023 20:22:26 +0800 Subject: [PATCH 21/73] feat: add deployments readme file Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- deployments/README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 deployments/README.md diff --git a/deployments/README.md b/deployments/README.md new file mode 100644 index 000000000..9c3cff05e --- /dev/null +++ b/deployments/README.md @@ -0,0 +1,26 @@ +# OpenIM 应用容器化部署指南 + +OpenIM 支持很多种集群化部署方式,包括但不限于 helm, sealos, kubeam, kubesphere, kubeflow, kuboard, kubespray, k3s, k3d, k3c, k3sup, k3v, k3x + +### 依赖检查 + +```bash +Kubernetes: >= 1.16.0-0 +Helm: >= 3.0 +``` + +假设 OpenIM 项目根目录路径为 `OpenIM_ROOT` + +进入 OpenIM 项目根目录 + +$ cd ${OpenIM_ROOT} + + + +### 容器化安装 + +具体安装步骤如下: + + + +### Helm安装 \ No newline at end of file From 1158de9f35133715a4044fcbcc1cab84d79e10b5 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Tue, 4 Jul 2023 22:16:20 +0800 Subject: [PATCH 22/73] fix: details optimize Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- .gitmodules | 4 ---- install_guide.sh | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) delete mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index be3279250..000000000 --- a/.gitmodules +++ /dev/null @@ -1,4 +0,0 @@ - -[submodule "cmd/Open-IM-SDK-Core"] - path = cmd/Open-IM-SDK-Core - url = https://github.com/OpenIMSDK/Open-IM-SDK-Core.git diff --git a/install_guide.sh b/install_guide.sh index 57fad73dd..1c5f06fb9 100644 --- a/install_guide.sh +++ b/install_guide.sh @@ -43,7 +43,7 @@ edit_config() { vi config/config.yaml ;; 2) - echo "do not edit config" + echo "do not edit config" ;; esac } @@ -174,4 +174,3 @@ case $choice in echo "Invalid option, please try again." ;; esac - From 98fe83b62d88618781cc2b4dee3b193296e277e4 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Wed, 5 Jul 2023 10:21:38 +0800 Subject: [PATCH 23/73] feat: add make Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ceffba205..dffb32ae4 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,8 @@ Options: PLATFORMS Platform to build for. Default is linux_arm64 and linux_amd64. This option is available when using: make {build}.multiarch - Example: make build.multiarch PLATFORMS="linux_arm64 linux_amd64". + Example: make build.multiarch PLATFORMS="linux_s390x linux_mips64 + linux_mips64le darwin_amd64 windows_amd64 linux_amd64 linux_arm64". V Set to 1 enable verbose build. Default is 0. endef From fe16385c9b76b814383fa0d93801867ad14e65f8 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Wed, 5 Jul 2023 15:26:07 +0800 Subject: [PATCH 24/73] fix: rename open_im to openim Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- .gitignore | 22 +++++++------- CHANGELOG/CHANGELOG-0.0.0.md | 5 ++-- Makefile | 2 +- cmd/openim_api/Makefile | 2 +- cmd/openim_api/deploy.Dockerfile | 4 +-- cmd/openim_cmdutils/Makefile | 2 +- cmd/openim_crontask/Makefile | 2 +- cmd/openim_crontask/deploy.Dockerfile | 4 +-- cmd/openim_msggateway/Makefile | 3 +- cmd/openim_msggateway/deploy.Dockerfile | 4 +-- cmd/openim_msgtransfer/Makefile | 2 +- cmd/openim_msgtransfer/deploy.Dockerfile | 4 +-- cmd/openim_push/Makefile | 2 +- cmd/openim_push/deploy.Dockerfile | 4 +-- cmd/openim_rpc/openim_rpc_auth/Makefile | 2 +- .../openim_rpc_auth/deploy.Dockerfile | 4 +-- .../openim_rpc_conversation/Makefile | 2 +- .../openim_rpc_conversation/deploy.Dockerfile | 4 +-- cmd/openim_rpc/openim_rpc_friend/Makefile | 4 +-- .../openim_rpc_friend/deploy.Dockerfile | 4 +-- cmd/openim_rpc/openim_rpc_group/Makefile | 7 +---- .../openim_rpc_group/deploy.Dockerfile | 4 +-- cmd/openim_rpc/openim_rpc_msg/Makefile | 4 +-- .../openim_rpc_msg/deploy.Dockerfile | 4 +-- cmd/openim_rpc/openim_rpc_third/Makefile | 7 +---- .../openim_rpc_third/deploy.Dockerfile | 4 +-- cmd/openim_rpc/openim_rpc_user/Makefile | 7 +---- .../openim_rpc_user/deploy.Dockerfile | 4 +-- docker-compose.yaml | 10 +++---- scripts/build_images.sh | 2 +- scripts/check_all.sh | 2 +- scripts/enterprise/check_all.sh | 3 +- scripts/enterprise/path_info.cfg | 10 +++---- scripts/make-rules/golang.mk | 18 +++++------ scripts/path_info.cfg | 30 +++++++++---------- scripts/start_rpc_service.sh | 14 ++++----- 36 files changed, 95 insertions(+), 117 deletions(-) diff --git a/.gitignore b/.gitignore index 17e4853a3..9cea0d86f 100644 --- a/.gitignore +++ b/.gitignore @@ -30,17 +30,17 @@ output/ _output/ ### OpenIM deploy ### -deploy/open_im_demo -deploy/open_im_api -deploy/open_im_msg_gateway -deploy/open_im_msg_transfer -deploy/open_im_push -deploy/open_im_timer_task -deploy/open_im_rpc_user -deploy/open_im_rpc_friend -deploy/open_im_rpc_group -deploy/open_im_rpc_msg -deploy/open_im_rpc_auth +deploy/openim_demo +deploy/openim_api +deploy/openim_msg_gateway +deploy/openim_msg_transfer +deploy/openim_push +deploy/openim_timer_task +deploy/openim_rpc_user +deploy/openim_rpc_friend +deploy/openim_rpc_group +deploy/openim_rpc_msg +deploy/openim_rpc_auth deploy/Open-IM-SDK-Core # files used by the developer diff --git a/CHANGELOG/CHANGELOG-0.0.0.md b/CHANGELOG/CHANGELOG-0.0.0.md index ab21e1729..65a0ab1b1 100644 --- a/CHANGELOG/CHANGELOG-0.0.0.md +++ b/CHANGELOG/CHANGELOG-0.0.0.md @@ -142,7 +142,7 @@ ### Pb -* open_im_sdk.OfflinePushInfo +* openim_sdk.OfflinePushInfo ### Pull Requests @@ -162,7 +162,7 @@ ### Pb -* open_im_sdk.OfflinePushInfo +* openim_sdk.OfflinePushInfo @@ -225,4 +225,3 @@ * Merge pull request [#7](https://github.com/OpenIMSDK/Open-IM-Server/issues/7) from memory-qianxiao/docker-compose_update * Merge pull request [#4](https://github.com/OpenIMSDK/Open-IM-Server/issues/4) from wujingke/patch-1 - diff --git a/Makefile b/Makefile index dffb32ae4..7c59f6027 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ Options: BINS Binaries to build. Default is all binaries under cmd. This option is available when using: make {build}(.multiarch) - Example: make build BINS="open_im_api open_im_cms_api". + Example: make build BINS="openim_api openim_cms_api". PLATFORMS Platform to build for. Default is linux_arm64 and linux_amd64. This option is available when using: make {build}.multiarch diff --git a/cmd/openim_api/Makefile b/cmd/openim_api/Makefile index 480c9c298..4fe47f51f 100644 --- a/cmd/openim_api/Makefile +++ b/cmd/openim_api/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=open_im_api +NAME=openim_api BIN_DIR=../../bin/ OS:= $(or $(os),linux) diff --git a/cmd/openim_api/deploy.Dockerfile b/cmd/openim_api/deploy.Dockerfile index a236c02f0..24a17af37 100644 --- a/cmd/openim_api/deploy.Dockerfile +++ b/cmd/openim_api/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./open_im_api ./ +COPY ./openim_api ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./open_im_api","--port", "10002"] +CMD ["./openim_api","--port", "10002"] diff --git a/cmd/openim_cmdutils/Makefile b/cmd/openim_cmdutils/Makefile index 1ed0829fd..9c51aee84 100644 --- a/cmd/openim_cmdutils/Makefile +++ b/cmd/openim_cmdutils/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=open_im_cmd_utils +NAME=openim_cmd_utils BIN_DIR=../../bin/ OS:= $(or $(os),linux) diff --git a/cmd/openim_crontask/Makefile b/cmd/openim_crontask/Makefile index 4103d7cd5..1f09bf396 100644 --- a/cmd/openim_crontask/Makefile +++ b/cmd/openim_crontask/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=open_im_cron_task +NAME=openim_cron_task BIN_DIR=../../bin/ OS:= $(or $(os),linux) diff --git a/cmd/openim_crontask/deploy.Dockerfile b/cmd/openim_crontask/deploy.Dockerfile index e80006def..07d328474 100644 --- a/cmd/openim_crontask/deploy.Dockerfile +++ b/cmd/openim_crontask/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./open_im_cron_task ./ +COPY ./openim_cron_task ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./open_im_cron_task"] +CMD ["./openim_cron_task"] diff --git a/cmd/openim_msggateway/Makefile b/cmd/openim_msggateway/Makefile index 0bb3b41f5..07cad40a9 100644 --- a/cmd/openim_msggateway/Makefile +++ b/cmd/openim_msggateway/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=open_im_msg_gateway +NAME=openim_msg_gateway BIN_DIR=../../bin/ OS:= $(or $(os),linux) @@ -32,4 +32,3 @@ install:build clean: @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi - diff --git a/cmd/openim_msggateway/deploy.Dockerfile b/cmd/openim_msggateway/deploy.Dockerfile index 7310fe0dc..9ecc83e95 100644 --- a/cmd/openim_msggateway/deploy.Dockerfile +++ b/cmd/openim_msggateway/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./open_im_msg_gateway ./ +COPY ./openim_msg_gateway ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./open_im_msg_gateway","--port", "10140" "--ws_port", "10001", "--prometheus_port", "20240"] +CMD ["./openim_msg_gateway","--port", "10140" "--ws_port", "10001", "--prometheus_port", "20240"] diff --git a/cmd/openim_msgtransfer/Makefile b/cmd/openim_msgtransfer/Makefile index ca6355aa4..dc22834de 100644 --- a/cmd/openim_msgtransfer/Makefile +++ b/cmd/openim_msgtransfer/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=open_im_msg_transfer +NAME=openim_msg_transfer BIN_DIR=../../bin/ OS:= $(or $(os),linux) diff --git a/cmd/openim_msgtransfer/deploy.Dockerfile b/cmd/openim_msgtransfer/deploy.Dockerfile index 0c9485694..91767adf4 100644 --- a/cmd/openim_msgtransfer/deploy.Dockerfile +++ b/cmd/openim_msgtransfer/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./open_im_msg_transfer ./ +COPY ./openim_msg_transfer ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./open_im_msg_transfer","--prometheus_port", "21400"] +CMD ["./openim_msg_transfer","--prometheus_port", "21400"] diff --git a/cmd/openim_push/Makefile b/cmd/openim_push/Makefile index 064dd2df1..c67882553 100644 --- a/cmd/openim_push/Makefile +++ b/cmd/openim_push/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=open_im_push +NAME=openim_push BIN_DIR=../../bin/ OS:= $(or $(os),linux) diff --git a/cmd/openim_push/deploy.Dockerfile b/cmd/openim_push/deploy.Dockerfile index 79b8cb4f1..9b9a542c3 100644 --- a/cmd/openim_push/deploy.Dockerfile +++ b/cmd/openim_push/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./open_im_push ./ +COPY ./openim_push ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./open_im_push", "--port", "10170", "--prometheus_port", "20170"] +CMD ["./openim_push", "--port", "10170", "--prometheus_port", "20170"] diff --git a/cmd/openim_rpc/openim_rpc_auth/Makefile b/cmd/openim_rpc/openim_rpc_auth/Makefile index 4c3f622a7..f702786ae 100644 --- a/cmd/openim_rpc/openim_rpc_auth/Makefile +++ b/cmd/openim_rpc/openim_rpc_auth/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=open_im_auth +NAME=openim_auth BIN_DIR=../../../bin/ OS:= $(or $(os),linux) diff --git a/cmd/openim_rpc/openim_rpc_auth/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_auth/deploy.Dockerfile index a7b69cc0a..1fc1682dd 100644 --- a/cmd/openim_rpc/openim_rpc_auth/deploy.Dockerfile +++ b/cmd/openim_rpc/openim_rpc_auth/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./open_im_auth ./ +COPY ./openim_auth ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./open_im_auth", "--port", "10160"] +CMD ["./openim_auth", "--port", "10160"] diff --git a/cmd/openim_rpc/openim_rpc_conversation/Makefile b/cmd/openim_rpc/openim_rpc_conversation/Makefile index 833e9f651..fa973060e 100644 --- a/cmd/openim_rpc/openim_rpc_conversation/Makefile +++ b/cmd/openim_rpc/openim_rpc_conversation/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=open_im_conversation +NAME=openim_conversation BIN_DIR=../../../bin/ OS:= $(or $(os),linux) diff --git a/cmd/openim_rpc/openim_rpc_conversation/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_conversation/deploy.Dockerfile index 4e7591499..5aab79104 100644 --- a/cmd/openim_rpc/openim_rpc_conversation/deploy.Dockerfile +++ b/cmd/openim_rpc/openim_rpc_conversation/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./open_im_conversation ./ +COPY ./openim_conversation ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./open_im_conversation", "--port", "10230", "--prometheus_port","20230"] +CMD ["./openim_conversation", "--port", "10230", "--prometheus_port","20230"] diff --git a/cmd/openim_rpc/openim_rpc_friend/Makefile b/cmd/openim_rpc/openim_rpc_friend/Makefile index bf04b74e1..8cbb39c2b 100644 --- a/cmd/openim_rpc/openim_rpc_friend/Makefile +++ b/cmd/openim_rpc/openim_rpc_friend/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=open_im_friend +NAME=openim_friend BIN_DIR=../../../bin/ OS:= $(or $(os),linux) @@ -32,5 +32,3 @@ install:build clean: @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi - - diff --git a/cmd/openim_rpc/openim_rpc_friend/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_friend/deploy.Dockerfile index d159ee0dd..3aaf86885 100644 --- a/cmd/openim_rpc/openim_rpc_friend/deploy.Dockerfile +++ b/cmd/openim_rpc/openim_rpc_friend/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./open_im_friend ./ +COPY ./openim_friend ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./open_im_friend", "--port", "10120", "--prometheus_port","20120"] +CMD ["./openim_friend", "--port", "10120", "--prometheus_port","20120"] diff --git a/cmd/openim_rpc/openim_rpc_group/Makefile b/cmd/openim_rpc/openim_rpc_group/Makefile index 123d50945..b33dbb259 100644 --- a/cmd/openim_rpc/openim_rpc_group/Makefile +++ b/cmd/openim_rpc/openim_rpc_group/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=open_im_group +NAME=openim_group BIN_DIR=../../../bin/ OS:= $(or $(os),linux) @@ -32,8 +32,3 @@ install:build clean: @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi - - - - - diff --git a/cmd/openim_rpc/openim_rpc_group/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_group/deploy.Dockerfile index e676708d5..1adb00bc5 100644 --- a/cmd/openim_rpc/openim_rpc_group/deploy.Dockerfile +++ b/cmd/openim_rpc/openim_rpc_group/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./open_im_group ./ +COPY ./openim_group ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./open_im_group", "--port", "10150", "--prometheus_port","20150"] +CMD ["./openim_group", "--port", "10150", "--prometheus_port","20150"] diff --git a/cmd/openim_rpc/openim_rpc_msg/Makefile b/cmd/openim_rpc/openim_rpc_msg/Makefile index 6270183f8..b27c5c420 100644 --- a/cmd/openim_rpc/openim_rpc_msg/Makefile +++ b/cmd/openim_rpc/openim_rpc_msg/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=open_im_msg +NAME=openim_msg BIN_DIR=../../../bin/ OS:= $(or $(os),linux) @@ -32,5 +32,3 @@ install:build clean: @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi - - diff --git a/cmd/openim_rpc/openim_rpc_msg/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_msg/deploy.Dockerfile index 8e6307ccb..10fd29f0a 100644 --- a/cmd/openim_rpc/openim_rpc_msg/deploy.Dockerfile +++ b/cmd/openim_rpc/openim_rpc_msg/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./open_im_msg ./ +COPY ./openim_msg ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./open_im_msg", "--port", "10130", "--prometheus_port","20130"] +CMD ["./openim_msg", "--port", "10130", "--prometheus_port","20130"] diff --git a/cmd/openim_rpc/openim_rpc_third/Makefile b/cmd/openim_rpc/openim_rpc_third/Makefile index fe44c27ef..b5a7c546a 100644 --- a/cmd/openim_rpc/openim_rpc_third/Makefile +++ b/cmd/openim_rpc/openim_rpc_third/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=open_im_third +NAME=openim_third BIN_DIR=../../../bin/ OS:= $(or $(os),linux) @@ -32,8 +32,3 @@ install:build clean: @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi - - - - - diff --git a/cmd/openim_rpc/openim_rpc_third/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_third/deploy.Dockerfile index 462798b00..7b6a1dfaf 100644 --- a/cmd/openim_rpc/openim_rpc_third/deploy.Dockerfile +++ b/cmd/openim_rpc/openim_rpc_third/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./open_im_third ./ +COPY ./openim_third ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./open_im_third", "--port", "10200"] +CMD ["./openim_third", "--port", "10200"] diff --git a/cmd/openim_rpc/openim_rpc_user/Makefile b/cmd/openim_rpc/openim_rpc_user/Makefile index 9c7f03d5d..aef2d605a 100644 --- a/cmd/openim_rpc/openim_rpc_user/Makefile +++ b/cmd/openim_rpc/openim_rpc_user/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=open_im_user +NAME=openim_user BIN_DIR=../../../bin/ OS:= $(or $(os),linux) @@ -32,8 +32,3 @@ install:build clean: @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi - - - - - diff --git a/cmd/openim_rpc/openim_rpc_user/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_user/deploy.Dockerfile index 047698788..0977dd634 100644 --- a/cmd/openim_rpc/openim_rpc_user/deploy.Dockerfile +++ b/cmd/openim_rpc/openim_rpc_user/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./open_im_user ./ +COPY ./openim_user ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./open_im_user", "--port", "10110"] +CMD ["./openim_user", "--port", "10110"] diff --git a/docker-compose.yaml b/docker-compose.yaml index cbf361de1..db3ba4bcc 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -99,9 +99,9 @@ services: command: minio server /data --console-address ':9090' - open_im_server: - image: openim/open_im_server:v3.0.1 - container_name: open_im_server + openim_server: + image: openim/openim_server:v3.0.1 + container_name: openim_server volumes: - ./logs:/Open-IM-Server/logs - ./config/config.yaml:/Open-IM-Server/config/config.yaml @@ -131,7 +131,7 @@ services: - mongodb - redis - minio - - open_im_server + - openim_server network_mode: "host" logging: driver: json-file @@ -147,7 +147,7 @@ services: # ports: # - 9091:9091 depends_on: - - open_im_server + - openim_server command: --web.listen-address=:9091 --config.file="/etc/prometheus/prometheus.yml" network_mode: "host" diff --git a/scripts/build_images.sh b/scripts/build_images.sh index ad47cdb24..a94bc91d6 100755 --- a/scripts/build_images.sh +++ b/scripts/build_images.sh @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -image=openim/open_im_server:v1.0.5 +image=openim/openim_server:v1.0.5 rm Open-IM-Server -rf git clone https://github.com/OpenIMSDK/Open-IM-Server.git --recursive cd Open-IM-Server diff --git a/scripts/check_all.sh b/scripts/check_all.sh index 16fec32ff..15507102a 100755 --- a/scripts/check_all.sh +++ b/scripts/check_all.sh @@ -34,7 +34,7 @@ for i in ${service_port_name[*]}; do list=$(cat $config_path | grep -w ${i} | awk -F '[:]' '{print $NF}') list_to_string $list for j in ${ports_array}; do - port=$(ss -tunlp| grep open_im | awk '{print $5}' | grep -w ${j} | awk -F '[:]' '{print $NF}') + port=$(ss -tunlp| grep openim | awk '{print $5}' | grep -w ${j} | awk -F '[:]' '{print $NF}') if [[ ${port} -ne ${j} ]]; then echo -e ${YELLOW_PREFIX}${i}${COLOR_SUFFIX}${RED_PREFIX}" service does not start normally,not initiated port is "${COLOR_SUFFIX}${YELLOW_PREFIX}${j}${COLOR_SUFFIX} echo -e ${RED_PREFIX}"please check ../logs/openIM.log "${COLOR_SUFFIX} diff --git a/scripts/enterprise/check_all.sh b/scripts/enterprise/check_all.sh index f439404f1..893e1bf32 100755 --- a/scripts/enterprise/check_all.sh +++ b/scripts/enterprise/check_all.sh @@ -29,7 +29,7 @@ for i in ${service_port_name[*]}; do list=$(cat $config_path | grep -w ${i} | awk -F '[:]' '{print $NF}') list_to_string $list for j in ${ports_array}; do - port=$(ss -tunlp| grep open_im | awk '{print $5}' | grep -w ${j} | awk -F '[:]' '{print $NF}') + port=$(ss -tunlp| grep openim | awk '{print $5}' | grep -w ${j} | awk -F '[:]' '{print $NF}') if [[ ${port} -ne ${j} ]]; then echo -e ${YELLOW_PREFIX}${i}${COLOR_SUFFIX}${RED_PREFIX}" service does not start normally,not initiated port is "${COLOR_SUFFIX}${YELLOW_PREFIX}${j}${COLOR_SUFFIX} echo -e ${RED_PREFIX}"please check ../logs/openIM.log "${COLOR_SUFFIX} @@ -39,4 +39,3 @@ for i in ${service_port_name[*]}; do fi done done - diff --git a/scripts/enterprise/path_info.cfg b/scripts/enterprise/path_info.cfg index 7106ac5d5..29d3112c1 100644 --- a/scripts/enterprise/path_info.cfg +++ b/scripts/enterprise/path_info.cfg @@ -1,7 +1,7 @@ #Don't put the space between "=" -demo_server_name="open_im_chat_api" +demo_server_name="openim_chat_api" demo_server_binary_root="../bin/" @@ -21,9 +21,9 @@ service_source_root=( #service filename service_names=( #api service filename - open_im_chat_api - open_im_admin_api + openim_chat_api + openim_admin_api #rpc service filename - open_im_admin - open_im_chat + openim_admin + openim_chat ) diff --git a/scripts/make-rules/golang.mk b/scripts/make-rules/golang.mk index de1e6b74e..f0042e043 100644 --- a/scripts/make-rules/golang.mk +++ b/scripts/make-rules/golang.mk @@ -71,20 +71,20 @@ EXCLUDE_TESTS=github.com/OpenIMSDK/Open-IM-Server/test github.com/OpenIMSDK/Open # ❯ tree -L 1 cmd # cmd # ├── openim-sdk-core/ - main.go -# ├── open_im_api -# ├── open_im_cms_api -# ├── open_im_cron_task -# ├── open_im_demo -# ├── open_im_msg_gateway -# ├── open_im_msg_transfer -# ├── open_im_push -# ├── rpc/open_im_admin_cms/ - main.go +# ├── openim_api +# ├── openim_cms_api +# ├── openim_cron_task +# ├── openim_demo +# ├── openim_msg_gateway +# ├── openim_msg_transfer +# ├── openim_push +# ├── rpc/openim_admin_cms/ - main.go # └── test/ - main.go # COMMAND=openim # PLATFORM=linux_amd64 # OS=linux # ARCH=amd64 -# BINS=open_im_api open_im_cms_api open_im_cron_task open_im_demo open_im_msg_gateway open_im_msg_transfer open_im_push +# BINS=openim_api openim_cms_api openim_cron_task openim_demo openim_msg_gateway openim_msg_transfer openim_push # BIN_DIR=/root/workspaces/OpenIM/_output/bin # ============================================================================== diff --git a/scripts/path_info.cfg b/scripts/path_info.cfg index f406d11fe..530455f36 100644 --- a/scripts/path_info.cfg +++ b/scripts/path_info.cfg @@ -1,35 +1,35 @@ #Don't put the space between "=" -msg_gateway_name="open_im_msg_gateway" +msg_gateway_name="openim_msg_gateway" msg_gateway_binary_root="../bin/" msg_gateway_source_root="../cmd/msggateway/" -msg_name="open_im_msg" +msg_name="openim_msg" msg_binary_root="../bin/" msg_source_root="../cmd/rpc/msg/" -push_name="open_im_push" +push_name="openim_push" push_binary_root="../bin/" push_source_root="../cmd/push/" -msg_transfer_name="open_im_msg_transfer" +msg_transfer_name="openim_msg_transfer" msg_transfer_binary_root="../bin/" msg_transfer_source_root="../cmd/msgtransfer/" msg_transfer_service_num=4 -sdk_server_name="open_im_sdk_server" +sdk_server_name="openim_sdk_server" sdk_server_binary_root="../bin/" sdk_server_source_root="../cmd/Open-IM-SDK-Core/" -cron_task_name="open_im_cron_task" +cron_task_name="openim_cron_task" cron_task_binary_root="../bin/" cron_task_source_root="../cmd/crontask/" -cmd_utils_name="open_im_cmd_utils" +cmd_utils_name="openim_cmd_utils" cmd_utils_binary_root="../bin/" cmd_utils_source_root="../cmd/cmduitls/" @@ -57,15 +57,15 @@ service_source_root=( #service filename service_names=( #api service filename - open_im_api + openim_api #rpc service filename - open_im_user - open_im_friend - open_im_group - open_im_auth - open_im_conversation - open_im_third - open_im_cron_task + openim_user + openim_friend + openim_group + openim_auth + openim_conversation + openim_third + openim_cron_task ${msg_gateway_name} ${msg_transfer_name} ${msg_name} diff --git a/scripts/start_rpc_service.sh b/scripts/start_rpc_service.sh index f6bafe58b..d0c2f721d 100755 --- a/scripts/start_rpc_service.sh +++ b/scripts/start_rpc_service.sh @@ -21,15 +21,15 @@ source ./function.sh #service filename service_filename=( #api - open_im_api + openim_api #rpc - open_im_user - open_im_friend - open_im_group - open_im_auth + openim_user + openim_friend + openim_group + openim_auth ${msg_name} - open_im_conversation - open_im_third + openim_conversation + openim_third ) #service config port name From 80a4e35c0f71abacb1fa649529cb61fd1c37c7ce Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Wed, 5 Jul 2023 15:32:47 +0800 Subject: [PATCH 25/73] fix: remove test file Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- test/mongo/cmd/main.go | 61 -------------------------------- test/mongo/mongo_utils.go | 74 --------------------------------------- test/mysql/cmd/main.go | 23 ------------ test/mysql/importuser.go | 70 ------------------------------------ 4 files changed, 228 deletions(-) delete mode 100644 test/mongo/cmd/main.go delete mode 100644 test/mongo/mongo_utils.go delete mode 100644 test/mysql/cmd/main.go delete mode 100644 test/mysql/importuser.go diff --git a/test/mongo/cmd/main.go b/test/mongo/cmd/main.go deleted file mode 100644 index 7873fee90..000000000 --- a/test/mongo/cmd/main.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "Open_IM/pkg/common/config" - mongo2 "Open_IM/test/mongo" - "context" - "flag" - "fmt" - - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" -) - -func init() { - uri := "mongodb://sample.host:27017/?maxPoolSize=20&w=majority" - if config.Config.Mongo.DBUri != "" { - // example: - // mongodb://$user:$password@mongo1.mongo:27017,mongo2.mongo:27017,mongo3.mongo:27017/$DBDatabase/?replicaSet=rs0&readPreference=secondary&authSource=admin&maxPoolSize=$DBMaxPoolSize - uri = config.Config.Mongo.DBUri - } else { - if config.Config.Mongo.DBPassword != "" && config.Config.Mongo.DBUserName != "" { - uri = fmt.Sprintf("mongodb://%s:%s@%s/%s?maxPoolSize=%d", config.Config.Mongo.DBUserName, config.Config.Mongo.DBPassword, config.Config.Mongo.DBAddress[0], - config.Config.Mongo.DBDatabase, config.Config.Mongo.DBMaxPoolSize) - } else { - uri = fmt.Sprintf("mongodb://%s/%s/?maxPoolSize=%d", - config.Config.Mongo.DBAddress[0], config.Config.Mongo.DBDatabase, - config.Config.Mongo.DBMaxPoolSize) - } - } - var err error - mongo2.Client, err = mongo.Connect(context.TODO(), options.Client().ApplyURI(uri)) - if err != nil { - panic(err) - } - err = mongo2.Client.Ping(context.TODO(), nil) - if err != nil { - panic(err) - } - fmt.Println("Connected to MongoDB!") -} - -func main() { - userID := flag.String("userID", "", "userID") - flag.Parse() - fmt.Println("userID:", *userID) - mongo2.GetUserAllChat(*userID) -} diff --git a/test/mongo/mongo_utils.go b/test/mongo/mongo_utils.go deleted file mode 100644 index 18cdcbea3..000000000 --- a/test/mongo/mongo_utils.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mongo - -import ( - "Open_IM/pkg/common/config" - server_api_params "Open_IM/pkg/proto/sdk_ws" - "context" - "fmt" - "time" - - "github.com/golang/protobuf/proto" - "go.mongodb.org/mongo-driver/mongo" - "gopkg.in/mgo.v2/bson" -) - -var ( - Client *mongo.Client -) - -type MsgInfo struct { - SendTime int64 - Msg []byte -} - -type UserChat struct { - UID string - Msg []MsgInfo -} - -func GetUserAllChat(uid string) { - ctx, _ := context.WithTimeout(context.Background(), time.Duration(config.Config.Mongo.DBTimeout)*time.Second) - collection := Client.Database(config.Config.Mongo.DBDatabase).Collection("msg") - var userChatList []UserChat - uid = uid + ":" - filter := bson.M{"uid": bson.M{"$regex": uid}} - //filter := bson.M{"uid": "17726378428:0"} - result, err := collection.Find(context.Background(), filter) - if err != nil { - fmt.Println("find error", err.Error()) - return - } - if err := result.All(ctx, &userChatList); err != nil { - fmt.Println(err.Error()) - } - for _, userChat := range userChatList { - for _, msg := range userChat.Msg { - msgData := &server_api_params.MsgData{} - err := proto.Unmarshal(msg.Msg, msgData) - if err != nil { - fmt.Println(err.Error(), msg) - continue - } - fmt.Println("seq: ", msgData.Seq, "status: ", msgData.Status, - "sendID: ", msgData.SendID, "recvID: ", msgData.RecvID, - "sendTime: ", msgData.SendTime, - "clientMsgID: ", msgData.ClientMsgID, - "serverMsgID: ", msgData.ServerMsgID, - "content: ", string(msgData.Content)) - } - } -} diff --git a/test/mysql/cmd/main.go b/test/mysql/cmd/main.go deleted file mode 100644 index b3d73b8db..000000000 --- a/test/mysql/cmd/main.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "Open_IM/test/mysql" -) - -func main() { - mysql.ImportUserToSuperGroup() -} diff --git a/test/mysql/importuser.go b/test/mysql/importuser.go deleted file mode 100644 index 07d97340d..000000000 --- a/test/mysql/importuser.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mysql - -import ( - "Open_IM/pkg/common/db" - "Open_IM/pkg/common/db/mysql_model/im_mysql_model" - "Open_IM/pkg/common/log" - "strconv" - "time" -) - -func ImportUserToSuperGroup() { - for i := 18000000700; i <= 18000000800; i++ { - user := db.User{ - UserID: strconv.Itoa(i), - Nickname: strconv.Itoa(i), - FaceURL: "", - Gender: 0, - PhoneNumber: strconv.Itoa(i), - Birth: time.Time{}, - Email: "", - Ex: "", - CreateTime: time.Time{}, - AppMangerLevel: 0, - GlobalRecvMsgOpt: 0, - } - err := im_mysql_model.UserRegister(user) - if err != nil { - log.NewError("", err.Error(), user) - continue - } - - groupMember := db.GroupMember{ - GroupID: "3907826375", - UserID: strconv.Itoa(i), - Nickname: strconv.Itoa(i), - FaceURL: "", - RoleLevel: 0, - JoinTime: time.Time{}, - JoinSource: 0, - InviterUserID: "openIMAdmin", - OperatorUserID: "openIMAdmin", - MuteEndTime: time.Time{}, - Ex: "", - } - - err = im_mysql_model.InsertIntoGroupMember(groupMember) - if err != nil { - log.NewError("", err.Error(), user) - continue - } - - log.NewInfo("success", i) - - } - -} From 74b849f4bc20c407b8c2cd2f0f2ebbf40fe662fb Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Wed, 5 Jul 2023 15:35:33 +0800 Subject: [PATCH 26/73] feat: add code comment Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- cmd/openim_msggateway/deploy.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/openim_msggateway/deploy.Dockerfile b/cmd/openim_msggateway/deploy.Dockerfile index 9ecc83e95..eab3d16db 100644 --- a/cmd/openim_msggateway/deploy.Dockerfile +++ b/cmd/openim_msggateway/deploy.Dockerfile @@ -29,4 +29,4 @@ COPY ./openim_msg_gateway ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./openim_msg_gateway","--port", "10140" "--ws_port", "10001", "--prometheus_port", "20240"] +CMD ["./openim_msg_gateway","--port", "10140" "--ws_port", "10001", "--prometheus_port", "20240"] From ae2369c3a93a5a2298c3ce88e5874906a3cd8e3e Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Wed, 5 Jul 2023 17:11:51 +0800 Subject: [PATCH 27/73] feat: add scripts Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- CONTRIBUTING.md | 2 +- Makefile | 2 +- docs/.generated_docs | 84 ++++++++++++++++++++++++++++++++++++ scripts/README.md | 30 +++++++++++++ scripts/common.sh | 23 +++++++--- scripts/githooks/pre-commit | 2 +- scripts/lib/golang.sh | 20 ++++++--- scripts/lib/release.sh | 33 +++++++++++--- scripts/lib/util.sh | 53 +++++++++++++++-------- scripts/make-rules/common.mk | 10 ++--- 10 files changed, 215 insertions(+), 44 deletions(-) create mode 100644 docs/.generated_docs create mode 100644 scripts/README.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 21359aff6..b29008183 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -89,7 +89,7 @@ To propose PR for the Open-IM-Server item, we assume you have registered a GitHu 1. Fork the repository(Open-IM-Server) -2. **CLONE** your own repository to master locally. Use `git clone https://github.com//Open-IM-Server.git` to clone repository to your local machine. Then you can create new branches to finish the change you wish to make. +2. **CLONE** your own repository to main locally. Use `git clone https://github.com//Open-IM-Server.git` to clone repository to your local machine. Then you can create new branches to finish the change you wish to make. 3. **Set Remote** upstream to be `https://github.com/OpenIMSDK/Open-IM-Server.git` using the following two commands: diff --git a/Makefile b/Makefile index 7c59f6027..179f986dc 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,7 @@ Options: PLATFORMS Platform to build for. Default is linux_arm64 and linux_amd64. This option is available when using: make {build}.multiarch - Example: make build.multiarch PLATFORMS="linux_s390x linux_mips64 + Example: make multiarch PLATFORMS="linux_s390x linux_mips64 linux_mips64le darwin_amd64 windows_amd64 linux_amd64 linux_arm64". V Set to 1 enable verbose build. Default is 0. diff --git a/docs/.generated_docs b/docs/.generated_docs new file mode 100644 index 000000000..380e2878d --- /dev/null +++ b/docs/.generated_docs @@ -0,0 +1,84 @@ +docs/.generated_docs +docs/guide/en-US/cmd/iam-apiserver.md +docs/guide/en-US/cmd/iam-authz-server.md +docs/guide/en-US/cmd/iam-pump.md +docs/guide/en-US/cmd/iam-watcher.md +docs/guide/en-US/cmd/iamctl/iamctl.md +docs/guide/en-US/cmd/iamctl/iamctl_color.md +docs/guide/en-US/cmd/iamctl/iamctl_completion.md +docs/guide/en-US/cmd/iamctl/iamctl_info.md +docs/guide/en-US/cmd/iamctl/iamctl_jwt.md +docs/guide/en-US/cmd/iamctl/iamctl_jwt_show.md +docs/guide/en-US/cmd/iamctl/iamctl_jwt_sign.md +docs/guide/en-US/cmd/iamctl/iamctl_jwt_verify.md +docs/guide/en-US/cmd/iamctl/iamctl_new.md +docs/guide/en-US/cmd/iamctl/iamctl_options.md +docs/guide/en-US/cmd/iamctl/iamctl_policy.md +docs/guide/en-US/cmd/iamctl/iamctl_policy_create.md +docs/guide/en-US/cmd/iamctl/iamctl_policy_delete.md +docs/guide/en-US/cmd/iamctl/iamctl_policy_get.md +docs/guide/en-US/cmd/iamctl/iamctl_policy_list.md +docs/guide/en-US/cmd/iamctl/iamctl_policy_update.md +docs/guide/en-US/cmd/iamctl/iamctl_secret.md +docs/guide/en-US/cmd/iamctl/iamctl_secret_create.md +docs/guide/en-US/cmd/iamctl/iamctl_secret_delete.md +docs/guide/en-US/cmd/iamctl/iamctl_secret_get.md +docs/guide/en-US/cmd/iamctl/iamctl_secret_list.md +docs/guide/en-US/cmd/iamctl/iamctl_secret_update.md +docs/guide/en-US/cmd/iamctl/iamctl_set.md +docs/guide/en-US/cmd/iamctl/iamctl_user.md +docs/guide/en-US/cmd/iamctl/iamctl_user_create.md +docs/guide/en-US/cmd/iamctl/iamctl_user_delete.md +docs/guide/en-US/cmd/iamctl/iamctl_user_get.md +docs/guide/en-US/cmd/iamctl/iamctl_user_list.md +docs/guide/en-US/cmd/iamctl/iamctl_user_update.md +docs/guide/en-US/cmd/iamctl/iamctl_validate.md +docs/guide/en-US/cmd/iamctl/iamctl_version.md +docs/guide/en-US/yaml/iamctl/iamctl.yaml +docs/guide/en-US/yaml/iamctl/iamctl_color.yaml +docs/guide/en-US/yaml/iamctl/iamctl_completion.yaml +docs/guide/en-US/yaml/iamctl/iamctl_info.yaml +docs/guide/en-US/yaml/iamctl/iamctl_jwt.yaml +docs/guide/en-US/yaml/iamctl/iamctl_new.yaml +docs/guide/en-US/yaml/iamctl/iamctl_options.yaml +docs/guide/en-US/yaml/iamctl/iamctl_policy.yaml +docs/guide/en-US/yaml/iamctl/iamctl_secret.yaml +docs/guide/en-US/yaml/iamctl/iamctl_set.yaml +docs/guide/en-US/yaml/iamctl/iamctl_user.yaml +docs/guide/en-US/yaml/iamctl/iamctl_validate.yaml +docs/guide/en-US/yaml/iamctl/iamctl_version.yaml +docs/man/man1/iam-apiserver.1 +docs/man/man1/iam-authz-server.1 +docs/man/man1/iam-pump.1 +docs/man/man1/iam-watcher.1 +docs/man/man1/iamctl-color.1 +docs/man/man1/iamctl-completion.1 +docs/man/man1/iamctl-info.1 +docs/man/man1/iamctl-jwt-show.1 +docs/man/man1/iamctl-jwt-sign.1 +docs/man/man1/iamctl-jwt-verify.1 +docs/man/man1/iamctl-jwt.1 +docs/man/man1/iamctl-new.1 +docs/man/man1/iamctl-options.1 +docs/man/man1/iamctl-policy-create.1 +docs/man/man1/iamctl-policy-delete.1 +docs/man/man1/iamctl-policy-get.1 +docs/man/man1/iamctl-policy-list.1 +docs/man/man1/iamctl-policy-update.1 +docs/man/man1/iamctl-policy.1 +docs/man/man1/iamctl-secret-create.1 +docs/man/man1/iamctl-secret-delete.1 +docs/man/man1/iamctl-secret-get.1 +docs/man/man1/iamctl-secret-list.1 +docs/man/man1/iamctl-secret-update.1 +docs/man/man1/iamctl-secret.1 +docs/man/man1/iamctl-set.1 +docs/man/man1/iamctl-user-create.1 +docs/man/man1/iamctl-user-delete.1 +docs/man/man1/iamctl-user-get.1 +docs/man/man1/iamctl-user-list.1 +docs/man/man1/iamctl-user-update.1 +docs/man/man1/iamctl-user.1 +docs/man/man1/iamctl-validate.1 +docs/man/man1/iamctl-version.1 +docs/man/man1/iamctl.1 diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 000000000..04bda00ae --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,30 @@ +# `/scripts` + +## Supported platforms + +- Linux x86_64 (linux_amd64) : 64-bit Linux for most desktop and server systems. + +- Windows x86_64 (windows_amd64) : 64-bit version for most Windows operating systems. + +- macOS x86_64 (darwin_amd64) : 64-bit version for Apple Macintosh computers. + +- Linux ARM64 (linux_arm64) : For ARM-based 64-bit Linux systems such as Raspberry Pi 4 and Jetson Nano. + +- Linux s390x (linux_s390x) : 64-bit Linux for IBM System z hosts. + +- Linux MIPS64 (linux_mips64) : 64-bit Linux for MIPS architecture. + +- Linux MIPS64LE (linux_mips64le) : Suitable for 64-bit Linux systems with little endian MIPS architecture. + + + +## examples +Scripts to perform various build, install, analysis, etc operations. + +These scripts keep the root level Makefile small and simple. + +Examples: + +* https://github.com/kubernetes/helm/tree/master/scripts +* https://github.com/cockroachdb/cockroach/tree/master/scripts +* https://github.com/hashicorp/terraform/tree/master/scripts \ No newline at end of file diff --git a/scripts/common.sh b/scripts/common.sh index 3a2090b6f..396f1c4a2 100755 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -89,11 +89,22 @@ openim::build::get_docker_wrapped_binaries() { local debian_iptables_version=v12.1.0 ### If you change any of these lists, please also update DOCKERIZED_BINARIES ### in build/BUILD. And openim::golang::server_image_targets + local targets=( - "openim-apiserver,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" - "openim-controller-manager,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" - "openim-scheduler,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" - "openim-proxy,${OPENIM_BASE_IMAGE_REGISTRY}/debian-iptables-${arch}:${debian_iptables_version}" + "openim-api,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-cmdutils,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-crontask,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-msggateway,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-msgtransfer,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-push,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-rpc-auth,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-rpc-conversation,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-rpc-friend,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-rpc-group,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-rpc-msg,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-rpc-third,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-rpc-user,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + ) echo "${targets[@]}" @@ -189,7 +200,7 @@ function openim::build::prepare_docker_machine() { local virtualbox_memory_mb=$(( available_memory_bytes / (bytes_in_mb * memory_divisor) )) docker-machine inspect "${DOCKER_MACHINE_NAME}" &> /dev/null || { - openim::log::status "Creating a machine to build IAM" + openim::log::status "Creating a machine to build OPENIM" docker-machine create --driver "${DOCKER_MACHINE_DRIVER}" \ --virtualbox-memory "${virtualbox_memory_mb}" \ --engine-env HTTP_PROXY="${IAMRNETES_HTTP_PROXY:-}" \ @@ -503,5 +514,5 @@ function openim::build::ensure_data_container() { # Build all openim commands. function openim::build::build_command() { openim::log::status "Running build command..." - make -C "${OPENIM_ROOT}" build.multiarch BINS="openimctl openim-apiserver openim-authz-server openim-pump openim-watcher" + make -C "${OPENIM_ROOT}" multiarch } diff --git a/scripts/githooks/pre-commit b/scripts/githooks/pre-commit index dbd84edef..8fbee166c 100644 --- a/scripts/githooks/pre-commit +++ b/scripts/githooks/pre-commit @@ -26,7 +26,7 @@ LC_ALL=C local_branch="$(git rev-parse --abbrev-ref HEAD)" -valid_branch_regex="^(main|master|develop)$|(feature|feat|release|hotfix|test|bug|ci|style|)\/[a-z0-9._-]+$|^HEAD$" +valid_branch_regex="^(main|master|develop)$|(feature|feat|openim|release|hotfix|test|bug|ci|cicd|style|)\/[a-z0-9._-]+$|^HEAD$" YELLOW="\e[93m" GREEN="\e[32m" diff --git a/scripts/lib/golang.sh b/scripts/lib/golang.sh index 1353ec2d2..56e34703b 100755 --- a/scripts/lib/golang.sh +++ b/scripts/lib/golang.sh @@ -33,10 +33,20 @@ readonly OPENIM_SUPPORTED_CLIENT_PLATFORMS=( # If you update this list, please also update build/BUILD. openim::golang::server_targets() { local targets=( - openim-apiserver - openim-authz-server - openim-pump - openim-watcher + openim_api + openim_cmdutils + openim_cmdutils + openim_crontask + openim_msggateway + openim_msgtransfer + openim_push + openim_rpc_auth + openim_rpc_conversation + openim_rpc_friend + openim_rpc_group + openim_rpc_msg + openim_rpc_third + openim_rpc_user ) echo "${targets[@]}" } @@ -137,7 +147,7 @@ openim::golang::setup_platforms # The set of client targets that we are building for all platforms # If you update this list, please also update build/BUILD. readonly OPENIM_CLIENT_TARGETS=( - iamctl + imctl ) readonly OPENIM_CLIENT_BINARIES=("${OPENIM_CLIENT_TARGETS[@]##*/}") diff --git a/scripts/lib/release.sh b/scripts/lib/release.sh index e596afaff..a299a8943 100755 --- a/scripts/lib/release.sh +++ b/scripts/lib/release.sh @@ -137,12 +137,13 @@ function openim::release::package_src_tarball() { ! \( \ \( -path "${OPENIM_ROOT}"/_\* -o \ -path "${OPENIM_ROOT}"/.git\* -o \ + -path "${OPENIM_ROOT}"/.github\* -o \ -path "${OPENIM_ROOT}"/.gitignore\* -o \ - -path "${OPENIM_ROOT}"/.gsemver.yaml\* -o \ + -path "${OPENIM_ROOT}"/.gsemver.yml\* -o \ -path "${OPENIM_ROOT}"/.config\* -o \ -path "${OPENIM_ROOT}"/.chglog\* -o \ -path "${OPENIM_ROOT}"/.gitlint -o \ - -path "${OPENIM_ROOT}"/.golangci.yaml -o \ + -path "${OPENIM_ROOT}"/.golangci.yml -o \ -path "${OPENIM_ROOT}"/.goreleaser.yml -o \ -path "${OPENIM_ROOT}"/.note.md -o \ -path "${OPENIM_ROOT}"/.todo.md \ @@ -274,6 +275,15 @@ function openim::release::sha1() { fi } +function openim::release::sha256() { + if which sha256sum >/dev/null 2>&1; then + sha256sum "$1" | awk '{ print $1 }' + else + shasum -a256 "$1" | awk '{ print $1 }' + fi +} + + function openim::release::build_conformance_image() { local -r arch="$1" local -r registry="$2" @@ -396,10 +406,19 @@ function openim::release::package_iam_manifests_tarball() { local dst_dir="${release_stage}" mkdir -p "${dst_dir}" cp -r ${src_dir}/* "${dst_dir}" - #cp "${src_dir}/openim-apiserver.yaml" "${dst_dir}" - #cp "${src_dir}/openim-authz-server.yaml" "${dst_dir}" - #cp "${src_dir}/openim-pump.yaml" "${dst_dir}" - #cp "${src_dir}/openim-watcher.yaml" "${dst_dir}" + #cp "${src_dir}/openim-api.yaml" "${dst_dir}" + #cp "${src_dir}/openim_cmdutils.yaml" "${dst_dir}" + #cp "${src_dir}/openim_crontask.yaml" "${dst_dir}" + #cp "${src_dir}/openim_msggateway.yaml" "${dst_dir}" + #cp "${src_dir}/openim_msgtransfer.yaml" "${dst_dir}" + #cp "${src_dir}/openim_push.yaml" "${dst_dir}" + #cp "${src_dir}/openim_rpc_auth.yaml" "${dst_dir}" + #cp "${src_dir}/openim_rpc_conversation.yaml" "${dst_dir}" + #cp "${src_dir}/openim_rpc_friend.yaml" "${dst_dir}" + #cp "${src_dir}/openim_rpc_group.yaml" "${dst_dir}" + #cp "${src_dir}/openim_rpc_msg.yaml" "${dst_dir}" + #cp "${src_dir}/openim_rpc_third.yaml" "${dst_dir}" + #cp "${src_dir}/openim_rpc_user.yaml" "${dst_dir}" #cp "${OPENIM_ROOT}/cluster/gce/gci/health-monitor.sh" "${dst_dir}/health-monitor.sh" openim::release::clean_cruft @@ -603,5 +622,5 @@ function openim::release::generate_changelog() { set +o errexit git add ${OPENIM_ROOT}/CHANGELOG/CHANGELOG-${OPENIM_GIT_VERSION#v}.md git commit -a -m "docs(changelog): add CHANGELOG-${OPENIM_GIT_VERSION#v}.md" - git push -f origin master # 最后将 CHANGELOG 也 push 上去 + git push -f origin main # 最后将 CHANGELOG 也 push 上去 } diff --git a/scripts/lib/util.sh b/scripts/lib/util.sh index 2f32dac99..dee1cad74 100755 --- a/scripts/lib/util.sh +++ b/scripts/lib/util.sh @@ -64,7 +64,7 @@ openim::util::wait_for_url() { return 1 } -# Example: openim::util::wait_for_success 120 5 "iamctl get nodes|grep localhost" +# Example: openim::util::wait_for_success 120 5 "imctl get nodes|grep localhost" # arguments: wait time, sleep time, shell command # returns 0 if the shell command get output, 1 otherwise. openim::util::wait_for_success(){ @@ -212,7 +212,7 @@ openim::util::find-binary() { openim::util::find-binary-for-platform "$1" "$(openim::util::host_platform)" } -# Run all known doc generators (today gendocs and genman for iamctl) +# Run all known doc generators (today gendocs and genman for imctl) # $1 is the directory to put those generated documents openim::util::gen-docs() { local dest="$1" @@ -228,25 +228,42 @@ openim::util::gen-docs() { # least from k/k tree), remove it completely. openim::util::sourced_variable "${genfeddocs}" - mkdir -p "${dest}/docs/guide/en-US/cmd/iamctl/" - "${gendocs}" "${dest}/docs/guide/en-US/cmd/iamctl/" + mkdir -p "${dest}/docs/guide/en-US/cmd/imctl/" + "${gendocs}" "${dest}/docs/guide/en-US/cmd/imctl/" mkdir -p "${dest}/docs/guide/en-US/cmd/" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-apiserver" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-authz-server" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-pump" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-watcher" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/iamctl" "iamctl" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_api" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_cmdutils" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_crontask" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_msggateway" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_msgtransfer" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_push" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_rpc_auth" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_rpc_conversation" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_rpc_friend" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_rpc_group" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_rpc_msg" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_rpc_third" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_rpc_user" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/imctl" "imctl" mkdir -p "${dest}/docs/man/man1/" - "${genman}" "${dest}/docs/man/man1/" "openim-apiserver" - "${genman}" "${dest}/docs/man/man1/" "openim-authz-server" - "${genman}" "${dest}/docs/man/man1/" "openim-pump" - "${genman}" "${dest}/docs/man/man1/" "openim-watcher" - "${genman}" "${dest}/docs/man/man1/" "iamctl" - - mkdir -p "${dest}/docs/guide/en-US/yaml/iamctl/" - "${genyaml}" "${dest}/docs/guide/en-US/yaml/iamctl/" +"${genman}" "${dest}/docs/man/man1/" "openim_api" +"${genman}" "${dest}/docs/man/man1/" "openim_cmdutils" +"${genman}" "${dest}/docs/man/man1/" "openim_crontask" +"${genman}" "${dest}/docs/man/man1/" "openim_msggateway" +"${genman}" "${dest}/docs/man/man1/" "openim_msgtransfer" +"${genman}" "${dest}/docs/man/man1/" "openim_push" +"${genman}" "${dest}/docs/man/man1/" "openim_rpc_auth" +"${genman}" "${dest}/docs/man/man1/" "openim_rpc_conversation" +"${genman}" "${dest}/docs/man/man1/" "openim_rpc_friend" +"${genman}" "${dest}/docs/man/man1/" "openim_rpc_group" +"${genman}" "${dest}/docs/man/man1/" "openim_rpc_msg" +"${genman}" "${dest}/docs/man/man1/" "openim_rpc_third" +"${genman}" "${dest}/docs/man/man1/" "openim_rpc_user" + + mkdir -p "${dest}/docs/guide/en-US/yaml/imctl/" + "${genyaml}" "${dest}/docs/guide/en-US/yaml/imct/" # create the list of generated files pushd "${dest}" > /dev/null || return 1 @@ -471,7 +488,7 @@ EOF # flatten the iamconfig files to make them self contained username=$(whoami) ${sudo} /usr/bin/env bash -e < "/tmp/${client_id}.iamconfig" + $(openim::util::find-binary imct) --iamconfig="${dest_dir}/${client_id}.iamconfig" config view --minify --flatten > "/tmp/${client_id}.iamconfig" mv -f "/tmp/${client_id}.iamconfig" "${dest_dir}/${client_id}.iamconfig" chown ${username} "${dest_dir}/${client_id}.iamconfig" EOF diff --git a/scripts/make-rules/common.mk b/scripts/make-rules/common.mk index 5019ebc77..179643b12 100644 --- a/scripts/make-rules/common.mk +++ b/scripts/make-rules/common.mk @@ -147,18 +147,18 @@ endef # Here are some examples of builds define MAKEFILE_EXAMPLE -# make build BINS=imctl Only a single imctl binary is built. +# make build BINS=openim_api Only a single openim_api binary is built. # make -j (nproc) all Run tidy gen add-copyright format lint cover build concurrently. # make gen Generate all necessary files. -# make linux.arm64 imctl is compiled on arm64 platform. +# make release Build release binaries for all platforms. # make verify-copyright Verify the license headers for all files. # make install-deepcopy-gen Install deepcopy-gen tools if the license is missing. -# make build BINS=imctl V=1 DEBUG=1 Build debug binaries for only imctl. -# make multiarch PLATFORMS="linux_arm64 linux_amd64" V=1 Build binaries for both platforms. +# make build BINS=openim_api V=1 DEBUG=1 Build debug binaries for only openim_api. +# make multiarch -j PLATFORMS="linux_arm64 linux_amd64" V=1 Build binaries for both platforms. endef export MAKEFILE_EXAMPLE -# Define all help functions @printf "\n\033[1mCurrent imctl version information: $(shell imctl version):\033[0m\n\n" +# Define all help functions @printf "\n\033[1mCurrent openim_api version information: $(shell openim_api version):\033[0m\n\n" define makeallhelp @printf "\n\033[1mMake example:\033[0m\n\n" $(call MAKEFILE_EXAMPLE) From d30131166db208ee6d507267a48e265d75d43190 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Wed, 5 Jul 2023 22:12:48 +0800 Subject: [PATCH 28/73] feat: add make file Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- .github/sync.yml | 12 +++ Makefile | 61 +++++++-------- docs/contrib/cicd-actions.md | 127 +++++++++++++++++++++++++++++++ docs/contrib/code_conventions.md | 38 +++++++++ scripts/common.sh | 18 ++--- scripts/lib/release.sh | 8 +- scripts/lib/util.sh | 1 + scripts/make-rules/common.mk | 2 +- scripts/make-rules/image.mk | 7 ++ 9 files changed, 230 insertions(+), 44 deletions(-) create mode 100644 docs/contrib/cicd-actions.md create mode 100644 docs/contrib/code_conventions.md diff --git a/.github/sync.yml b/.github/sync.yml index 859096c6f..fdc003a0f 100644 --- a/.github/sync.yml +++ b/.github/sync.yml @@ -28,6 +28,9 @@ OpenIMSDK/openim-sdk-core: - source: .github/workflows/stale.yml dest: .github/workflows/stale.yml replace: false + - source: .github/.codecov.yml + dest: .github/.codecov.yml + replace: false OpenIMSDK/OpenIM-Docs: - source: .github/workflows/ @@ -38,6 +41,9 @@ OpenIMSDK/OpenIM-Docs: - source: scripts/githooks/ dest: scripts/githooks/ replace: true + - source: .github/.codecov.yml + dest: .github/.codecov.yml + replace: false OpenIMSDK/OpenKF: - source: LICENSE @@ -51,6 +57,9 @@ OpenIMSDK/OpenKF: - source: .github/workflows/stale.yml dest: .github/workflows/stale.yml replace: false + - source: .github/.codecov.yml + dest: .github/.codecov.yml + replace: false group: # first group:common to all warehouses @@ -75,6 +84,9 @@ group: - source: .github/workflows/help-comment-issue.yml dest: .github/workflows/help-comment-issue.yml replace: false + - source: .github/.codecov.yml + dest: .github/.codecov.yml + replace: false - source: ./scripts/githooks/ dest: ./scripts/githooks/ replace: true \ No newline at end of file diff --git a/Makefile b/Makefile index 179f986dc..8b827e91b 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ .DEFAULT_GOAL := help +## all: Run tidy, gen, add-copyright, format, lint, cover, build ✨ .PHONY: all all: tidy gen add-copyright format lint cover build @@ -52,135 +53,135 @@ export USAGE_OPTIONS # ============================================================================== # Targets -## build: Build binaries by default +## build: Build binaries by default ✨ .PHONY: build build: @$(MAKE) go.build -## multiarch: Build binaries for multiple platforms. See option PLATFORMS. +## multiarch: Build binaries for multiple platforms. See option PLATFORMS. ✨ .PHONY: multiarch multiarch: @$(MAKE) go.build.multiarch -## tidy: tidy go.mod +## tidy: tidy go.mod ✨ .PHONY: tidy tidy: @$(GO) mod tidy -## vendor: vendor go.mod +## vendor: vendor go.mod ✨ .PHONY: vendor vendor: @$(GO) mod vendor -## style: code style -> fmt,vet,lint +## style: code style -> fmt,vet,lint ✨ .PHONY: style style: fmt vet lint -## fmt: Run go fmt against code. +## fmt: Run go fmt against code. ✨ .PHONY: fmt fmt: @$(GO) fmt ./... -## vet: Run go vet against code. +## vet: Run go vet against code. ✨ .PHONY: vet vet: @$(GO) vet ./... -## lint: Check syntax and styling of go sources. +lint: Check syntax and styling of go sources. ✨ .PHONY: lint lint: @$(MAKE) go.lint -## format: Gofmt (reformat) package sources (exclude vendor dir if existed). +## format: Gofmt (reformat) package sources (exclude vendor dir if existed). ✨ .PHONY: format format: @$(MAKE) go.format -## test: Run unit test. +## test: Run unit test. ✨ .PHONY: test test: @$(MAKE) go.test -## cover: Run unit test and get test coverage. -.PHONY: cover +## cover: Run unit test and get test coverage. ✨ +.PHONY: cover cover: @$(MAKE) go.test.cover -## updates: Check for updates to go.mod dependencies +## updates: Check for updates to go.mod dependencies ✨ .PHONY: updates @$(MAKE) go.updates -## imports: task to automatically handle import packages in Go files using goimports tool +## imports: task to automatically handle import packages in Go files using goimports tool ✨ .PHONY: imports imports: @$(MAKE) go.imports -## clean: Remove all files that are created by building. +## clean: Remove all files that are created by building. ✨ .PHONY: clean clean: @$(MAKE) go.clean -## image: Build docker images for host arch. +## image: Build docker images for host arch. ✨ .PHONY: image image: @$(MAKE) image.build -## image.multiarch: Build docker images for multiple platforms. See option PLATFORMS. +## image.multiarch: Build docker images for multiple platforms. See option PLATFORMS. ✨ .PHONY: image.multiarch image.multiarch: @$(MAKE) image.build.multiarch -## push: Build docker images for host arch and push images to registry. +## push: Build docker images for host arch and push images to registry. ✨ .PHONY: push push: @$(MAKE) image.push -## push.multiarch: Build docker images for multiple platforms and push images to registry. +## push.multiarch: Build docker images for multiple platforms and push images to registry. ✨ .PHONY: push.multiarch push.multiarch: @$(MAKE) image.push.multiarch -## tools: Install dependent tools. +## tools: Install dependent tools. ✨ .PHONY: tools tools: @$(MAKE) tools.install -## gen: Generate all necessary files. +## gen: Generate all necessary files. ✨ .PHONY: gen gen: @$(MAKE) gen.run -## swagger: Generate swagger document. +## swagger: Generate swagger document. ✨ .PHONY: swagger swagger: @$(MAKE) swagger.run -## serve-swagger: Serve swagger spec and docs. +## serve-swagger: Serve swagger spec and docs. ✨ .PHONY: swagger.serve serve-swagger: @$(MAKE) swagger.serve -## verify-copyright: Verify the license headers for all files. +## verify-copyright: Verify the license headers for all files. ✨ .PHONY: verify-copyright verify-copyright: @$(MAKE) copyright.verify -## add-copyright: Add copyright ensure source code files have license headers. +## add-copyright: Add copyright ensure source code files have license headers. ✨ .PHONY: add-copyright add-copyright: @$(MAKE) copyright.add -## release: release the project +## release: release the project ✨ .PHONY: release release: release.verify release.ensure-tag @scripts/release.sh -## help: Show this help info. +## help: Show this help info. ✨ .PHONY: help help: Makefile $(call makehelp) -## help-all: Show all help details info. +## help-all: Show all help details info. ✨ .PHONY: help-all -help-all: go.help copyright.help tools.help image.help help - $(call makeallhelp) +help-all: go.help copyright.help tools.help image.help dependencies.help gen.help release.help swagger.help help + $(call makeallhelp) \ No newline at end of file diff --git a/docs/contrib/cicd-actions.md b/docs/contrib/cicd-actions.md new file mode 100644 index 000000000..7a581d3fa --- /dev/null +++ b/docs/contrib/cicd-actions.md @@ -0,0 +1,127 @@ +# Continuous Integration and Automation + +Every change on the K3s repository, either made through a pull request or direct push, triggers the continuous integration pipelines defined within the same repository. Needless to say, all the K3s contributions can be merged until all the checks pass (AKA having green builds). + +- [Continuous Integration and Automation](#continuous-integration-and-automation) + - [CI Platforms](#ci-platforms) + - [GitHub Actions](#github-actions) + - [Running locally](#running-locally) + +## CI Platforms + +Currently, there are two different platforms involved in running the CI processes: + +- GitHub actions +- Drone pipelines on CNCF infrastructure + +### GitHub Actions + +All the existing GitHub Actions are defined as YAML files under the `.github/workflows` directory. These can be grouped into: + +- **PR Checks**. These actions run all the required validations upon PR creation and update. Covering the DCO compliance check, `x86_64` test batteries (unit, integration, smoke), and code coverage. +- **Repository automation**. Currently, it only covers issues and epic grooming. + +Everything runs on GitHub's provided runners; thus, the tests are limited to run in `x86_64` architectures. + + +## Running locally + +A contributor should verify their changes locally to speed up the pull request process. Fortunately, all the CI steps can be on local environments, except for the publishing ones, through either of the following methods: + +**User Makefile:** +```bash +root@PS2023EVRHNCXG:~/workspaces/openim/Open-IM-Server# make help 😊 + +Usage: make ... + +Targets: + +all Run tidy, gen, add-copyright, format, lint, cover, build 🚀 +build Build binaries by default 🛠️ +multiarch Build binaries for multiple platforms. See option PLATFORMS. 🌍 +tidy tidy go.mod ✨ +vendor vendor go.mod 📦 +style code style -> fmt,vet,lint 💅 +fmt Run go fmt against code. ✨ +vet Run go vet against code. ✅ +lint Check syntax and styling of go sources. ✔️ +format Gofmt (reformat) package sources (exclude vendor dir if existed). 🔄 +test Run unit test. 🧪 +cover Run unit test and get test coverage. 📊 +updates Check for updates to go.mod dependencies 🆕 +imports task to automatically handle import packages in Go files using goimports tool 📥 +clean Remove all files that are created by building. 🗑️ +image Build docker images for host arch. 🐳 +image.multiarch Build docker images for multiple platforms. See option PLATFORMS. 🌍🐳 +push Build docker images for host arch and push images to registry. 📤🐳 +push.multiarch Build docker images for multiple platforms and push images to registry. 🌍📤🐳 +tools Install dependent tools. 🧰 +gen Generate all necessary files. 🧩 +swagger Generate swagger document. 📖 +serve-swagger Serve swagger spec and docs. 🚀📚 +verify-copyright Verify the license headers for all files. ✅ +add-copyright Add copyright ensure source code files have license headers. 📄 +release release the project 🎉 +help Show this help info. ℹ️ +help-all Show all help details info. ℹ️📚 + +Options: + +DEBUG Whether or not to generate debug symbols. Default is 0. ❓ + +BINS Binaries to build. Default is all binaries under cmd. 🛠️ +This option is available when using: make {build}(.multiarch) 🧰 +Example: make build BINS="openim_api openim_cms_api". + +PLATFORMS Platform to build for. Default is linux_arm64 and linux_amd64. 🌍 +This option is available when using: make {build}.multiarch 🌍 +Example: make multiarch PLATFORMS="linux_s390x linux_mips64 +linux_mips64le darwin_amd64 windows_amd64 linux_amd64 linux_arm64". + +V Set to 1 enable verbose build. Default is 0. 📝 +``` +How to Use Makefile to Help Contributors Build Projects Quickly 😊 + +The `make help` command is a handy tool that provides useful information on how to utilize the Makefile effectively. By running this command, contributors will gain insights into various targets and options available for building projects swiftly. + +Here's a breakdown of the targets and options provided by the Makefile: + +Targets 😃 + +1. `all`: This target runs multiple tasks like `tidy`, `gen`, `add-copyright`, `format`, `lint`, `cover`, and `build`. It ensures comprehensive project building. +2. `build`: The primary target that compiles binaries by default. It is particularly useful for creating the necessary executable files. +3. `multiarch`: A target that builds binaries for multiple platforms. Contributors can specify the desired platforms using the `PLATFORMS` option. +4. `tidy`: This target cleans up the `go.mod` file, ensuring its consistency. +5. `vendor`: A target that updates the project dependencies based on the `go.mod` file. +6. `style`: Checks the code style using tools like `fmt`, `vet`, and `lint`. It ensures a consistent coding style throughout the project. +7. `fmt`: Formats the code using the `go fmt` command, ensuring proper indentation and formatting. +8. `vet`: Runs the `go vet` command to identify common errors in the code. +9. `lint`: Validates the syntax and styling of Go source files using a linter. +10. `format`: Reformats the package sources using `gofmt`. It excludes the vendor directory if it exists. +11. `test`: Executes unit tests to ensure the functionality and stability of the code. +12. `cover`: Performs unit tests and calculates the test coverage of the code. +13. `updates`: Checks for updates to the project's dependencies specified in the `go.mod` file. +14. `imports`: Automatically handles import packages in Go files using the `goimports` tool. +15. `clean`: Removes all files generated during the build process, effectively cleaning up the project directory. +16. `image`: Builds Docker images for the host architecture. +17. `image.multiarch`: Similar to the `image` target, but it builds Docker images for multiple platforms. Contributors can specify the desired platforms using the `PLATFORMS` option. +18. `push`: Builds Docker images for the host architecture and pushes them to a registry. +19. `push.multiarch`: Builds Docker images for multiple platforms and pushes them to a registry. Contributors can specify the desired platforms using the `PLATFORMS` option. +20. `tools`: Installs the necessary tools or dependencies required by the project. +21. `gen`: Generates all the required files automatically. +22. `swagger`: Generates the swagger document for the project. +23. `serve-swagger`: Serves the swagger specification and documentation. +24. `verify-copyright`: Verifies the license headers for all project files. +25. `add-copyright`: Adds copyright headers to the source code files. +26. `release`: Releases the project, presumably for distribution. +27. `help`: Displays information about available targets and options. +28. `help-all`: Shows detailed information about all available targets and options. + +Options 😄 + +1. `DEBUG`: A boolean option that determines whether or not to generate debug symbols. The default value is 0 (false). +2. `BINS`: Specifies the binaries to build. By default, it builds all binaries under the `cmd` directory. Contributors can provide a list of specific binaries using this option. +3. `PLATFORMS`: Specifies the platforms to build for. The default platforms are `linux_arm64` and `linux_amd64`. Contributors can specify multiple platforms by providing a space-separated list of platform names. +4. `V`: A boolean option that enables verbose build output when set to 1 (true). The default value is 0 (false). + +With these targets and options in place, contributors can efficiently build projects using the Makefile. Happy coding! 🚀😊 diff --git a/docs/contrib/code_conventions.md b/docs/contrib/code_conventions.md new file mode 100644 index 000000000..1387da2f7 --- /dev/null +++ b/docs/contrib/code_conventions.md @@ -0,0 +1,38 @@ +# Code conventions + +- [Code conventions](#code-conventions) + - [POSIX shell](#posix-shell) + - [Go](#go) + - [Directory and file conventions](#directory-and-file-conventions) + - [Testing conventions](#testing-conventions) + +## POSIX shell + +- [Style guide](https://google.github.io/styleguide/shell.xml) + +## Go + +- [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments) +- [Effective Go](https://golang.org/doc/effective_go.html) +- Know and avoid [Go landmines](https://gist.github.com/lavalamp/4bd23295a9f32706a48f) +- Comment your code. + - [Go's commenting conventions](http://blog.golang.org/godoc-documenting-go-code) + - If reviewers ask questions about why the code is the way it is, that's a sign that comments might be helpful. +- Command-line flags should use dashes, not underscores +- Naming + - Please consider package name when selecting an interface name, and avoid redundancy. For example, `storage.Interface` is better than `storage.StorageInterface`. + - Do not use uppercase characters, underscores, or dashes in package names. + - Please consider parent directory name when choosing a package name. For example, `pkg/controllers/autoscaler/foo.go` should say `package autoscaler` not `package autoscalercontroller`. + - Unless there's a good reason, the `package foo` line should match the name of the directory in which the `.go` file exists. + - Importers can use a different name if they need to disambiguate. + +## Directory and file conventions + +- Avoid general utility packages. Packages called "util" are suspect. Instead, derive a name that describes your desired function. For example, the utility functions dealing with waiting for operations are in the `wait` package and include functionality like `Poll`. The full name is `wait.Poll`. +- All filenames should be lowercase. +- All source files and directories should use underscores, not dashes. + - Package directories should generally avoid using separators as much as possible. When package names are multiple words, they usually should be in nested subdirectories. + +## Testing conventions + +Please refer to [TESTING.md](../../tests/TESTING.md) document. diff --git a/scripts/common.sh b/scripts/common.sh index 396f1c4a2..8ae2abfa5 100755 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -203,9 +203,9 @@ function openim::build::prepare_docker_machine() { openim::log::status "Creating a machine to build OPENIM" docker-machine create --driver "${DOCKER_MACHINE_DRIVER}" \ --virtualbox-memory "${virtualbox_memory_mb}" \ - --engine-env HTTP_PROXY="${IAMRNETES_HTTP_PROXY:-}" \ - --engine-env HTTPS_PROXY="${IAMRNETES_HTTPS_PROXY:-}" \ - --engine-env NO_PROXY="${IAMRNETES_NO_PROXY:-127.0.0.1}" \ + --engine-env HTTP_PROXY="${OPENIMRNETES_HTTP_PROXY:-}" \ + --engine-env HTTPS_PROXY="${OPENIMRNETES_HTTPS_PROXY:-}" \ + --engine-env NO_PROXY="${OPENIMRNETES_NO_PROXY:-127.0.0.1}" \ "${DOCKER_MACHINE_NAME}" > /dev/null || { openim::log::error "Something went wrong creating a machine." openim::log::error "Try the following: " @@ -250,14 +250,14 @@ function openim::build::update_dockerfile() { } function openim::build::set_proxy() { - if [[ -n "${IAMRNETES_HTTPS_PROXY:-}" ]]; then - echo "ENV https_proxy $IAMRNETES_HTTPS_PROXY" >> "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" + if [[ -n "${OPENIMRNETES_HTTPS_PROXY:-}" ]]; then + echo "ENV https_proxy $OPENIMRNETES_HTTPS_PROXY" >> "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" fi - if [[ -n "${IAMRNETES_HTTP_PROXY:-}" ]]; then - echo "ENV http_proxy $IAMRNETES_HTTP_PROXY" >> "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" + if [[ -n "${OPENIMRNETES_HTTP_PROXY:-}" ]]; then + echo "ENV http_proxy $OPENIMRNETES_HTTP_PROXY" >> "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" fi - if [[ -n "${IAMRNETES_NO_PROXY:-}" ]]; then - echo "ENV no_proxy $IAMRNETES_NO_PROXY" >> "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" + if [[ -n "${OPENIMRNETES_NO_PROXY:-}" ]]; then + echo "ENV no_proxy $OPENIMRNETES_NO_PROXY" >> "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" fi } diff --git a/scripts/lib/release.sh b/scripts/lib/release.sh index a299a8943..d6cdcdfcb 100755 --- a/scripts/lib/release.sh +++ b/scripts/lib/release.sh @@ -24,7 +24,7 @@ # Tencent cos configuration readonly BUCKET="openim-1306374445" -readonly REGION="ap-beijing" +readonly REGION="ap-guangzhou" readonly COS_RELEASE_DIR="openim-release" # default cos command tool coscli or coscmd @@ -178,7 +178,7 @@ function openim::release::package_server_tarballs() { # This fancy expression will expand to prepend a path # (${LOCAL_OUTPUT_BINPATH}/${platform}/) to every item in the # server_bins array. - cp "${server_bins[@]/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}" \ + cp "${server_bins[@]/bin/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}" \ "${release_stage}/server/bin/" openim::release::clean_cruft @@ -218,7 +218,7 @@ function openim::release::package_client_tarballs() { # This fancy expression will expand to prepend a path # (${LOCAL_OUTPUT_BINPATH}/${platform}/) to every item in the # client_bins array. - cp "${client_bins[@]/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}" \ + cp "${client_bins[@]/bin/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}" \ "${release_stage}/client/bin/" openim::release::clean_cruft @@ -252,7 +252,7 @@ function openim::release::build_server_images() { # This fancy expression will expand to prepend a path # (${LOCAL_OUTPUT_BINPATH}/${platform}/) to every item in the # OPENIM_SERVER_IMAGE_BINARIES array. - cp "${OPENIM_SERVER_IMAGE_BINARIES[@]/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}" \ + cp "${OPENIM_SERVER_IMAGE_BINARIES[@]/bin/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}" \ "${release_stage}/server/bin/" openim::release::create_docker_images_for_server "${release_stage}/server/bin" "${arch}" diff --git a/scripts/lib/util.sh b/scripts/lib/util.sh index dee1cad74..63d32eb79 100755 --- a/scripts/lib/util.sh +++ b/scripts/lib/util.sh @@ -199,6 +199,7 @@ openim::util::find-binary-for-platform() { "${OPENIM_ROOT}/_output/${platform}/${lookfor}" "${OPENIM_ROOT}/_output/local/bin/${platform}/${lookfor}" "${OPENIM_ROOT}/_output/platforms/${platform}/${lookfor}" + "${OPENIM_ROOT}/_output/platforms/bin/${platform}/${lookfor}" ) # List most recently-updated location. diff --git a/scripts/make-rules/common.mk b/scripts/make-rules/common.mk index 179643b12..e99585a76 100644 --- a/scripts/make-rules/common.mk +++ b/scripts/make-rules/common.mk @@ -87,7 +87,7 @@ endif # The OS must be linux when building docker images # PLATFORMS ?= linux_amd64 linux_arm64 # The OS can be linux/windows/darwin when building binaries -PLATFORMS ?= linux_s390x linux_mips64 linux_mips64le darwin_amd64 windows_amd64 linux_amd64 linux_arm64 +PLATFORMS ?= linux_s390x linux_mips64 linux_mips64le darwin_amd64 windows_amd64 linux_amd64 linux_arm64 linux_ppc64le # only support linux GOOS=linux diff --git a/scripts/make-rules/image.mk b/scripts/make-rules/image.mk index adcffa1cc..e70583c34 100644 --- a/scripts/make-rules/image.mk +++ b/scripts/make-rules/image.mk @@ -98,6 +98,13 @@ image.build.%: go.build.% fi @rm -rf $(TMP_DIR)/$(IMAGE) +## image.buildx.%: Build docker images with buildx +.PHONY: image.buildx.% +image.buildx.%: + $(eval IMAGE := $(word 1,$(subst ., ,$*))) + echo "===========> Building docker image $(IMAGE) $(VERSION)" + $(DOCKER) buildx build -f $(ROOT_DIR)/Dockerfile --pull --no-cache --platform=$(PLATFORMS) --push . -t $(REGISTRY_PREFIX)/$(IMAGE)-$(ARCH):$(VERSION) + ## image.push: Push docker images .PHONY: image.push image.push: image.verify go.build.verify $(addprefix image.push., $(addprefix $(IMAGE_PLAT)., $(IMAGES))) From 6484c32e2628ea72e43c6bffe13e958c091acf18 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Wed, 5 Jul 2023 22:36:15 +0800 Subject: [PATCH 29/73] docs: add more docs help deployer Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- docs/contrib/cicd-actions.md | 8 +-- docs/contrib/development.md | 80 +++++++++++++++++++++++++++ docs/contrib/git_workflow.md | 102 +++++++++++++++++++++++++++++++++++ 3 files changed, 187 insertions(+), 3 deletions(-) create mode 100644 docs/contrib/development.md create mode 100644 docs/contrib/git_workflow.md diff --git a/docs/contrib/cicd-actions.md b/docs/contrib/cicd-actions.md index 7a581d3fa..2a95860be 100644 --- a/docs/contrib/cicd-actions.md +++ b/docs/contrib/cicd-actions.md @@ -1,6 +1,6 @@ # Continuous Integration and Automation -Every change on the K3s repository, either made through a pull request or direct push, triggers the continuous integration pipelines defined within the same repository. Needless to say, all the K3s contributions can be merged until all the checks pass (AKA having green builds). +Every change on the OpenIM repository, either made through a pull request or direct push, triggers the continuous integration pipelines defined within the same repository. Needless to say, all the OpenIM contributions can be merged until all the checks pass (AKA having green builds). - [Continuous Integration and Automation](#continuous-integration-and-automation) - [CI Platforms](#ci-platforms) @@ -80,13 +80,15 @@ linux_mips64le darwin_amd64 windows_amd64 linux_amd64 linux_arm64". V Set to 1 enable verbose build. Default is 0. 📝 ``` + + How to Use Makefile to Help Contributors Build Projects Quickly 😊 The `make help` command is a handy tool that provides useful information on how to utilize the Makefile effectively. By running this command, contributors will gain insights into various targets and options available for building projects swiftly. Here's a breakdown of the targets and options provided by the Makefile: -Targets 😃 +**Targets 😃** 1. `all`: This target runs multiple tasks like `tidy`, `gen`, `add-copyright`, `format`, `lint`, `cover`, and `build`. It ensures comprehensive project building. 2. `build`: The primary target that compiles binaries by default. It is particularly useful for creating the necessary executable files. @@ -117,7 +119,7 @@ Targets 😃 27. `help`: Displays information about available targets and options. 28. `help-all`: Shows detailed information about all available targets and options. -Options 😄 +**Options 😄** 1. `DEBUG`: A boolean option that determines whether or not to generate debug symbols. The default value is 0 (false). 2. `BINS`: Specifies the binaries to build. By default, it builds all binaries under the `cmd` directory. Contributors can provide a list of specific binaries using this option. diff --git a/docs/contrib/development.md b/docs/contrib/development.md new file mode 100644 index 000000000..64aa08cda --- /dev/null +++ b/docs/contrib/development.md @@ -0,0 +1,80 @@ +# Development Guide + +Since OpenIM is written in Go, it is fair to assume that the Go tools are all one needs to contribute to this project. Unfortunately, there is a point where this no longer holds true when required to test or build local changes. This document elaborates on the required tooling for OpenIM development. + +- [Development Guide](#development-guide) + - [Non-Linux environment prerequisites](#non-linux-environment-prerequisites) + - [Windows Setup](#windows-setup) + - [macOS Setup](#macos-setup) + - [Installing Required Software](#installing-required-software) + - [Go](#go) + - [Docker](#docker) + - [Vagrant](#vagrant) + - [Cloning, Building and Testing OpenIM](#cloning-building-and-testing-openim) + - [Dependency management](#dependency-management) + +## Non-Linux environment prerequisites + +All the test and build scripts within this repository were created to be run on GNU Linux development environments. Due to this, it is suggested to use the virtual machine defined on this repository's [Vagrantfile](../../Vagrantfile) to use them. + +Either way, if one still wants to build and test OpenIM on non-Linux environments, specific setups are to be followed. + +### Windows Setup + +To build OpenIM on Windows is only possible for versions that support Windows Subsystem for Linux (WSL). If the development environment in question has Windows 10, Version 2004, Build 19041 or higher, [follow these instructions to install WSL2](https://docs.microsoft.com/en-us/windows/wsl/install-win10); otherwise, use a Linux Virtual machine instead. + +### macOS Setup + +The shell scripts in charge of the build and test processes rely on GNU utils (i.e. `sed`), [which slightly differ on macOS](https://unix.stackexchange.com/a/79357), meaning that one must make some adjustments before using them. + +First, install the GNU utils: + +```sh +brew install coreutils findutils gawk gnu-sed gnu-tar grep make +``` + +Then update the shell init script (i.e. `.bashrc`) to prepend the GNU Utils to the `$PATH` variable + +```sh +GNUBINS="$(find /usr/local/opt -type d -follow -name gnubin -print)" + +for bindir in ${GNUBINS[@]}; do + PATH=$bindir:$PATH +done + +export PATH +``` + +## Installing Required Software + +### Go + +It is well known that OpenIM is written in [Go](http://golang.org). Please follow the [Go Getting Started guide](https://golang.org/doc/install) to install and set up the Go tools used to compile and run the test batteries. + +| OpenIM | requires Go | +|----------------|-------------| +| 2.24 - 3.00 | 1.15 + | +| 3.30 + | 1.18 + | + +### Docker + +OpenIM build and test processes development require Docker to run certain steps. [Follow the Docker website instructions to install Docker](https://docs.docker.com/get-docker/) in the development environment. + +### Vagrant + +As described in the [Testing documentation](../../tests/TESTING.md), all the smoke tests are run in virtual machines managed by Vagrant. To install Vagrant in the development environment, [follow the instructions from the Hashicorp website](https://www.vagrantup.com/downloads), alongside any of the following hypervisors: + +- [VirtualBox](https://www.virtualbox.org/) +- [libvirt](https://libvirt.org/) and the [vagrant-libvirt plugin](https://github.com/vagrant-libvirt/vagrant-libvirt#installation) + +## Cloning, Building and Testing OpenIM + +These topics already have been addressed on their respective documents: + +- [Git Workflow](./git-workflow.md) +- [Building](../../BUILDING.md) +- [Testing](../../tests/TESTING.md) + +## Dependency management + +OpenIM uses [go modules](https://github.com/golang/go/wiki/Modules) to manage dependencies. diff --git a/docs/contrib/git_workflow.md b/docs/contrib/git_workflow.md new file mode 100644 index 000000000..1d21f9cdd --- /dev/null +++ b/docs/contrib/git_workflow.md @@ -0,0 +1,102 @@ +# Git workflows + +This document is an overview of OpenIM git workflow. It includes conventions, tips, and how to maintain good repository hygiene. + +- [Git workflows](#git-workflows) + - [Branching model](#branching-model) + - [Branch naming conventions](#branch-naming-conventions) + - [Backport policy](#backport-policy) + - [Git operations](#git-operations) + - [Setting up](#setting-up) + - [Branching out](#branching-out) + - [Keeping local branches in sync](#keeping-local-branches-in-sync) + - [Pushing changes](#pushing-changes) + +## Branching model + +OpenIM project uses the [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow) as its branching model, where most of the changes come from repositories forks instead of branches within the same one. + +### Branch naming conventions + +Every forked repository works independently, meaning that any contributor can create branches with the name they see fit. However, it is worth noting that OpenIM mirrors [OpenIM version skew policy](https://github.com/OpenIMSDK/Open-IM-Server/releases) by maintaining release branches for the most recent three minor releases. The only exception is that the main branch mirrors the latest OpenIM release (3.10) instead of using a `release-` prefixed one. + +```text +main -------------------------------------------. (OpenIM 3.10) +release-3.0.0 \---------------|---------------. (OpenIM 3.00) +release-2.4.0 \---------------. (OpenIM 2.40) +``` + + +### Backport policy + +All new work happens on the main branch, which means that for most cases, one should branch out from there and create the pull request against it. If the change involves adding a feature or patching OpenIM, the maintainers will backport it into the supported release branches. + +## Git operations + +There are everyday tasks related to git that every contributor needs to perform, and this section elaborates on them. + +### Setting up + +Creating a OpenIM fork, cloning it, and setting its upstream remote can be summarized on: + +1. Visit +2. Click the `Fork` button (top right) to establish a cloud-based fork +3. Clone fork to local storage +4. Add to your fork OpenIM remote as upstream + +Once cloned, in code it would look this way: + +```sh +## Clone fork to local storage +export user="your github profile name" +git clone https://github.com/$user/OpenIM.git +# or: git clone git@github.com:$user/OpenIM.git + +## Add OpenIM as upstream to your fork +cd OpenIM +git remote add upstream https://github.com/OpenIMSDK/Open-IM-Server.git +# or: git remote add upstream git@github.com:OpenIMSDK/Open-IM-Server.git + +## Ensure to never push to upstream directly +git remote set-url --push upstream no_push + +## Confirm that your remotes make sense: +git remote -v +``` + +### Branching out + +Every time one wants to work on a new OpenIM feature, we do: + +1. Get local main branch up to date +2. Create a new branch from the main one (i.e.: myfeature branch ) + +In code it would look this way: + +```sh +## Get local main up to date +# Assuming the OpenIM clone is the current working directory +git fetch upstream +git checkout main +git rebase upstream/main + +## Create a new branch from main +git checkout -b myfeature +``` + +### Keeping local branches in sync + +Either when branching out from main or a release one, keep in mind it is worth checking if any change has been pushed upstream by doing: + +```sh +git fetch upstream +git rebase upstream/main +``` + +It is suggested to `fetch` then `rebase` instead of `pull` since the latter does a merge, which leaves merge commits. For this, one can consider changing the local repository configuration by doing `git config branch.autoSetupRebase always` to change the behavior of `git pull`, or another non-merge option such as `git pull --rebase`. + +### Pushing changes + +For commit messages and signatures please refer to the [CONTRIBUTING.md](../../CONTRIBUTING.md) document. + +Nobody should push directly to upstream, even if one has such contributor access; instead, prefer [Github's pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) mechanism to contribute back into OpenIM. For expectations and guidelines about pull requests, consult the [CONTRIBUTING.md](../../CONTRIBUTING.md) document. From f7bd391ed75e266108b17f12f8b5155609ac7528 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Fri, 7 Jul 2023 12:03:20 +0800 Subject: [PATCH 30/73] fix: optimization details Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- CONTRIBUTING.md | 1 - Makefile | 8 +-- docs/.generated_docs | 150 +++++++++++++++++++++---------------------- scripts/lib/color.sh | 8 +-- 4 files changed, 83 insertions(+), 84 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b29008183..24793b187 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -305,7 +305,6 @@ The documentation for Open-IM-Server includes: + [README.md](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/README.md): This file includes the basic information and instructions for getting started with Open-IM-Server. + [CONTRIBUTING.md](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/CONTRIBUTING.md): This file contains guidelines for contributing to Open-IM-Server's codebase, such as how to submit issues, pull requests, and code reviews. -+ [DEVELOPGUIDE.md](https://github.com/OpenIMSDK/.github/blob/main/DEVELOPGUIDE.md): This file provides a more in-depth guide to developing Open-IM-Server, including information on the project's architecture, coding conventions, and testing practices. + [Official Documentation](https://doc.rentsoft.cn/): This is the official documentation for Open-IM-Server, which includes comprehensive information on all of its features, configuration options, and troubleshooting tips. Please obey the following rules to better format the docs, which would greatly improve the reading experience. diff --git a/Makefile b/Makefile index 8b827e91b..8d5c66eb5 100644 --- a/Makefile +++ b/Makefile @@ -39,11 +39,11 @@ Options: BINS Binaries to build. Default is all binaries under cmd. This option is available when using: make {build}(.multiarch) - Example: make build BINS="openim_api openim_cms_api". + Example: make build BINS="openim_api openim_cmdutils". PLATFORMS Platform to build for. Default is linux_arm64 and linux_amd64. This option is available when using: make {build}.multiarch - Example: make multiarch PLATFORMS="linux_s390x linux_mips64 + Example: make multiarch PLATFORMS="linux_s390x linux_mips64 linux_mips64le darwin_amd64 windows_amd64 linux_amd64 linux_arm64". V Set to 1 enable verbose build. Default is 0. @@ -107,11 +107,11 @@ test: cover: @$(MAKE) go.test.cover -## updates: Check for updates to go.mod dependencies ✨ +## updates: Check for updates to go.mod dependencies. ✨ .PHONY: updates @$(MAKE) go.updates -## imports: task to automatically handle import packages in Go files using goimports tool ✨ +## imports: task to automatically handle import packages in Go files using goimports tool. ✨ .PHONY: imports imports: @$(MAKE) go.imports diff --git a/docs/.generated_docs b/docs/.generated_docs index 380e2878d..d46d40d88 100644 --- a/docs/.generated_docs +++ b/docs/.generated_docs @@ -3,82 +3,82 @@ docs/guide/en-US/cmd/iam-apiserver.md docs/guide/en-US/cmd/iam-authz-server.md docs/guide/en-US/cmd/iam-pump.md docs/guide/en-US/cmd/iam-watcher.md -docs/guide/en-US/cmd/iamctl/iamctl.md -docs/guide/en-US/cmd/iamctl/iamctl_color.md -docs/guide/en-US/cmd/iamctl/iamctl_completion.md -docs/guide/en-US/cmd/iamctl/iamctl_info.md -docs/guide/en-US/cmd/iamctl/iamctl_jwt.md -docs/guide/en-US/cmd/iamctl/iamctl_jwt_show.md -docs/guide/en-US/cmd/iamctl/iamctl_jwt_sign.md -docs/guide/en-US/cmd/iamctl/iamctl_jwt_verify.md -docs/guide/en-US/cmd/iamctl/iamctl_new.md -docs/guide/en-US/cmd/iamctl/iamctl_options.md -docs/guide/en-US/cmd/iamctl/iamctl_policy.md -docs/guide/en-US/cmd/iamctl/iamctl_policy_create.md -docs/guide/en-US/cmd/iamctl/iamctl_policy_delete.md -docs/guide/en-US/cmd/iamctl/iamctl_policy_get.md -docs/guide/en-US/cmd/iamctl/iamctl_policy_list.md -docs/guide/en-US/cmd/iamctl/iamctl_policy_update.md -docs/guide/en-US/cmd/iamctl/iamctl_secret.md -docs/guide/en-US/cmd/iamctl/iamctl_secret_create.md -docs/guide/en-US/cmd/iamctl/iamctl_secret_delete.md -docs/guide/en-US/cmd/iamctl/iamctl_secret_get.md -docs/guide/en-US/cmd/iamctl/iamctl_secret_list.md -docs/guide/en-US/cmd/iamctl/iamctl_secret_update.md -docs/guide/en-US/cmd/iamctl/iamctl_set.md -docs/guide/en-US/cmd/iamctl/iamctl_user.md -docs/guide/en-US/cmd/iamctl/iamctl_user_create.md -docs/guide/en-US/cmd/iamctl/iamctl_user_delete.md -docs/guide/en-US/cmd/iamctl/iamctl_user_get.md -docs/guide/en-US/cmd/iamctl/iamctl_user_list.md -docs/guide/en-US/cmd/iamctl/iamctl_user_update.md -docs/guide/en-US/cmd/iamctl/iamctl_validate.md -docs/guide/en-US/cmd/iamctl/iamctl_version.md -docs/guide/en-US/yaml/iamctl/iamctl.yaml -docs/guide/en-US/yaml/iamctl/iamctl_color.yaml -docs/guide/en-US/yaml/iamctl/iamctl_completion.yaml -docs/guide/en-US/yaml/iamctl/iamctl_info.yaml -docs/guide/en-US/yaml/iamctl/iamctl_jwt.yaml -docs/guide/en-US/yaml/iamctl/iamctl_new.yaml -docs/guide/en-US/yaml/iamctl/iamctl_options.yaml -docs/guide/en-US/yaml/iamctl/iamctl_policy.yaml -docs/guide/en-US/yaml/iamctl/iamctl_secret.yaml -docs/guide/en-US/yaml/iamctl/iamctl_set.yaml -docs/guide/en-US/yaml/iamctl/iamctl_user.yaml -docs/guide/en-US/yaml/iamctl/iamctl_validate.yaml -docs/guide/en-US/yaml/iamctl/iamctl_version.yaml +docs/guide/en-US/cmd/openim/openim.md +docs/guide/en-US/cmd/openim/openim_color.md +docs/guide/en-US/cmd/openim/openim_completion.md +docs/guide/en-US/cmd/openim/openim_info.md +docs/guide/en-US/cmd/openim/openim_jwt.md +docs/guide/en-US/cmd/openim/openim_jwt_show.md +docs/guide/en-US/cmd/openim/openim_jwt_sign.md +docs/guide/en-US/cmd/openim/openim_jwt_verify.md +docs/guide/en-US/cmd/openim/openim_new.md +docs/guide/en-US/cmd/openim/openim_options.md +docs/guide/en-US/cmd/openim/openim_policy.md +docs/guide/en-US/cmd/openim/openim_policy_create.md +docs/guide/en-US/cmd/openim/openim_policy_delete.md +docs/guide/en-US/cmd/openim/openim_policy_get.md +docs/guide/en-US/cmd/openim/openim_policy_list.md +docs/guide/en-US/cmd/openim/openim_policy_update.md +docs/guide/en-US/cmd/openim/openim_secret.md +docs/guide/en-US/cmd/openim/openim_secret_create.md +docs/guide/en-US/cmd/openim/openim_secret_delete.md +docs/guide/en-US/cmd/openim/openim_secret_get.md +docs/guide/en-US/cmd/openim/openim_secret_list.md +docs/guide/en-US/cmd/openim/openim_secret_update.md +docs/guide/en-US/cmd/openim/openim_set.md +docs/guide/en-US/cmd/openim/openim_user.md +docs/guide/en-US/cmd/openim/openim_user_create.md +docs/guide/en-US/cmd/openim/openim_user_delete.md +docs/guide/en-US/cmd/openim/openim_user_get.md +docs/guide/en-US/cmd/openim/openim_user_list.md +docs/guide/en-US/cmd/openim/openim_user_update.md +docs/guide/en-US/cmd/openim/openim_validate.md +docs/guide/en-US/cmd/openim/openim_version.md +docs/guide/en-US/yaml/openim/openim.yaml +docs/guide/en-US/yaml/openim/openim_color.yaml +docs/guide/en-US/yaml/openim/openim_completion.yaml +docs/guide/en-US/yaml/openim/openim_info.yaml +docs/guide/en-US/yaml/openim/openim_jwt.yaml +docs/guide/en-US/yaml/openim/openim_new.yaml +docs/guide/en-US/yaml/openim/openim_options.yaml +docs/guide/en-US/yaml/openim/openim_policy.yaml +docs/guide/en-US/yaml/openim/openim_secret.yaml +docs/guide/en-US/yaml/openim/openim_set.yaml +docs/guide/en-US/yaml/openim/openim_user.yaml +docs/guide/en-US/yaml/openim/openim_validate.yaml +docs/guide/en-US/yaml/openim/openim_version.yaml docs/man/man1/iam-apiserver.1 docs/man/man1/iam-authz-server.1 docs/man/man1/iam-pump.1 docs/man/man1/iam-watcher.1 -docs/man/man1/iamctl-color.1 -docs/man/man1/iamctl-completion.1 -docs/man/man1/iamctl-info.1 -docs/man/man1/iamctl-jwt-show.1 -docs/man/man1/iamctl-jwt-sign.1 -docs/man/man1/iamctl-jwt-verify.1 -docs/man/man1/iamctl-jwt.1 -docs/man/man1/iamctl-new.1 -docs/man/man1/iamctl-options.1 -docs/man/man1/iamctl-policy-create.1 -docs/man/man1/iamctl-policy-delete.1 -docs/man/man1/iamctl-policy-get.1 -docs/man/man1/iamctl-policy-list.1 -docs/man/man1/iamctl-policy-update.1 -docs/man/man1/iamctl-policy.1 -docs/man/man1/iamctl-secret-create.1 -docs/man/man1/iamctl-secret-delete.1 -docs/man/man1/iamctl-secret-get.1 -docs/man/man1/iamctl-secret-list.1 -docs/man/man1/iamctl-secret-update.1 -docs/man/man1/iamctl-secret.1 -docs/man/man1/iamctl-set.1 -docs/man/man1/iamctl-user-create.1 -docs/man/man1/iamctl-user-delete.1 -docs/man/man1/iamctl-user-get.1 -docs/man/man1/iamctl-user-list.1 -docs/man/man1/iamctl-user-update.1 -docs/man/man1/iamctl-user.1 -docs/man/man1/iamctl-validate.1 -docs/man/man1/iamctl-version.1 -docs/man/man1/iamctl.1 +docs/man/man1/openim-color.1 +docs/man/man1/openim-completion.1 +docs/man/man1/openim-info.1 +docs/man/man1/openim-jwt-show.1 +docs/man/man1/openim-jwt-sign.1 +docs/man/man1/openim-jwt-verify.1 +docs/man/man1/openim-jwt.1 +docs/man/man1/openim-new.1 +docs/man/man1/openim-options.1 +docs/man/man1/openim-policy-create.1 +docs/man/man1/openim-policy-delete.1 +docs/man/man1/openim-policy-get.1 +docs/man/man1/openim-policy-list.1 +docs/man/man1/openim-policy-update.1 +docs/man/man1/openim-policy.1 +docs/man/man1/openim-secret-create.1 +docs/man/man1/openim-secret-delete.1 +docs/man/man1/openim-secret-get.1 +docs/man/man1/openim-secret-list.1 +docs/man/man1/openim-secret-update.1 +docs/man/man1/openim-secret.1 +docs/man/man1/openim-set.1 +docs/man/man1/openim-user-create.1 +docs/man/man1/openim-user-delete.1 +docs/man/man1/openim-user-get.1 +docs/man/man1/openim-user-list.1 +docs/man/man1/openim-user-update.1 +docs/man/man1/openim-user.1 +docs/man/man1/openim-validate.1 +docs/man/man1/openim-version.1 +docs/man/man1/openim.1 diff --git a/scripts/lib/color.sh b/scripts/lib/color.sh index 52caa78f8..c4bdefe25 100755 --- a/scripts/lib/color.sh +++ b/scripts/lib/color.sh @@ -14,18 +14,18 @@ # limitations under the License. -#Define color variables -#Feature +# Define color variables +# Feature COLOR_NORMAL='\033[0m';COLOR_BOLD='\033[1m';COLOR_DIM='\033[2m';COLOR_UNDER='\033[4m'; COLOR_ITALIC='\033[3m';COLOR_NOITALIC='\033[23m';COLOR_BLINK='\033[5m'; COLOR_REVERSE='\033[7m';COLOR_CONCEAL='\033[8m';COLOR_NOBOLD='\033[22m'; COLOR_NOUNDER='\033[24m';COLOR_NOBLINK='\033[25m'; -#Front color +# Front color COLOR_BLACK='\033[30m';COLOR_RED='\033[31m';COLOR_GREEN='\033[32m';COLOR_YELLOW='\033[33m'; COLOR_BLUE='\033[34m';COLOR_MAGENTA='\033[35m';COLOR_CYAN='\033[36m';COLOR_WHITE='\033[37m'; -#background color +# background color COLOR_BBLACK='\033[40m';COLOR_BRED='\033[41m'; COLOR_BGREEN='\033[42m';COLOR_BYELLOW='\033[43m'; COLOR_BBLUE='\033[44m';COLOR_BMAGENTA='\033[45m'; From d6a89567e5190bc0dc3d7389c5f504306e63ce5f Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Fri, 7 Jul 2023 18:20:08 +0800 Subject: [PATCH 31/73] feat: add all page Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- scripts/batch_start_all.sh | 3 - scripts/build_all_service.sh | 106 +++++++++++++++++++++++++++++------ scripts/check_all.sh | 1 + scripts/push_start.sh | 2 - scripts/sdk_svr_start.sh | 60 -------------------- scripts/start_all.sh | 48 ++++++++++++---- scripts/style_info.cfg | 53 +++++++++++++++--- 7 files changed, 172 insertions(+), 101 deletions(-) delete mode 100755 scripts/sdk_svr_start.sh diff --git a/scripts/batch_start_all.sh b/scripts/batch_start_all.sh index e917d7003..119e85f65 100755 --- a/scripts/batch_start_all.sh +++ b/scripts/batch_start_all.sh @@ -22,7 +22,6 @@ need_to_start_server_shell=( msg_gateway_start.sh push_start.sh msg_transfer_start.sh - sdk_svr_start.sh ) time=`date +"%Y-%m-%d %H:%M:%S"` echo "==========================================================">>../logs/openIM.log 2>&1 & @@ -70,5 +69,3 @@ if [ $success_num == ${#need_to_start_server_shell[*]} ] then echo -e ${YELLOW_PREFIX}"all services build success"${COLOR_SUFFIX} fi - - diff --git a/scripts/build_all_service.sh b/scripts/build_all_service.sh index a6495939d..7359b39b3 100755 --- a/scripts/build_all_service.sh +++ b/scripts/build_all_service.sh @@ -17,11 +17,18 @@ source ./style_info.cfg source ./path_info.cfg source ./function.sh +echo -e "\n" + +echo -e "${BACKGROUND_BLUE}===============> Building all using make build binary files ${COLOR_SUFFIX}" + +echo -e "\n" +echo -e "${BOLD_PREFIX}_____ _ _ _____ _____ _____ _____ _____ _____ _____ _____ _____ ${COLOR_SUFFIX}" + bin_dir="../bin" logs_dir="../logs" sdk_db_dir="../db/sdk/" -#Automatically created when there is no bin, logs folder +# Automatically created when there is no bin, logs folder if [ ! -d $bin_dir ]; then mkdir -p $bin_dir fi @@ -32,19 +39,86 @@ if [ ! -d $sdk_db_dir ]; then mkdir -p $sdk_db_dir fi -#begin path -begin_path=$PWD - -for ((i = 0; i < ${#service_source_root[*]}; i++)); do - cd $begin_path - service_path=${service_source_root[$i]} - cd $service_path - make install - if [ $? -ne 0 ]; then - echo -e "${RED_PREFIX}${service_names[$i]} build failed ${COLOR_SUFFIX}\n" - exit -1 - else - echo -e "${GREEN_PREFIX}${service_names[$i]} successfully be built ${COLOR_SUFFIX}\n" - fi +# OpenIM root path +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. + +source ./style_info.cfg +source ./path_info.cfg +source ./function.sh + +#!/bin/bash + +cd $OPENIM_ROOT + +# Execute 'make build' +make build + +# Get the current operating system and architecture +OS=$(uname -s | tr '[:upper:]' '[:lower:]') +ARCH=$(uname -m) + +# Select the repository home directory based on the operating system and architecture +if [[ "$OS" == "darwin" ]]; then + if [[ "$ARCH" == "x86_64" ]]; then + REPO_DIR="darwin/amd64" + else + REPO_DIR="darwin/386" + fi +elif [[ "$OS" == "linux" ]]; then + if [[ "$ARCH" == "x86_64" ]]; then + REPO_DIR="linux/amd64" + elif [[ "$ARCH" == "arm64" ]]; then + REPO_DIR="linux/arm64" + elif [[ "$ARCH" == "mips64" ]]; then + REPO_DIR="linux/mips64" + elif [[ "$ARCH" == "mips64le" ]]; then + REPO_DIR="linux/mips64le" + elif [[ "$ARCH" == "ppc64le" ]]; then + REPO_DIR="linux/ppc64le" + elif [[ "$ARCH" == "s390x" ]]; then + REPO_DIR="linux/s390x" + else + REPO_DIR="linux/386" + fi +elif [[ "$OS" == "windows" ]]; then + if [[ "$ARCH" == "x86_64" ]]; then + REPO_DIR="windows/amd64" + else + REPO_DIR="windows/386" + fi +else + echo -e "${RED_PREFIX}Unsupported OS: $OS${COLOR_SUFFIX}" + exit 1 +fi + +# Determine if all scripts were successfully built +BUILD_SUCCESS=true +FAILED_SCRIPTS=() + +for binary in $(find _output/bin/platforms/$REPO_DIR -type f); do + if [[ ! -x $binary ]]; then + FAILED_SCRIPTS+=("$binary") + BUILD_SUCCESS=false + fi done -echo -e ${YELLOW_PREFIX}"all services build success"${COLOR_SUFFIX} + +echo -e " \n" + +echo -e "${BOLD_PREFIX}=====================> Build Results <=====================${COLOR_SUFFIX}" + +echo -e " \n" + +if [[ "$BUILD_SUCCESS" == true ]]; then + echo -e "${GREEN_PREFIX}All binaries built successfully.${COLOR_SUFFIX}" +else + echo -e "${RED_PREFIX}Some binary builds failed. Please check the following binary files:${COLOR_SUFFIX}" + for script in "${FAILED_SCRIPTS[@]}"; do + echo -e "${RED_PREFIX}$script${COLOR_SUFFIX}" + done +fi + +echo -e " \n" + +echo -e "${BOLD_PREFIX}============================================================${COLOR_SUFFIX}" + +echo -e " \n" diff --git a/scripts/check_all.sh b/scripts/check_all.sh index 15507102a..65f51c0cf 100755 --- a/scripts/check_all.sh +++ b/scripts/check_all.sh @@ -17,6 +17,7 @@ source ./style_info.cfg source ./path_info.cfg source ./function.sh + service_port_name=( openImWsPort openImApiPort diff --git a/scripts/push_start.sh b/scripts/push_start.sh index d38936a2e..80da0f575 100755 --- a/scripts/push_start.sh +++ b/scripts/push_start.sh @@ -18,8 +18,6 @@ source ./style_info.cfg source ./path_info.cfg source ./function.sh - - list1=$(cat $config_path | grep openImPushPort | awk -F '[:]' '{print $NF}') list2=$(cat $config_path | grep pushPrometheusPort | awk -F '[:]' '{print $NF}') list_to_string $list1 diff --git a/scripts/sdk_svr_start.sh b/scripts/sdk_svr_start.sh deleted file mode 100755 index 29212887a..000000000 --- a/scripts/sdk_svr_start.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env bash -# Copyright © 2023 OpenIM. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -#Include shell font styles and some basic information -source ./style_info.cfg -source ./path_info.cfg -source ./function.sh -ulimit -n 200000 - -ws_address=$(cat $config_path | grep openImWsAddress | awk -F '[ ]' '{print $NF}') -api_address=$(cat $config_path | grep openImApiAddress | awk -F '[ ]' '{print $NF}') -list3=$(cat $config_path | grep openImSdkWsPort | awk -F '[:]' '{print $NF}') -logLevel=$(cat $config_path | grep remainLogLevel | awk -F '[:]' '{print $NF}') -list_to_string $list3 -sdkws_ports=($ports_array) - - - -#Check if the service exists -#If it is exists,kill this process -check=$(ps aux | grep -w ./${sdk_server_name} | grep -v grep | wc -l) -if [ $check -ge 1 ]; then - oldPid=$(ps aux | grep -w ./${sdk_server_name} | grep -v grep | awk '{print $2}') - kill -9 ${oldPid} -fi -#Waiting port recycling -sleep 1 -cd ${sdk_server_binary_root} - echo "==========================start js sdk server===========================">>../logs/openIM.log - nohup ./${sdk_server_name} -openIM_ws_address ${ws_address} -sdk_ws_port ${sdkws_ports[0]} -openIM_api_address ${api_address} -openIM_log_level ${logLevel} >>../logs/openIM.log 2>&1 & - -#Check launched service process -sleep 3 -check=$(ps aux | grep -w ./${sdk_server_name} | grep -v grep | wc -l) -allPorts="" -if [ $check -ge 1 ]; then - allNewPid=$(ps aux | grep -w ./${sdk_server_name} | grep -v grep | awk '{print $2}') - for i in $allNewPid; do - ports=$(netstat -netulp | grep -w ${i} | awk '{print $4}' | awk -F '[:]' '{print $NF}') - allPorts=${allPorts}"$ports " - done - echo -e ${SKY_BLUE_PREFIX}"SERVICE START SUCCESS "${COLOR_SUFFIX} - echo -e ${SKY_BLUE_PREFIX}"SERVICE_NAME: "${COLOR_SUFFIX}${YELLOW_PREFIX}${sdk_server_name}${COLOR_SUFFIX} - echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${YELLOW_PREFIX}${allNewPid}${COLOR_SUFFIX} - echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${YELLOW_PREFIX}${allPorts}${COLOR_SUFFIX} -else - echo -e ${YELLOW_PREFIX}${sdk_server_name}${COLOR_SUFFIX}${RED_PREFIX}"SERVICE START ERROR PLEASE CHECK openIM.log"${COLOR_SUFFIX} -fi diff --git a/scripts/start_all.sh b/scripts/start_all.sh index 1a86db7f3..c16ae3a11 100755 --- a/scripts/start_all.sh +++ b/scripts/start_all.sh @@ -16,30 +16,56 @@ #FIXME This script is the startup script for multiple servers. #FIXME The full names of the shell scripts that need to be started are placed in the `need_to_start_server_shell` array. -#FIXME Put the shell script names here +#Include shell font styles and some basic information +source ./style_info.cfg +source ./path_info.cfg + +# Print title +echo -e "${BOLD_PREFIX}${BLUE_PREFIX}OpenIM Server Start${COLOR_SUFFIX}" + +# Get current time +time=$(date +"%Y-%m-%d %H:%M:%S") + +# Print section separator +echo -e "${PURPLE_PREFIX}==========================================================${COLOR_SUFFIX}" + +# Print server start time +echo -e "${BOLD_PREFIX}${CYAN_PREFIX}Server Start Time: ${time}${COLOR_SUFFIX}" + +# Print section separator +echo -e "${PURPLE_PREFIX}==========================================================${COLOR_SUFFIX}" + +# FIXME Put the shell script names here need_to_start_server_shell=( start_rpc_service.sh push_start.sh msg_transfer_start.sh -# sdk_svr_start.sh msg_gateway_start.sh start_cron.sh ) -time=`date +"%Y-%m-%d %H:%M:%S"` -echo "==========================================================" >> ../logs/openIM.log 2>&1 & -echo "==========================================================" >> ../logs/openIM.log 2>&1 & -echo "==========================================================" >> ../logs/openIM.log 2>&1 & -echo "==========server start time:${time}===========" >> ../logs/openIM.log 2>&1 & -echo "==========================================================" >> ../logs/openIM.log 2>&1 & -echo "==========================================================" >> ../logs/openIM.log 2>&1 & -echo "==========================================================" >> ../logs/openIM.log 2>&1 & +# Loop through the script names and execute them for i in ${need_to_start_server_shell[*]}; do chmod +x $i - echo "=====================exec ${i}======================" >> ../logs/openIM.log + + echo -e "" + # Print script execution message + echo -e "=========> ${YELLOW_PREFIX}Executing ${i}...${COLOR_SUFFIX}" + echo -e "" + ./$i + + # Check if the script executed successfully if [ $? -ne 0 ]; then + # Print error message and exit + echo "${BOLD_PREFIX}${RED_PREFIX}Error executing ${i}. Exiting...${COLOR_SUFFIX}" exit -1 fi done + +# Print section separator +echo "${PURPLE_PREFIX}==========================================================${COLOR_SUFFIX}" + +# Print completion message +echo "${GREEN_PREFIX}${BOLD_PREFIX}OpenIM Server has been started successfully!${COLOR_SUFFIX}" \ No newline at end of file diff --git a/scripts/style_info.cfg b/scripts/style_info.cfg index ff28dcb88..f0b6a32db 100644 --- a/scripts/style_info.cfg +++ b/scripts/style_info.cfg @@ -1,9 +1,44 @@ -#Shell font formatting information -COLOR_SUFFIX="\033[0m" -BLACK_PREFIX="\033[30m" -RED_PREFIX="\033[31m" -GREEN_PREFIX="\033[32m" -YELLOW_PREFIX="\033[33m" -BLUE_PREFIX="\033[34m" -PURPLE_PREFIX="\033[35m" -SKY_BLUE_PREFIX="\033[36m" +COLOR_SUFFIX="\033[0m" # End all colors and special effects + +BLACK_PREFIX="\033[30m" # Black prefix +RED_PREFIX="\033[31m" # Red prefix +GREEN_PREFIX="\033[32m" # Green prefix +YELLOW_PREFIX="\033[33m" # Yellow prefix +BLUE_PREFIX="\033[34m" # Blue prefix +PURPLE_PREFIX="\033[35m" # Purple prefix +SKY_BLUE_PREFIX="\033[36m" # Sky blue prefix +WHITE_PREFIX="\033[37m" # White prefix +BOLD_PREFIX="\033[1m" # Bold prefix +UNDERLINE_PREFIX="\033[4m" # Underline prefix +ITALIC_PREFIX="\033[3m" # Italic prefix + +BACKGROUND_BLACK="\033[40m" # Black background +BACKGROUND_RED="\033[41m" # Red background +BACKGROUND_GREEN="\033[42m" # Green background +BACKGROUND_YELLOW="\033[43m" # Yellow background +BACKGROUND_BLUE="\033[44m" # Blue background +BACKGROUND_PURPLE="\033[45m" # Purple background +BACKGROUND_SKY_BLUE="\033[46m" # Sky blue background +BACKGROUND_WHITE="\033[47m" # White background + +BLINK="\033[5m" # Blinking effect +INVERT="\033[7m" # Invert color +HIDE="\033[8m" # Hide text + +GRAY_PREFIX="\033[90m" # Gray prefix +LIGHT_RED_PREFIX="\033[91m" # Light red prefix +LIGHT_GREEN_PREFIX="\033[92m" # Light green prefix +LIGHT_YELLOW_PREFIX="\033[93m" # Light yellow prefix +LIGHT_BLUE_PREFIX="\033[94m" # Light blue prefix +LIGHT_PURPLE_PREFIX="\033[95m" # Light purple prefix +LIGHT_SKY_BLUE_PREFIX="\033[96m" # Light sky blue prefix +LIGHT_WHITE_PREFIX="\033[97m" # Light white prefix + +BACKGROUND_GRAY="\033[100m" # Gray background +BACKGROUND_LIGHT_RED="\033[101m" # Light red background +BACKGROUND_LIGHT_GREEN="\033[102m" # Light green background +BACKGROUND_LIGHT_YELLOW="\033[103m" # Light yellow background +BACKGROUND_LIGHT_BLUE="\033[104m" # Light blue background +BACKGROUND_LIGHT_PURPLE="\033[105m" # Light purple background +BACKGROUND_LIGHT_SKY_BLUE="\033[106m" # Light sky blue background +BACKGROUND_LIGHT_WHITE="\033[107m" # Light white background From 3e7989c2e7bc36a245735621f0402cf9f85df2ef Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Fri, 7 Jul 2023 19:45:32 +0800 Subject: [PATCH 32/73] fix: binary name modification Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- .gitignore | 14 +- Makefile | 2 +- cmd/openim_api/Makefile | 34 ----- cmd/openim_api/deploy.Dockerfile | 32 ---- cmd/openim_api/main.go | 102 ------------- cmd/openim_cmdutils/Makefile | 34 ----- cmd/openim_cmdutils/main.go | 59 -------- cmd/openim_crontask/Makefile | 34 ----- cmd/openim_crontask/deploy.Dockerfile | 32 ---- cmd/openim_crontask/main.go | 27 ---- cmd/openim_msggateway/Makefile | 34 ----- cmd/openim_msggateway/deploy.Dockerfile | 32 ---- cmd/openim_msggateway/main.go | 29 ---- cmd/openim_msgtransfer/Makefile | 34 ----- cmd/openim_msgtransfer/deploy.Dockerfile | 32 ---- cmd/openim_msgtransfer/main.go | 27 ---- cmd/openim_push/Makefile | 34 ----- cmd/openim_push/deploy.Dockerfile | 32 ---- cmd/openim_push/main.go | 33 ---- cmd/openim_rpc/openim_rpc_auth/Makefile | 34 ----- .../openim_rpc_auth/deploy.Dockerfile | 32 ---- cmd/openim_rpc/openim_rpc_auth/main.go | 33 ---- .../openim_rpc_conversation/Makefile | 34 ----- .../openim_rpc_conversation/deploy.Dockerfile | 32 ---- .../openim_rpc_conversation/main.go | 33 ---- cmd/openim_rpc/openim_rpc_friend/Makefile | 34 ----- .../openim_rpc_friend/deploy.Dockerfile | 32 ---- cmd/openim_rpc/openim_rpc_friend/main.go | 33 ---- cmd/openim_rpc/openim_rpc_group/Makefile | 34 ----- .../openim_rpc_group/deploy.Dockerfile | 32 ---- cmd/openim_rpc/openim_rpc_group/main.go | 33 ---- cmd/openim_rpc/openim_rpc_msg/Makefile | 34 ----- .../openim_rpc_msg/deploy.Dockerfile | 32 ---- cmd/openim_rpc/openim_rpc_msg/main.go | 33 ---- cmd/openim_rpc/openim_rpc_third/Makefile | 34 ----- .../openim_rpc_third/deploy.Dockerfile | 32 ---- cmd/openim_rpc/openim_rpc_third/main.go | 33 ---- cmd/openim_rpc/openim_rpc_user/Makefile | 34 ----- .../openim_rpc_user/deploy.Dockerfile | 32 ---- cmd/openim_rpc/openim_rpc_user/main.go | 33 ---- docs/contrib/cicd-actions.md | 2 +- scripts/lib/golang.sh | 28 ++-- scripts/lib/release.sh | 24 +-- scripts/lib/util.sh | 52 +++---- scripts/make-rules/common.mk | 6 +- scripts/make-rules/golang.mk | 6 +- scripts/path_info.cfg | 142 +++++++++--------- scripts/start_rpc_service.sh | 2 +- 48 files changed, 141 insertions(+), 1471 deletions(-) delete mode 100644 cmd/openim_api/Makefile delete mode 100644 cmd/openim_api/deploy.Dockerfile delete mode 100644 cmd/openim_api/main.go delete mode 100644 cmd/openim_cmdutils/Makefile delete mode 100644 cmd/openim_cmdutils/main.go delete mode 100644 cmd/openim_crontask/Makefile delete mode 100644 cmd/openim_crontask/deploy.Dockerfile delete mode 100644 cmd/openim_crontask/main.go delete mode 100644 cmd/openim_msggateway/Makefile delete mode 100644 cmd/openim_msggateway/deploy.Dockerfile delete mode 100644 cmd/openim_msggateway/main.go delete mode 100644 cmd/openim_msgtransfer/Makefile delete mode 100644 cmd/openim_msgtransfer/deploy.Dockerfile delete mode 100644 cmd/openim_msgtransfer/main.go delete mode 100644 cmd/openim_push/Makefile delete mode 100644 cmd/openim_push/deploy.Dockerfile delete mode 100644 cmd/openim_push/main.go delete mode 100644 cmd/openim_rpc/openim_rpc_auth/Makefile delete mode 100644 cmd/openim_rpc/openim_rpc_auth/deploy.Dockerfile delete mode 100644 cmd/openim_rpc/openim_rpc_auth/main.go delete mode 100644 cmd/openim_rpc/openim_rpc_conversation/Makefile delete mode 100644 cmd/openim_rpc/openim_rpc_conversation/deploy.Dockerfile delete mode 100644 cmd/openim_rpc/openim_rpc_conversation/main.go delete mode 100644 cmd/openim_rpc/openim_rpc_friend/Makefile delete mode 100644 cmd/openim_rpc/openim_rpc_friend/deploy.Dockerfile delete mode 100644 cmd/openim_rpc/openim_rpc_friend/main.go delete mode 100644 cmd/openim_rpc/openim_rpc_group/Makefile delete mode 100644 cmd/openim_rpc/openim_rpc_group/deploy.Dockerfile delete mode 100644 cmd/openim_rpc/openim_rpc_group/main.go delete mode 100644 cmd/openim_rpc/openim_rpc_msg/Makefile delete mode 100644 cmd/openim_rpc/openim_rpc_msg/deploy.Dockerfile delete mode 100644 cmd/openim_rpc/openim_rpc_msg/main.go delete mode 100644 cmd/openim_rpc/openim_rpc_third/Makefile delete mode 100644 cmd/openim_rpc/openim_rpc_third/deploy.Dockerfile delete mode 100644 cmd/openim_rpc/openim_rpc_third/main.go delete mode 100644 cmd/openim_rpc/openim_rpc_user/Makefile delete mode 100644 cmd/openim_rpc/openim_rpc_user/deploy.Dockerfile delete mode 100644 cmd/openim_rpc/openim_rpc_user/main.go diff --git a/.gitignore b/.gitignore index 9cea0d86f..7ca2fe3fd 100644 --- a/.gitignore +++ b/.gitignore @@ -31,16 +31,16 @@ _output/ ### OpenIM deploy ### deploy/openim_demo -deploy/openim_api +deploy/openim-api deploy/openim_msg_gateway deploy/openim_msg_transfer -deploy/openim_push +deploy/openim-push deploy/openim_timer_task -deploy/openim_rpc_user -deploy/openim_rpc_friend -deploy/openim_rpc_group -deploy/openim_rpc_msg -deploy/openim_rpc_auth +deploy/openim-rpc-user +deploy/openim-rpc-friend +deploy/openim-rpc-group +deploy/openim-rpc-msg +deploy/openim-rpc-auth deploy/Open-IM-SDK-Core # files used by the developer diff --git a/Makefile b/Makefile index 8d5c66eb5..5fcc3b8c3 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ Options: BINS Binaries to build. Default is all binaries under cmd. This option is available when using: make {build}(.multiarch) - Example: make build BINS="openim_api openim_cmdutils". + Example: make build BINS="openim-api openim-cmdutils". PLATFORMS Platform to build for. Default is linux_arm64 and linux_amd64. This option is available when using: make {build}.multiarch diff --git a/cmd/openim_api/Makefile b/cmd/openim_api/Makefile deleted file mode 100644 index 4fe47f51f..000000000 --- a/cmd/openim_api/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -.PHONY: all build run gotool install clean help - -NAME=openim_api -BIN_DIR=../../bin/ - -OS:= $(or $(os),linux) -ARCH:=$(or $(arch),amd64) -all: gotool build - -ifeq ($(OS),windows) - -BINARY_NAME=${NAME}.exe - -else - -BINARY_NAME=${NAME} - -endif - -build: - CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} - -run: - @go run ./ - -gotool: - go fmt ./ - go vet ./ - -install:build - mv ${BINARY_NAME} ${BIN_DIR} - -clean: - @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim_api/deploy.Dockerfile b/cmd/openim_api/deploy.Dockerfile deleted file mode 100644 index 24a17af37..000000000 --- a/cmd/openim_api/deploy.Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright © 2023 OpenIM. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM ubuntu - -WORKDIR /Open-IM-Server/bin - -RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ -&&apt-get install net-tools -#Non-interactive operation -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get install -y vim curl tzdata gawk -#Time zone adjusted to East eighth District -RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata -RUN apt-get -qq update \ - && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_api ./ - -VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] - -CMD ["./openim_api","--port", "10002"] diff --git a/cmd/openim_api/main.go b/cmd/openim_api/main.go deleted file mode 100644 index aac83a805..000000000 --- a/cmd/openim_api/main.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "context" - "fmt" - "net" - "os" - "runtime" - "strconv" - "time" - - "net/http" - _ "net/http/pprof" - - "github.com/OpenIMSDK/Open-IM-Server/internal/api" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" - "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" - openKeeper "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry/zookeeper" -) - -func main() { - apiCmd := cmd.NewApiCmd() - apiCmd.AddPortFlag() - apiCmd.AddApi(run) - if err := apiCmd.Execute(); err != nil { - panic(err.Error()) - } -} - -func startPprof() { - runtime.GOMAXPROCS(1) - runtime.SetMutexProfileFraction(1) - runtime.SetBlockProfileRate(1) - if err := http.ListenAndServe(":6060", nil); err != nil { - panic(err) - } - os.Exit(0) -} - -func run(port int) error { - if port == 0 { - return fmt.Errorf("port is empty") - } - rdb, err := cache.NewRedis() - if err != nil { - return err - } - fmt.Println("api start init discov client") - var client discoveryregistry.SvcDiscoveryRegistry - client, err = openKeeper.NewClient(config.Config.Zookeeper.ZkAddr, config.Config.Zookeeper.Schema, - openKeeper.WithFreq(time.Hour), openKeeper.WithUserNameAndPassword( - config.Config.Zookeeper.Username, - config.Config.Zookeeper.Password, - ), openKeeper.WithRoundRobin(), openKeeper.WithTimeout(10), openKeeper.WithLogger(log.NewZkLogger())) - if err != nil { - return err - } - if client.CreateRpcRootNodes(config.GetServiceNames()); err != nil { - return err - } - fmt.Println("api init discov client success") - fmt.Println("api register public config to discov") - if err := client.RegisterConf2Registry(constant.OpenIMCommonConfigKey, config.EncodeConfig()); err != nil { - return err - } - fmt.Println("api register public config to discov success") - router := api.NewGinRouter(client, rdb) - fmt.Println("api init router success") - var address string - if config.Config.Api.ListenIP != "" { - address = net.JoinHostPort(config.Config.Api.ListenIP, strconv.Itoa(port)) - } else { - address = net.JoinHostPort("0.0.0.0", strconv.Itoa(port)) - } - fmt.Println("start api server, address: ", address, ", OpenIM version: ", config.Version) - log.ZInfo(context.Background(), "start server success", "address", address, "version", config.Version) - go startPprof() - err = router.Run(address) - if err != nil { - log.ZError(context.Background(), "api run failed ", err, "address", address) - return err - } - return nil -} diff --git a/cmd/openim_cmdutils/Makefile b/cmd/openim_cmdutils/Makefile deleted file mode 100644 index 9c51aee84..000000000 --- a/cmd/openim_cmdutils/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -.PHONY: all build run gotool install clean help - -NAME=openim_cmd_utils -BIN_DIR=../../bin/ - -OS:= $(or $(os),linux) -ARCH:=$(or $(arch),amd64) -all: gotool build - -ifeq ($(OS),windows) - -BINARY_NAME=${NAME}.exe - -else - -BINARY_NAME=${NAME} - -endif - -build: - CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} - -run: - @go run ./ - -gotool: - go fmt ./ - go vet ./ - -install:build - mv ${BINARY_NAME} ${BIN_DIR} - -clean: - @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim_cmdutils/main.go b/cmd/openim_cmdutils/main.go deleted file mode 100644 index 64079c7e1..000000000 --- a/cmd/openim_cmdutils/main.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" -) - -func main() { - msgUtilsCmd := cmd.NewMsgUtilsCmd("openIMCmdUtils", "openIM cmd utils", nil) - getCmd := cmd.NewGetCmd() - fixCmd := cmd.NewFixCmd() - clearCmd := cmd.NewClearCmd() - seqCmd := cmd.NewSeqCmd() - msgCmd := cmd.NewMsgCmd() - getCmd.AddCommand(seqCmd.GetSeqCmd(), msgCmd.GetMsgCmd()) - getCmd.AddSuperGroupIDFlag() - getCmd.AddUserIDFlag() - getCmd.AddBeginSeqFlag() - getCmd.AddLimitFlag() - // openIM get seq --userID=xxx - // openIM get seq --superGroupID=xxx - // openIM get msg --userID=xxx --beginSeq=100 --limit=10 - // openIM get msg --superGroupID=xxx --beginSeq=100 --limit=10 - - fixCmd.AddCommand(seqCmd.FixSeqCmd()) - fixCmd.AddSuperGroupIDFlag() - fixCmd.AddUserIDFlag() - fixCmd.AddFixAllFlag() - // openIM fix seq --userID=xxx - // openIM fix seq --superGroupID=xxx - // openIM fix seq --fixAll - - clearCmd.AddCommand(msgCmd.ClearMsgCmd()) - clearCmd.AddSuperGroupIDFlag() - clearCmd.AddUserIDFlag() - clearCmd.AddClearAllFlag() - clearCmd.AddBeginSeqFlag() - clearCmd.AddLimitFlag() - // openIM clear msg --userID=xxx --beginSeq=100 --limit=10 - // openIM clear msg --superGroupID=xxx --beginSeq=100 --limit=10 - // openIM clear msg --clearAll - msgUtilsCmd.AddCommand(&getCmd.Command, &fixCmd.Command, &clearCmd.Command) - if err := msgUtilsCmd.Execute(); err != nil { - panic(err) - } -} diff --git a/cmd/openim_crontask/Makefile b/cmd/openim_crontask/Makefile deleted file mode 100644 index 1f09bf396..000000000 --- a/cmd/openim_crontask/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -.PHONY: all build run gotool install clean help - -NAME=openim_cron_task -BIN_DIR=../../bin/ - -OS:= $(or $(os),linux) -ARCH:=$(or $(arch),amd64) -all: gotool build - -ifeq ($(OS),windows) - -BINARY_NAME=${NAME}.exe - -else - -BINARY_NAME=${NAME} - -endif - -build: - CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} - -run: - @go run ./ - -gotool: - go fmt ./ - go vet ./ - -install:build - mv ${BINARY_NAME} ${BIN_DIR} - -clean: - @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim_crontask/deploy.Dockerfile b/cmd/openim_crontask/deploy.Dockerfile deleted file mode 100644 index 07d328474..000000000 --- a/cmd/openim_crontask/deploy.Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright © 2023 OpenIM. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM ubuntu - -WORKDIR /Open-IM-Server/bin - -RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ -&&apt-get install net-tools -#Non-interactive operation -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get install -y vim curl tzdata gawk -#Time zone adjusted to East eighth District -RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata -RUN apt-get -qq update \ - && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_cron_task ./ - -VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] - -CMD ["./openim_cron_task"] diff --git a/cmd/openim_crontask/main.go b/cmd/openim_crontask/main.go deleted file mode 100644 index 73deb8c66..000000000 --- a/cmd/openim_crontask/main.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/OpenIMSDK/Open-IM-Server/internal/tools" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" -) - -func main() { - cronTaskCmd := cmd.NewCronTaskCmd() - if err := cronTaskCmd.Exec(tools.StartCronTask); err != nil { - panic(err.Error()) - } -} diff --git a/cmd/openim_msggateway/Makefile b/cmd/openim_msggateway/Makefile deleted file mode 100644 index 07cad40a9..000000000 --- a/cmd/openim_msggateway/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -.PHONY: all build run gotool install clean help - -NAME=openim_msg_gateway -BIN_DIR=../../bin/ - -OS:= $(or $(os),linux) -ARCH:=$(or $(arch),amd64) -all: gotool build - -ifeq ($(OS),windows) - -BINARY_NAME=${NAME}.exe - -else - -BINARY_NAME=${NAME} - -endif - -build: - CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} - -run: - @go run ./ - -gotool: - go fmt ./ - go vet ./ - -install:build - mv ${BINARY_NAME} ${BIN_DIR} - -clean: - @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim_msggateway/deploy.Dockerfile b/cmd/openim_msggateway/deploy.Dockerfile deleted file mode 100644 index eab3d16db..000000000 --- a/cmd/openim_msggateway/deploy.Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright © 2023 OpenIM. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM ubuntu - -WORKDIR /Open-IM-Server/bin - -RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ -&&apt-get install net-tools -#Non-interactive operation -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get install -y vim curl tzdata gawk -#Time zone adjusted to East eighth District -RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata -RUN apt-get -qq update \ - && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_msg_gateway ./ - -VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] - -CMD ["./openim_msg_gateway","--port", "10140" "--ws_port", "10001", "--prometheus_port", "20240"] diff --git a/cmd/openim_msggateway/main.go b/cmd/openim_msggateway/main.go deleted file mode 100644 index 2782e4fd3..000000000 --- a/cmd/openim_msggateway/main.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" -) - -func main() { - msgGatewayCmd := cmd.NewMsgGatewayCmd() - msgGatewayCmd.AddWsPortFlag() - msgGatewayCmd.AddPortFlag() - msgGatewayCmd.AddPrometheusPortFlag() - if err := msgGatewayCmd.Exec(); err != nil { - panic(err.Error()) - } -} diff --git a/cmd/openim_msgtransfer/Makefile b/cmd/openim_msgtransfer/Makefile deleted file mode 100644 index dc22834de..000000000 --- a/cmd/openim_msgtransfer/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -.PHONY: all build run gotool install clean help - -NAME=openim_msg_transfer -BIN_DIR=../../bin/ - -OS:= $(or $(os),linux) -ARCH:=$(or $(arch),amd64) -all: gotool build - -ifeq ($(OS),windows) - -BINARY_NAME=${NAME}.exe - -else - -BINARY_NAME=${NAME} - -endif - -build: - CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} - -run: - @go run ./ - -gotool: - go fmt ./ - go vet ./ - -install:build - mv ${BINARY_NAME} ${BIN_DIR} - -clean: - @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim_msgtransfer/deploy.Dockerfile b/cmd/openim_msgtransfer/deploy.Dockerfile deleted file mode 100644 index 91767adf4..000000000 --- a/cmd/openim_msgtransfer/deploy.Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright © 2023 OpenIM. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM ubuntu - -WORKDIR /Open-IM-Server/bin - -RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ -&&apt-get install net-tools -#Non-interactive operation -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get install -y vim curl tzdata gawk -#Time zone adjusted to East eighth District -RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata -RUN apt-get -qq update \ - && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_msg_transfer ./ - -VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] - -CMD ["./openim_msg_transfer","--prometheus_port", "21400"] diff --git a/cmd/openim_msgtransfer/main.go b/cmd/openim_msgtransfer/main.go deleted file mode 100644 index aef347793..000000000 --- a/cmd/openim_msgtransfer/main.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" -) - -func main() { - msgTransferCmd := cmd.NewMsgTransferCmd() - msgTransferCmd.AddPrometheusPortFlag() - if err := msgTransferCmd.Exec(); err != nil { - panic(err.Error()) - } -} diff --git a/cmd/openim_push/Makefile b/cmd/openim_push/Makefile deleted file mode 100644 index c67882553..000000000 --- a/cmd/openim_push/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -.PHONY: all build run gotool install clean help - -NAME=openim_push -BIN_DIR=../../bin/ - -OS:= $(or $(os),linux) -ARCH:=$(or $(arch),amd64) -all: gotool build - -ifeq ($(OS),windows) - -BINARY_NAME=${NAME}.exe - -else - -BINARY_NAME=${NAME} - -endif - -build: - CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} - -run: - @go run ./ - -gotool: - go fmt ./ - go vet ./ - -install:build - mv ${BINARY_NAME} ${BIN_DIR} - -clean: - @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim_push/deploy.Dockerfile b/cmd/openim_push/deploy.Dockerfile deleted file mode 100644 index 9b9a542c3..000000000 --- a/cmd/openim_push/deploy.Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright © 2023 OpenIM. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM ubuntu - -WORKDIR /Open-IM-Server/bin - -RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ -&&apt-get install net-tools -#Non-interactive operation -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get install -y vim curl tzdata gawk -#Time zone adjusted to East eighth District -RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata -RUN apt-get -qq update \ - && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_push ./ - -VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] - -CMD ["./openim_push", "--port", "10170", "--prometheus_port", "20170"] diff --git a/cmd/openim_push/main.go b/cmd/openim_push/main.go deleted file mode 100644 index 03db8ae03..000000000 --- a/cmd/openim_push/main.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/OpenIMSDK/Open-IM-Server/internal/push" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" -) - -func main() { - pushCmd := cmd.NewRpcCmd("push") - pushCmd.AddPortFlag() - pushCmd.AddPrometheusPortFlag() - if err := pushCmd.Exec(); err != nil { - panic(err.Error()) - } - if err := pushCmd.StartSvr(config.Config.RpcRegisterName.OpenImPushName, push.Start); err != nil { - panic(err.Error()) - } -} diff --git a/cmd/openim_rpc/openim_rpc_auth/Makefile b/cmd/openim_rpc/openim_rpc_auth/Makefile deleted file mode 100644 index f702786ae..000000000 --- a/cmd/openim_rpc/openim_rpc_auth/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -.PHONY: all build run gotool install clean help - -NAME=openim_auth -BIN_DIR=../../../bin/ - -OS:= $(or $(os),linux) -ARCH:=$(or $(arch),amd64) -all: gotool build - -ifeq ($(OS),windows) - -BINARY_NAME=${NAME}.exe - -else - -BINARY_NAME=${NAME} - -endif - -build: - CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} - -run: - @go run ./ - -gotool: - go fmt ./ - go vet ./ - -install:build - mv ${BINARY_NAME} ${BIN_DIR} - -clean: - @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim_rpc/openim_rpc_auth/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_auth/deploy.Dockerfile deleted file mode 100644 index 1fc1682dd..000000000 --- a/cmd/openim_rpc/openim_rpc_auth/deploy.Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright © 2023 OpenIM. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM ubuntu - -WORKDIR /Open-IM-Server/bin - -RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ -&&apt-get install net-tools -#Non-interactive operation -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get install -y vim curl tzdata gawk -#Time zone adjusted to East eighth District -RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata -RUN apt-get -qq update \ - && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_auth ./ - -VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] - -CMD ["./openim_auth", "--port", "10160"] diff --git a/cmd/openim_rpc/openim_rpc_auth/main.go b/cmd/openim_rpc/openim_rpc_auth/main.go deleted file mode 100644 index 524804988..000000000 --- a/cmd/openim_rpc/openim_rpc_auth/main.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/OpenIMSDK/Open-IM-Server/internal/rpc/auth" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" -) - -func main() { - authCmd := cmd.NewRpcCmd("auth") - authCmd.AddPortFlag() - authCmd.AddPrometheusPortFlag() - if err := authCmd.Exec(); err != nil { - panic(err.Error()) - } - if err := authCmd.StartSvr(config.Config.RpcRegisterName.OpenImAuthName, auth.Start); err != nil { - panic(err.Error()) - } -} diff --git a/cmd/openim_rpc/openim_rpc_conversation/Makefile b/cmd/openim_rpc/openim_rpc_conversation/Makefile deleted file mode 100644 index fa973060e..000000000 --- a/cmd/openim_rpc/openim_rpc_conversation/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -.PHONY: all build run gotool install clean help - -NAME=openim_conversation -BIN_DIR=../../../bin/ - -OS:= $(or $(os),linux) -ARCH:=$(or $(arch),amd64) -all: gotool build - -ifeq ($(OS),windows) - -BINARY_NAME=${NAME}.exe - -else - -BINARY_NAME=${NAME} - -endif - -build: - CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} - -run: - @go run ./ - -gotool: - go fmt ./ - go vet ./ - -install:build - mv ${BINARY_NAME} ${BIN_DIR} - -clean: - @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim_rpc/openim_rpc_conversation/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_conversation/deploy.Dockerfile deleted file mode 100644 index 5aab79104..000000000 --- a/cmd/openim_rpc/openim_rpc_conversation/deploy.Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright © 2023 OpenIM. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM ubuntu - -WORKDIR /Open-IM-Server/bin - -RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ -&&apt-get install net-tools -#Non-interactive operation -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get install -y vim curl tzdata gawk -#Time zone adjusted to East eighth District -RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata -RUN apt-get -qq update \ - && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_conversation ./ - -VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] - -CMD ["./openim_conversation", "--port", "10230", "--prometheus_port","20230"] diff --git a/cmd/openim_rpc/openim_rpc_conversation/main.go b/cmd/openim_rpc/openim_rpc_conversation/main.go deleted file mode 100644 index fec8226f8..000000000 --- a/cmd/openim_rpc/openim_rpc_conversation/main.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/OpenIMSDK/Open-IM-Server/internal/rpc/conversation" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" -) - -func main() { - rpcCmd := cmd.NewRpcCmd("conversation") - rpcCmd.AddPortFlag() - rpcCmd.AddPrometheusPortFlag() - if err := rpcCmd.Exec(); err != nil { - panic(err.Error()) - } - if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImConversationName, conversation.Start); err != nil { - panic(err.Error()) - } -} diff --git a/cmd/openim_rpc/openim_rpc_friend/Makefile b/cmd/openim_rpc/openim_rpc_friend/Makefile deleted file mode 100644 index 8cbb39c2b..000000000 --- a/cmd/openim_rpc/openim_rpc_friend/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -.PHONY: all build run gotool install clean help - -NAME=openim_friend -BIN_DIR=../../../bin/ - -OS:= $(or $(os),linux) -ARCH:=$(or $(arch),amd64) -all: gotool build - -ifeq ($(OS),windows) - -BINARY_NAME=${NAME}.exe - -else - -BINARY_NAME=${NAME} - -endif - -build: - CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} - -run: - @go run ./ - -gotool: - go fmt ./ - go vet ./ - -install:build - mv ${BINARY_NAME} ${BIN_DIR} - -clean: - @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim_rpc/openim_rpc_friend/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_friend/deploy.Dockerfile deleted file mode 100644 index 3aaf86885..000000000 --- a/cmd/openim_rpc/openim_rpc_friend/deploy.Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright © 2023 OpenIM. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM ubuntu - -WORKDIR /Open-IM-Server/bin - -RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ -&&apt-get install net-tools -#Non-interactive operation -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get install -y vim curl tzdata gawk -#Time zone adjusted to East eighth District -RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata -RUN apt-get -qq update \ - && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_friend ./ - -VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] - -CMD ["./openim_friend", "--port", "10120", "--prometheus_port","20120"] diff --git a/cmd/openim_rpc/openim_rpc_friend/main.go b/cmd/openim_rpc/openim_rpc_friend/main.go deleted file mode 100644 index fbd44038e..000000000 --- a/cmd/openim_rpc/openim_rpc_friend/main.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/OpenIMSDK/Open-IM-Server/internal/rpc/friend" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" -) - -func main() { - rpcCmd := cmd.NewRpcCmd("friend") - rpcCmd.AddPortFlag() - rpcCmd.AddPrometheusPortFlag() - if err := rpcCmd.Exec(); err != nil { - panic(err.Error()) - } - if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImFriendName, friend.Start); err != nil { - panic(err.Error()) - } -} diff --git a/cmd/openim_rpc/openim_rpc_group/Makefile b/cmd/openim_rpc/openim_rpc_group/Makefile deleted file mode 100644 index b33dbb259..000000000 --- a/cmd/openim_rpc/openim_rpc_group/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -.PHONY: all build run gotool install clean help - -NAME=openim_group -BIN_DIR=../../../bin/ - -OS:= $(or $(os),linux) -ARCH:=$(or $(arch),amd64) -all: gotool build - -ifeq ($(OS),windows) - -BINARY_NAME=${NAME}.exe - -else - -BINARY_NAME=${NAME} - -endif - -build: - CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} - -run: - @go run ./ - -gotool: - go fmt ./ - go vet ./ - -install:build - mv ${BINARY_NAME} ${BIN_DIR} - -clean: - @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim_rpc/openim_rpc_group/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_group/deploy.Dockerfile deleted file mode 100644 index 1adb00bc5..000000000 --- a/cmd/openim_rpc/openim_rpc_group/deploy.Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright © 2023 OpenIM. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM ubuntu - -WORKDIR /Open-IM-Server/bin - -RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ -&&apt-get install net-tools -#Non-interactive operation -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get install -y vim curl tzdata gawk -#Time zone adjusted to East eighth District -RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata -RUN apt-get -qq update \ - && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_group ./ - -VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] - -CMD ["./openim_group", "--port", "10150", "--prometheus_port","20150"] diff --git a/cmd/openim_rpc/openim_rpc_group/main.go b/cmd/openim_rpc/openim_rpc_group/main.go deleted file mode 100644 index 06baac155..000000000 --- a/cmd/openim_rpc/openim_rpc_group/main.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/OpenIMSDK/Open-IM-Server/internal/rpc/group" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" -) - -func main() { - rpcCmd := cmd.NewRpcCmd("group") - rpcCmd.AddPortFlag() - rpcCmd.AddPrometheusPortFlag() - if err := rpcCmd.Exec(); err != nil { - panic(err.Error()) - } - if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImGroupName, group.Start); err != nil { - panic(err.Error()) - } -} diff --git a/cmd/openim_rpc/openim_rpc_msg/Makefile b/cmd/openim_rpc/openim_rpc_msg/Makefile deleted file mode 100644 index b27c5c420..000000000 --- a/cmd/openim_rpc/openim_rpc_msg/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -.PHONY: all build run gotool install clean help - -NAME=openim_msg -BIN_DIR=../../../bin/ - -OS:= $(or $(os),linux) -ARCH:=$(or $(arch),amd64) -all: gotool build - -ifeq ($(OS),windows) - -BINARY_NAME=${NAME}.exe - -else - -BINARY_NAME=${NAME} - -endif - -build: - CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} - -run: - @go run ./ - -gotool: - go fmt ./ - go vet ./ - -install:build - mv ${BINARY_NAME} ${BIN_DIR} - -clean: - @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim_rpc/openim_rpc_msg/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_msg/deploy.Dockerfile deleted file mode 100644 index 10fd29f0a..000000000 --- a/cmd/openim_rpc/openim_rpc_msg/deploy.Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright © 2023 OpenIM. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM ubuntu - -WORKDIR /Open-IM-Server/bin - -RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ -&&apt-get install net-tools -#Non-interactive operation -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get install -y vim curl tzdata gawk -#Time zone adjusted to East eighth District -RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata -RUN apt-get -qq update \ - && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_msg ./ - -VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] - -CMD ["./openim_msg", "--port", "10130", "--prometheus_port","20130"] diff --git a/cmd/openim_rpc/openim_rpc_msg/main.go b/cmd/openim_rpc/openim_rpc_msg/main.go deleted file mode 100644 index 356081d33..000000000 --- a/cmd/openim_rpc/openim_rpc_msg/main.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/OpenIMSDK/Open-IM-Server/internal/rpc/msg" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" -) - -func main() { - rpcCmd := cmd.NewRpcCmd("msg") - rpcCmd.AddPortFlag() - rpcCmd.AddPrometheusPortFlag() - if err := rpcCmd.Exec(); err != nil { - panic(err.Error()) - } - if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImMsgName, msg.Start); err != nil { - panic(err.Error()) - } -} diff --git a/cmd/openim_rpc/openim_rpc_third/Makefile b/cmd/openim_rpc/openim_rpc_third/Makefile deleted file mode 100644 index b5a7c546a..000000000 --- a/cmd/openim_rpc/openim_rpc_third/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -.PHONY: all build run gotool install clean help - -NAME=openim_third -BIN_DIR=../../../bin/ - -OS:= $(or $(os),linux) -ARCH:=$(or $(arch),amd64) -all: gotool build - -ifeq ($(OS),windows) - -BINARY_NAME=${NAME}.exe - -else - -BINARY_NAME=${NAME} - -endif - -build: - CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} - -run: - @go run ./ - -gotool: - go fmt ./ - go vet ./ - -install:build - mv ${BINARY_NAME} ${BIN_DIR} - -clean: - @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim_rpc/openim_rpc_third/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_third/deploy.Dockerfile deleted file mode 100644 index 7b6a1dfaf..000000000 --- a/cmd/openim_rpc/openim_rpc_third/deploy.Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright © 2023 OpenIM. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM ubuntu - -WORKDIR /Open-IM-Server/bin - -RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ -&&apt-get install net-tools -#Non-interactive operation -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get install -y vim curl tzdata gawk -#Time zone adjusted to East eighth District -RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata -RUN apt-get -qq update \ - && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_third ./ - -VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] - -CMD ["./openim_third", "--port", "10200"] diff --git a/cmd/openim_rpc/openim_rpc_third/main.go b/cmd/openim_rpc/openim_rpc_third/main.go deleted file mode 100644 index c070e6811..000000000 --- a/cmd/openim_rpc/openim_rpc_third/main.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/OpenIMSDK/Open-IM-Server/internal/rpc/third" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" -) - -func main() { - rpcCmd := cmd.NewRpcCmd("third") - rpcCmd.AddPortFlag() - rpcCmd.AddPrometheusPortFlag() - if err := rpcCmd.Exec(); err != nil { - panic(err.Error()) - } - if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImThirdName, third.Start); err != nil { - panic(err.Error()) - } -} diff --git a/cmd/openim_rpc/openim_rpc_user/Makefile b/cmd/openim_rpc/openim_rpc_user/Makefile deleted file mode 100644 index aef2d605a..000000000 --- a/cmd/openim_rpc/openim_rpc_user/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -.PHONY: all build run gotool install clean help - -NAME=openim_user -BIN_DIR=../../../bin/ - -OS:= $(or $(os),linux) -ARCH:=$(or $(arch),amd64) -all: gotool build - -ifeq ($(OS),windows) - -BINARY_NAME=${NAME}.exe - -else - -BINARY_NAME=${NAME} - -endif - -build: - CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} - -run: - @go run ./ - -gotool: - go fmt ./ - go vet ./ - -install:build - mv ${BINARY_NAME} ${BIN_DIR} - -clean: - @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim_rpc/openim_rpc_user/deploy.Dockerfile b/cmd/openim_rpc/openim_rpc_user/deploy.Dockerfile deleted file mode 100644 index 0977dd634..000000000 --- a/cmd/openim_rpc/openim_rpc_user/deploy.Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright © 2023 OpenIM. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM ubuntu - -WORKDIR /Open-IM-Server/bin - -RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ -&&apt-get install net-tools -#Non-interactive operation -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get install -y vim curl tzdata gawk -#Time zone adjusted to East eighth District -RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata -RUN apt-get -qq update \ - && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_user ./ - -VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] - -CMD ["./openim_user", "--port", "10110"] diff --git a/cmd/openim_rpc/openim_rpc_user/main.go b/cmd/openim_rpc/openim_rpc_user/main.go deleted file mode 100644 index 6d6d9008d..000000000 --- a/cmd/openim_rpc/openim_rpc_user/main.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/OpenIMSDK/Open-IM-Server/internal/rpc/user" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" -) - -func main() { - rpcCmd := cmd.NewRpcCmd("user") - rpcCmd.AddPortFlag() - rpcCmd.AddPrometheusPortFlag() - if err := rpcCmd.Exec(); err != nil { - panic(err.Error()) - } - if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImUserName, user.Start); err != nil { - panic(err.Error()) - } -} diff --git a/docs/contrib/cicd-actions.md b/docs/contrib/cicd-actions.md index 2a95860be..99072f369 100644 --- a/docs/contrib/cicd-actions.md +++ b/docs/contrib/cicd-actions.md @@ -71,7 +71,7 @@ DEBUG Whether or not to generate debug symbols. Default is 0. ❓ BINS Binaries to build. Default is all binaries under cmd. 🛠️ This option is available when using: make {build}(.multiarch) 🧰 -Example: make build BINS="openim_api openim_cms_api". +Example: make build BINS="openim-api openim_cms_api". PLATFORMS Platform to build for. Default is linux_arm64 and linux_amd64. 🌍 This option is available when using: make {build}.multiarch 🌍 diff --git a/scripts/lib/golang.sh b/scripts/lib/golang.sh index 56e34703b..b442e1160 100755 --- a/scripts/lib/golang.sh +++ b/scripts/lib/golang.sh @@ -33,20 +33,20 @@ readonly OPENIM_SUPPORTED_CLIENT_PLATFORMS=( # If you update this list, please also update build/BUILD. openim::golang::server_targets() { local targets=( - openim_api - openim_cmdutils - openim_cmdutils - openim_crontask - openim_msggateway - openim_msgtransfer - openim_push - openim_rpc_auth - openim_rpc_conversation - openim_rpc_friend - openim_rpc_group - openim_rpc_msg - openim_rpc_third - openim_rpc_user + openim-api + openim-cmdutils + openim-cmdutils + openim-crontask + openim-msggateway + openim-msgtransfer + openim-push + openim-rpc-auth + openim-rpc-conversation + openim-rpc-friend + openim-rpc-group + openim-rpc-msg + openim-rpc-third + openim-rpc-user ) echo "${targets[@]}" } diff --git a/scripts/lib/release.sh b/scripts/lib/release.sh index d6cdcdfcb..a198e1ae6 100755 --- a/scripts/lib/release.sh +++ b/scripts/lib/release.sh @@ -407,18 +407,18 @@ function openim::release::package_iam_manifests_tarball() { mkdir -p "${dst_dir}" cp -r ${src_dir}/* "${dst_dir}" #cp "${src_dir}/openim-api.yaml" "${dst_dir}" - #cp "${src_dir}/openim_cmdutils.yaml" "${dst_dir}" - #cp "${src_dir}/openim_crontask.yaml" "${dst_dir}" - #cp "${src_dir}/openim_msggateway.yaml" "${dst_dir}" - #cp "${src_dir}/openim_msgtransfer.yaml" "${dst_dir}" - #cp "${src_dir}/openim_push.yaml" "${dst_dir}" - #cp "${src_dir}/openim_rpc_auth.yaml" "${dst_dir}" - #cp "${src_dir}/openim_rpc_conversation.yaml" "${dst_dir}" - #cp "${src_dir}/openim_rpc_friend.yaml" "${dst_dir}" - #cp "${src_dir}/openim_rpc_group.yaml" "${dst_dir}" - #cp "${src_dir}/openim_rpc_msg.yaml" "${dst_dir}" - #cp "${src_dir}/openim_rpc_third.yaml" "${dst_dir}" - #cp "${src_dir}/openim_rpc_user.yaml" "${dst_dir}" + #cp "${src_dir}/openim-cmdutils.yaml" "${dst_dir}" + #cp "${src_dir}/openim-crontask.yaml" "${dst_dir}" + #cp "${src_dir}/openim-msggateway.yaml" "${dst_dir}" + #cp "${src_dir}/openim-msgtransfer.yaml" "${dst_dir}" + #cp "${src_dir}/openim-push.yaml" "${dst_dir}" + #cp "${src_dir}/openim-rpc-auth.yaml" "${dst_dir}" + #cp "${src_dir}/openim-rpc-conversation.yaml" "${dst_dir}" + #cp "${src_dir}/openim-rpc-friend.yaml" "${dst_dir}" + #cp "${src_dir}/openim-rpc-group.yaml" "${dst_dir}" + #cp "${src_dir}/openim-rpc-msg.yaml" "${dst_dir}" + #cp "${src_dir}/openim-rpc-third.yaml" "${dst_dir}" + #cp "${src_dir}/openim-rpc-user.yaml" "${dst_dir}" #cp "${OPENIM_ROOT}/cluster/gce/gci/health-monitor.sh" "${dst_dir}/health-monitor.sh" openim::release::clean_cruft diff --git a/scripts/lib/util.sh b/scripts/lib/util.sh index 63d32eb79..04eb21de7 100755 --- a/scripts/lib/util.sh +++ b/scripts/lib/util.sh @@ -233,35 +233,35 @@ openim::util::gen-docs() { "${gendocs}" "${dest}/docs/guide/en-US/cmd/imctl/" mkdir -p "${dest}/docs/guide/en-US/cmd/" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_api" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_cmdutils" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_crontask" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_msggateway" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_msgtransfer" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_push" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_rpc_auth" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_rpc_conversation" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_rpc_friend" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_rpc_group" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_rpc_msg" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_rpc_third" - "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim_rpc_user" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-api" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-cmdutils" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-crontask" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-msggateway" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-msgtransfer" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-push" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-auth" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-conversation" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-friend" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-group" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-msg" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-third" + "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-user" "${geniamdocs}" "${dest}/docs/guide/en-US/cmd/imctl" "imctl" mkdir -p "${dest}/docs/man/man1/" -"${genman}" "${dest}/docs/man/man1/" "openim_api" -"${genman}" "${dest}/docs/man/man1/" "openim_cmdutils" -"${genman}" "${dest}/docs/man/man1/" "openim_crontask" -"${genman}" "${dest}/docs/man/man1/" "openim_msggateway" -"${genman}" "${dest}/docs/man/man1/" "openim_msgtransfer" -"${genman}" "${dest}/docs/man/man1/" "openim_push" -"${genman}" "${dest}/docs/man/man1/" "openim_rpc_auth" -"${genman}" "${dest}/docs/man/man1/" "openim_rpc_conversation" -"${genman}" "${dest}/docs/man/man1/" "openim_rpc_friend" -"${genman}" "${dest}/docs/man/man1/" "openim_rpc_group" -"${genman}" "${dest}/docs/man/man1/" "openim_rpc_msg" -"${genman}" "${dest}/docs/man/man1/" "openim_rpc_third" -"${genman}" "${dest}/docs/man/man1/" "openim_rpc_user" +"${genman}" "${dest}/docs/man/man1/" "openim-api" +"${genman}" "${dest}/docs/man/man1/" "openim-cmdutils" +"${genman}" "${dest}/docs/man/man1/" "openim-crontask" +"${genman}" "${dest}/docs/man/man1/" "openim-msggateway" +"${genman}" "${dest}/docs/man/man1/" "openim-msgtransfer" +"${genman}" "${dest}/docs/man/man1/" "openim-push" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-auth" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-conversation" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-friend" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-group" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-msg" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-third" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-user" mkdir -p "${dest}/docs/guide/en-US/yaml/imctl/" "${genyaml}" "${dest}/docs/guide/en-US/yaml/imct/" diff --git a/scripts/make-rules/common.mk b/scripts/make-rules/common.mk index e99585a76..d471ec112 100644 --- a/scripts/make-rules/common.mk +++ b/scripts/make-rules/common.mk @@ -147,18 +147,18 @@ endef # Here are some examples of builds define MAKEFILE_EXAMPLE -# make build BINS=openim_api Only a single openim_api binary is built. +# make build BINS=openim-api Only a single openim-api binary is built. # make -j (nproc) all Run tidy gen add-copyright format lint cover build concurrently. # make gen Generate all necessary files. # make release Build release binaries for all platforms. # make verify-copyright Verify the license headers for all files. # make install-deepcopy-gen Install deepcopy-gen tools if the license is missing. -# make build BINS=openim_api V=1 DEBUG=1 Build debug binaries for only openim_api. +# make build BINS=openim-api V=1 DEBUG=1 Build debug binaries for only openim-api. # make multiarch -j PLATFORMS="linux_arm64 linux_amd64" V=1 Build binaries for both platforms. endef export MAKEFILE_EXAMPLE -# Define all help functions @printf "\n\033[1mCurrent openim_api version information: $(shell openim_api version):\033[0m\n\n" +# Define all help functions @printf "\n\033[1mCurrent openim-api version information: $(shell openim-api version):\033[0m\n\n" define makeallhelp @printf "\n\033[1mMake example:\033[0m\n\n" $(call MAKEFILE_EXAMPLE) diff --git a/scripts/make-rules/golang.mk b/scripts/make-rules/golang.mk index f0042e043..9602dbbbd 100644 --- a/scripts/make-rules/golang.mk +++ b/scripts/make-rules/golang.mk @@ -71,20 +71,20 @@ EXCLUDE_TESTS=github.com/OpenIMSDK/Open-IM-Server/test github.com/OpenIMSDK/Open # ❯ tree -L 1 cmd # cmd # ├── openim-sdk-core/ - main.go -# ├── openim_api +# ├── openim-api # ├── openim_cms_api # ├── openim_cron_task # ├── openim_demo # ├── openim_msg_gateway # ├── openim_msg_transfer -# ├── openim_push +# ├── openim-push # ├── rpc/openim_admin_cms/ - main.go # └── test/ - main.go # COMMAND=openim # PLATFORM=linux_amd64 # OS=linux # ARCH=amd64 -# BINS=openim_api openim_cms_api openim_cron_task openim_demo openim_msg_gateway openim_msg_transfer openim_push +# BINS=openim-api openim_cms_api openim_cron_task openim_demo openim_msg_gateway openim_msg_transfer openim-push # BIN_DIR=/root/workspaces/OpenIM/_output/bin # ============================================================================== diff --git a/scripts/path_info.cfg b/scripts/path_info.cfg index 530455f36..aa36164ae 100644 --- a/scripts/path_info.cfg +++ b/scripts/path_info.cfg @@ -1,92 +1,96 @@ -#Don't put the space between "=" -msg_gateway_name="openim_msg_gateway" -msg_gateway_binary_root="../bin/" +# Determine the architecture and version +architecture=$(uname -m) +version=$(uname -s | tr '[:upper:]' '[:lower:]') + +# Define the supported architectures and corresponding bin directories +declare -A supported_architectures=( + ["linux-amd64"]="_output/bin/platforms/linux/amd64" + ["linux-arm64"]="_output/bin/platforms/linux/arm64" + ["linux-mips64"]="_output/bin/platforms/linux/mips64" + ["linux-mips64le"]="_output/bin/platforms/linux/mips64le" + ["linux-ppc64le"]="_output/bin/platforms/linux/ppc64le" + ["linux-s390x"]="_output/bin/platforms/linux/s390x" + ["darwin-amd64"]="_output/bin/platforms/darwin/amd64" + ["windows-amd64"]="_output/bin/platforms/windows/amd64" + ["linux-x86_64"]="_output/bin/platforms/linux/amd64" # Alias for linux-amd64 + ["darwin-x86_64"]="_output/bin/platforms/darwin/amd64" # Alias for darwin-amd64 +) + +# Check if the architecture and version are supported +if [[ -z ${supported_architectures["$version-$architecture"]} ]]; then + echo "Unsupported architecture: $architecture or version: $version" + exit 1 +fi + +# Set the BIN_DIR based on the architecture and version +BIN_DIR=${supported_architectures["$version-$architecture"]} + +echo "BIN_DIR: $BIN_DIR" + +# Don't put the space between "=" +msg_gateway_name="openim-msggateway" +msg_gateway_binary_root= $BIN_DIR msg_gateway_source_root="../cmd/msggateway/" msg_name="openim_msg" -msg_binary_root="../bin/" +msg_binary_root=$BIN_DIR msg_source_root="../cmd/rpc/msg/" -push_name="openim_push" -push_binary_root="../bin/" +push_name="openim-push" +push_binary_root=$BIN_DIR push_source_root="../cmd/push/" - - msg_transfer_name="openim_msg_transfer" -msg_transfer_binary_root="../bin/" +msg_transfer_binary_root=$BIN_DIR msg_transfer_source_root="../cmd/msgtransfer/" msg_transfer_service_num=4 - -sdk_server_name="openim_sdk_server" -sdk_server_binary_root="../bin/" -sdk_server_source_root="../cmd/Open-IM-SDK-Core/" - - cron_task_name="openim_cron_task" -cron_task_binary_root="../bin/" +cron_task_binary_root=$BIN_DIR cron_task_source_root="../cmd/crontask/" cmd_utils_name="openim_cmd_utils" -cmd_utils_binary_root="../bin/" +cmd_utils_binary_root=$BIN_DIR cmd_utils_source_root="../cmd/cmduitls/" -#Global configuration file default dir +# Global configuration file default dir config_path="../config/config.yaml" -#servicefile dir path +# servicefile dir path service_source_root=( - #api service file - ../cmd/api/ - #rpc service file - ../cmd/rpc/user/ - ../cmd/rpc/friend/ - ../cmd/rpc/group/ - ../cmd/rpc/auth/ - ../cmd/rpc/conversation/ - ../cmd/rpc/third/ - ../cmd/crontask - ${msg_gateway_source_root} - ${msg_transfer_source_root} - ${msg_source_root} - ${push_source_root} - # ${sdk_server_source_root} -) -#service filename -service_names=( - #api service filename - openim_api - #rpc service filename - openim_user - openim_friend - openim_group - openim_auth - openim_conversation - openim_third - openim_cron_task - ${msg_gateway_name} - ${msg_transfer_name} - ${msg_name} - ${push_name} - # ${sdk_server_name} + # api service file + "../cmd/api/" + # rpc service file + "../cmd/rpc/user/" + "../cmd/rpc/friend/" + "../cmd/rpc/group/" + "../cmd/rpc/auth/" + "../cmd/rpc/conversation/" + "../cmd/rpc/third/" + "../cmd/crontask" + "${msg_gateway_source_root}" + "${msg_transfer_source_root}" + "${msg_source_root}" + "${push_source_root}" + # "${sdk_server_source_root}" ) -image_names=( - #api service file - api - #rpc service file - user - friend - group - auth - conversation - third - cron_task - msg_gateway - msg_transfer - msg - push - # sdk_server -) +# service filename +service_names=( + # api service filename + "openim-api" + # rpc service filename + "openim_user" + "openim_friend" + "openim_group" + "openim_auth" + "openim_conversation" + "openim_third" + "openim_cron_task" + "${msg_gateway_name}" + "${msg_transfer_name}" + "${msg_name}" + "${push_name}" + # "${sdk_server_name}" +) \ No newline at end of file diff --git a/scripts/start_rpc_service.sh b/scripts/start_rpc_service.sh index d0c2f721d..379dba474 100755 --- a/scripts/start_rpc_service.sh +++ b/scripts/start_rpc_service.sh @@ -21,7 +21,7 @@ source ./function.sh #service filename service_filename=( #api - openim_api + openim-api #rpc openim_user openim_friend From 905c55caadedd7c3c7a4408d7eb59df95dfa33a4 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Fri, 7 Jul 2023 19:48:34 +0800 Subject: [PATCH 33/73] fix: binary name modification Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- cmd/openim-api/Makefile | 34 ++++++ cmd/openim-api/deploy.Dockerfile | 32 ++++++ cmd/openim-api/main.go | 102 ++++++++++++++++++ cmd/openim-cmdutils/Makefile | 34 ++++++ cmd/openim-cmdutils/main.go | 59 ++++++++++ cmd/openim-crontask/Makefile | 34 ++++++ cmd/openim-crontask/deploy.Dockerfile | 32 ++++++ cmd/openim-crontask/main.go | 27 +++++ cmd/openim-msggateway/Makefile | 34 ++++++ cmd/openim-msggateway/deploy.Dockerfile | 32 ++++++ cmd/openim-msggateway/main.go | 29 +++++ cmd/openim-msgtransfer/Makefile | 34 ++++++ cmd/openim-msgtransfer/deploy.Dockerfile | 32 ++++++ cmd/openim-msgtransfer/main.go | 27 +++++ cmd/openim-push/Makefile | 34 ++++++ cmd/openim-push/deploy.Dockerfile | 32 ++++++ cmd/openim-push/main.go | 33 ++++++ cmd/openim-rpc/openim-rpc-auth/Makefile | 34 ++++++ .../openim-rpc-auth/deploy.Dockerfile | 32 ++++++ cmd/openim-rpc/openim-rpc-auth/main.go | 33 ++++++ .../openim-rpc-conversation/Makefile | 34 ++++++ .../openim-rpc-conversation/deploy.Dockerfile | 32 ++++++ .../openim-rpc-conversation/main.go | 33 ++++++ cmd/openim-rpc/openim-rpc-friend/Makefile | 34 ++++++ .../openim-rpc-friend/deploy.Dockerfile | 32 ++++++ cmd/openim-rpc/openim-rpc-friend/main.go | 33 ++++++ cmd/openim-rpc/openim-rpc-group/Makefile | 34 ++++++ .../openim-rpc-group/deploy.Dockerfile | 32 ++++++ cmd/openim-rpc/openim-rpc-group/main.go | 33 ++++++ cmd/openim-rpc/openim-rpc-msg/Makefile | 34 ++++++ .../openim-rpc-msg/deploy.Dockerfile | 32 ++++++ cmd/openim-rpc/openim-rpc-msg/main.go | 33 ++++++ cmd/openim-rpc/openim-rpc-third/Makefile | 34 ++++++ .../openim-rpc-third/deploy.Dockerfile | 32 ++++++ cmd/openim-rpc/openim-rpc-third/main.go | 33 ++++++ cmd/openim-rpc/openim-rpc-user/Makefile | 34 ++++++ .../openim-rpc-user/deploy.Dockerfile | 32 ++++++ cmd/openim-rpc/openim-rpc-user/main.go | 33 ++++++ 38 files changed, 1334 insertions(+) create mode 100644 cmd/openim-api/Makefile create mode 100644 cmd/openim-api/deploy.Dockerfile create mode 100644 cmd/openim-api/main.go create mode 100644 cmd/openim-cmdutils/Makefile create mode 100644 cmd/openim-cmdutils/main.go create mode 100644 cmd/openim-crontask/Makefile create mode 100644 cmd/openim-crontask/deploy.Dockerfile create mode 100644 cmd/openim-crontask/main.go create mode 100644 cmd/openim-msggateway/Makefile create mode 100644 cmd/openim-msggateway/deploy.Dockerfile create mode 100644 cmd/openim-msggateway/main.go create mode 100644 cmd/openim-msgtransfer/Makefile create mode 100644 cmd/openim-msgtransfer/deploy.Dockerfile create mode 100644 cmd/openim-msgtransfer/main.go create mode 100644 cmd/openim-push/Makefile create mode 100644 cmd/openim-push/deploy.Dockerfile create mode 100644 cmd/openim-push/main.go create mode 100644 cmd/openim-rpc/openim-rpc-auth/Makefile create mode 100644 cmd/openim-rpc/openim-rpc-auth/deploy.Dockerfile create mode 100644 cmd/openim-rpc/openim-rpc-auth/main.go create mode 100644 cmd/openim-rpc/openim-rpc-conversation/Makefile create mode 100644 cmd/openim-rpc/openim-rpc-conversation/deploy.Dockerfile create mode 100644 cmd/openim-rpc/openim-rpc-conversation/main.go create mode 100644 cmd/openim-rpc/openim-rpc-friend/Makefile create mode 100644 cmd/openim-rpc/openim-rpc-friend/deploy.Dockerfile create mode 100644 cmd/openim-rpc/openim-rpc-friend/main.go create mode 100644 cmd/openim-rpc/openim-rpc-group/Makefile create mode 100644 cmd/openim-rpc/openim-rpc-group/deploy.Dockerfile create mode 100644 cmd/openim-rpc/openim-rpc-group/main.go create mode 100644 cmd/openim-rpc/openim-rpc-msg/Makefile create mode 100644 cmd/openim-rpc/openim-rpc-msg/deploy.Dockerfile create mode 100644 cmd/openim-rpc/openim-rpc-msg/main.go create mode 100644 cmd/openim-rpc/openim-rpc-third/Makefile create mode 100644 cmd/openim-rpc/openim-rpc-third/deploy.Dockerfile create mode 100644 cmd/openim-rpc/openim-rpc-third/main.go create mode 100644 cmd/openim-rpc/openim-rpc-user/Makefile create mode 100644 cmd/openim-rpc/openim-rpc-user/deploy.Dockerfile create mode 100644 cmd/openim-rpc/openim-rpc-user/main.go diff --git a/cmd/openim-api/Makefile b/cmd/openim-api/Makefile new file mode 100644 index 000000000..b72c6c717 --- /dev/null +++ b/cmd/openim-api/Makefile @@ -0,0 +1,34 @@ +.PHONY: all build run gotool install clean help + +NAME=openim-api +BIN_DIR=../../bin/ + +OS:= $(or $(os),linux) +ARCH:=$(or $(arch),amd64) +all: gotool build + +ifeq ($(OS),windows) + +BINARY_NAME=${NAME}.exe + +else + +BINARY_NAME=${NAME} + +endif + +build: + CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} + +run: + @go run ./ + +gotool: + go fmt ./ + go vet ./ + +install:build + mv ${BINARY_NAME} ${BIN_DIR} + +clean: + @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim-api/deploy.Dockerfile b/cmd/openim-api/deploy.Dockerfile new file mode 100644 index 000000000..3479e0cd3 --- /dev/null +++ b/cmd/openim-api/deploy.Dockerfile @@ -0,0 +1,32 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM ubuntu + +WORKDIR /Open-IM-Server/bin + +RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ +&&apt-get install net-tools +#Non-interactive operation +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get install -y vim curl tzdata gawk +#Time zone adjusted to East eighth District +RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata +RUN apt-get -qq update \ + && apt-get -qq install -y --no-install-recommends ca-certificates curl +COPY ./openim-api ./ + +VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] + +CMD ["./openim-api","--port", "10002"] diff --git a/cmd/openim-api/main.go b/cmd/openim-api/main.go new file mode 100644 index 000000000..aac83a805 --- /dev/null +++ b/cmd/openim-api/main.go @@ -0,0 +1,102 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "context" + "fmt" + "net" + "os" + "runtime" + "strconv" + "time" + + "net/http" + _ "net/http/pprof" + + "github.com/OpenIMSDK/Open-IM-Server/internal/api" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" + "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" + openKeeper "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry/zookeeper" +) + +func main() { + apiCmd := cmd.NewApiCmd() + apiCmd.AddPortFlag() + apiCmd.AddApi(run) + if err := apiCmd.Execute(); err != nil { + panic(err.Error()) + } +} + +func startPprof() { + runtime.GOMAXPROCS(1) + runtime.SetMutexProfileFraction(1) + runtime.SetBlockProfileRate(1) + if err := http.ListenAndServe(":6060", nil); err != nil { + panic(err) + } + os.Exit(0) +} + +func run(port int) error { + if port == 0 { + return fmt.Errorf("port is empty") + } + rdb, err := cache.NewRedis() + if err != nil { + return err + } + fmt.Println("api start init discov client") + var client discoveryregistry.SvcDiscoveryRegistry + client, err = openKeeper.NewClient(config.Config.Zookeeper.ZkAddr, config.Config.Zookeeper.Schema, + openKeeper.WithFreq(time.Hour), openKeeper.WithUserNameAndPassword( + config.Config.Zookeeper.Username, + config.Config.Zookeeper.Password, + ), openKeeper.WithRoundRobin(), openKeeper.WithTimeout(10), openKeeper.WithLogger(log.NewZkLogger())) + if err != nil { + return err + } + if client.CreateRpcRootNodes(config.GetServiceNames()); err != nil { + return err + } + fmt.Println("api init discov client success") + fmt.Println("api register public config to discov") + if err := client.RegisterConf2Registry(constant.OpenIMCommonConfigKey, config.EncodeConfig()); err != nil { + return err + } + fmt.Println("api register public config to discov success") + router := api.NewGinRouter(client, rdb) + fmt.Println("api init router success") + var address string + if config.Config.Api.ListenIP != "" { + address = net.JoinHostPort(config.Config.Api.ListenIP, strconv.Itoa(port)) + } else { + address = net.JoinHostPort("0.0.0.0", strconv.Itoa(port)) + } + fmt.Println("start api server, address: ", address, ", OpenIM version: ", config.Version) + log.ZInfo(context.Background(), "start server success", "address", address, "version", config.Version) + go startPprof() + err = router.Run(address) + if err != nil { + log.ZError(context.Background(), "api run failed ", err, "address", address) + return err + } + return nil +} diff --git a/cmd/openim-cmdutils/Makefile b/cmd/openim-cmdutils/Makefile new file mode 100644 index 000000000..9c51aee84 --- /dev/null +++ b/cmd/openim-cmdutils/Makefile @@ -0,0 +1,34 @@ +.PHONY: all build run gotool install clean help + +NAME=openim_cmd_utils +BIN_DIR=../../bin/ + +OS:= $(or $(os),linux) +ARCH:=$(or $(arch),amd64) +all: gotool build + +ifeq ($(OS),windows) + +BINARY_NAME=${NAME}.exe + +else + +BINARY_NAME=${NAME} + +endif + +build: + CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} + +run: + @go run ./ + +gotool: + go fmt ./ + go vet ./ + +install:build + mv ${BINARY_NAME} ${BIN_DIR} + +clean: + @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim-cmdutils/main.go b/cmd/openim-cmdutils/main.go new file mode 100644 index 000000000..64079c7e1 --- /dev/null +++ b/cmd/openim-cmdutils/main.go @@ -0,0 +1,59 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" +) + +func main() { + msgUtilsCmd := cmd.NewMsgUtilsCmd("openIMCmdUtils", "openIM cmd utils", nil) + getCmd := cmd.NewGetCmd() + fixCmd := cmd.NewFixCmd() + clearCmd := cmd.NewClearCmd() + seqCmd := cmd.NewSeqCmd() + msgCmd := cmd.NewMsgCmd() + getCmd.AddCommand(seqCmd.GetSeqCmd(), msgCmd.GetMsgCmd()) + getCmd.AddSuperGroupIDFlag() + getCmd.AddUserIDFlag() + getCmd.AddBeginSeqFlag() + getCmd.AddLimitFlag() + // openIM get seq --userID=xxx + // openIM get seq --superGroupID=xxx + // openIM get msg --userID=xxx --beginSeq=100 --limit=10 + // openIM get msg --superGroupID=xxx --beginSeq=100 --limit=10 + + fixCmd.AddCommand(seqCmd.FixSeqCmd()) + fixCmd.AddSuperGroupIDFlag() + fixCmd.AddUserIDFlag() + fixCmd.AddFixAllFlag() + // openIM fix seq --userID=xxx + // openIM fix seq --superGroupID=xxx + // openIM fix seq --fixAll + + clearCmd.AddCommand(msgCmd.ClearMsgCmd()) + clearCmd.AddSuperGroupIDFlag() + clearCmd.AddUserIDFlag() + clearCmd.AddClearAllFlag() + clearCmd.AddBeginSeqFlag() + clearCmd.AddLimitFlag() + // openIM clear msg --userID=xxx --beginSeq=100 --limit=10 + // openIM clear msg --superGroupID=xxx --beginSeq=100 --limit=10 + // openIM clear msg --clearAll + msgUtilsCmd.AddCommand(&getCmd.Command, &fixCmd.Command, &clearCmd.Command) + if err := msgUtilsCmd.Execute(); err != nil { + panic(err) + } +} diff --git a/cmd/openim-crontask/Makefile b/cmd/openim-crontask/Makefile new file mode 100644 index 000000000..1f09bf396 --- /dev/null +++ b/cmd/openim-crontask/Makefile @@ -0,0 +1,34 @@ +.PHONY: all build run gotool install clean help + +NAME=openim_cron_task +BIN_DIR=../../bin/ + +OS:= $(or $(os),linux) +ARCH:=$(or $(arch),amd64) +all: gotool build + +ifeq ($(OS),windows) + +BINARY_NAME=${NAME}.exe + +else + +BINARY_NAME=${NAME} + +endif + +build: + CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} + +run: + @go run ./ + +gotool: + go fmt ./ + go vet ./ + +install:build + mv ${BINARY_NAME} ${BIN_DIR} + +clean: + @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim-crontask/deploy.Dockerfile b/cmd/openim-crontask/deploy.Dockerfile new file mode 100644 index 000000000..07d328474 --- /dev/null +++ b/cmd/openim-crontask/deploy.Dockerfile @@ -0,0 +1,32 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM ubuntu + +WORKDIR /Open-IM-Server/bin + +RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ +&&apt-get install net-tools +#Non-interactive operation +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get install -y vim curl tzdata gawk +#Time zone adjusted to East eighth District +RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata +RUN apt-get -qq update \ + && apt-get -qq install -y --no-install-recommends ca-certificates curl +COPY ./openim_cron_task ./ + +VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] + +CMD ["./openim_cron_task"] diff --git a/cmd/openim-crontask/main.go b/cmd/openim-crontask/main.go new file mode 100644 index 000000000..73deb8c66 --- /dev/null +++ b/cmd/openim-crontask/main.go @@ -0,0 +1,27 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "github.com/OpenIMSDK/Open-IM-Server/internal/tools" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" +) + +func main() { + cronTaskCmd := cmd.NewCronTaskCmd() + if err := cronTaskCmd.Exec(tools.StartCronTask); err != nil { + panic(err.Error()) + } +} diff --git a/cmd/openim-msggateway/Makefile b/cmd/openim-msggateway/Makefile new file mode 100644 index 000000000..07cad40a9 --- /dev/null +++ b/cmd/openim-msggateway/Makefile @@ -0,0 +1,34 @@ +.PHONY: all build run gotool install clean help + +NAME=openim_msg_gateway +BIN_DIR=../../bin/ + +OS:= $(or $(os),linux) +ARCH:=$(or $(arch),amd64) +all: gotool build + +ifeq ($(OS),windows) + +BINARY_NAME=${NAME}.exe + +else + +BINARY_NAME=${NAME} + +endif + +build: + CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} + +run: + @go run ./ + +gotool: + go fmt ./ + go vet ./ + +install:build + mv ${BINARY_NAME} ${BIN_DIR} + +clean: + @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim-msggateway/deploy.Dockerfile b/cmd/openim-msggateway/deploy.Dockerfile new file mode 100644 index 000000000..eab3d16db --- /dev/null +++ b/cmd/openim-msggateway/deploy.Dockerfile @@ -0,0 +1,32 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM ubuntu + +WORKDIR /Open-IM-Server/bin + +RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ +&&apt-get install net-tools +#Non-interactive operation +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get install -y vim curl tzdata gawk +#Time zone adjusted to East eighth District +RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata +RUN apt-get -qq update \ + && apt-get -qq install -y --no-install-recommends ca-certificates curl +COPY ./openim_msg_gateway ./ + +VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] + +CMD ["./openim_msg_gateway","--port", "10140" "--ws_port", "10001", "--prometheus_port", "20240"] diff --git a/cmd/openim-msggateway/main.go b/cmd/openim-msggateway/main.go new file mode 100644 index 000000000..2782e4fd3 --- /dev/null +++ b/cmd/openim-msggateway/main.go @@ -0,0 +1,29 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" +) + +func main() { + msgGatewayCmd := cmd.NewMsgGatewayCmd() + msgGatewayCmd.AddWsPortFlag() + msgGatewayCmd.AddPortFlag() + msgGatewayCmd.AddPrometheusPortFlag() + if err := msgGatewayCmd.Exec(); err != nil { + panic(err.Error()) + } +} diff --git a/cmd/openim-msgtransfer/Makefile b/cmd/openim-msgtransfer/Makefile new file mode 100644 index 000000000..dc22834de --- /dev/null +++ b/cmd/openim-msgtransfer/Makefile @@ -0,0 +1,34 @@ +.PHONY: all build run gotool install clean help + +NAME=openim_msg_transfer +BIN_DIR=../../bin/ + +OS:= $(or $(os),linux) +ARCH:=$(or $(arch),amd64) +all: gotool build + +ifeq ($(OS),windows) + +BINARY_NAME=${NAME}.exe + +else + +BINARY_NAME=${NAME} + +endif + +build: + CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} + +run: + @go run ./ + +gotool: + go fmt ./ + go vet ./ + +install:build + mv ${BINARY_NAME} ${BIN_DIR} + +clean: + @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim-msgtransfer/deploy.Dockerfile b/cmd/openim-msgtransfer/deploy.Dockerfile new file mode 100644 index 000000000..91767adf4 --- /dev/null +++ b/cmd/openim-msgtransfer/deploy.Dockerfile @@ -0,0 +1,32 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM ubuntu + +WORKDIR /Open-IM-Server/bin + +RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ +&&apt-get install net-tools +#Non-interactive operation +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get install -y vim curl tzdata gawk +#Time zone adjusted to East eighth District +RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata +RUN apt-get -qq update \ + && apt-get -qq install -y --no-install-recommends ca-certificates curl +COPY ./openim_msg_transfer ./ + +VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] + +CMD ["./openim_msg_transfer","--prometheus_port", "21400"] diff --git a/cmd/openim-msgtransfer/main.go b/cmd/openim-msgtransfer/main.go new file mode 100644 index 000000000..aef347793 --- /dev/null +++ b/cmd/openim-msgtransfer/main.go @@ -0,0 +1,27 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" +) + +func main() { + msgTransferCmd := cmd.NewMsgTransferCmd() + msgTransferCmd.AddPrometheusPortFlag() + if err := msgTransferCmd.Exec(); err != nil { + panic(err.Error()) + } +} diff --git a/cmd/openim-push/Makefile b/cmd/openim-push/Makefile new file mode 100644 index 000000000..b417faecf --- /dev/null +++ b/cmd/openim-push/Makefile @@ -0,0 +1,34 @@ +.PHONY: all build run gotool install clean help + +NAME=openim-push +BIN_DIR=../../bin/ + +OS:= $(or $(os),linux) +ARCH:=$(or $(arch),amd64) +all: gotool build + +ifeq ($(OS),windows) + +BINARY_NAME=${NAME}.exe + +else + +BINARY_NAME=${NAME} + +endif + +build: + CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} + +run: + @go run ./ + +gotool: + go fmt ./ + go vet ./ + +install:build + mv ${BINARY_NAME} ${BIN_DIR} + +clean: + @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim-push/deploy.Dockerfile b/cmd/openim-push/deploy.Dockerfile new file mode 100644 index 000000000..67774a6f7 --- /dev/null +++ b/cmd/openim-push/deploy.Dockerfile @@ -0,0 +1,32 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM ubuntu + +WORKDIR /Open-IM-Server/bin + +RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ +&&apt-get install net-tools +#Non-interactive operation +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get install -y vim curl tzdata gawk +#Time zone adjusted to East eighth District +RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata +RUN apt-get -qq update \ + && apt-get -qq install -y --no-install-recommends ca-certificates curl +COPY ./openim-push ./ + +VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] + +CMD ["./openim-push", "--port", "10170", "--prometheus_port", "20170"] diff --git a/cmd/openim-push/main.go b/cmd/openim-push/main.go new file mode 100644 index 000000000..03db8ae03 --- /dev/null +++ b/cmd/openim-push/main.go @@ -0,0 +1,33 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "github.com/OpenIMSDK/Open-IM-Server/internal/push" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" +) + +func main() { + pushCmd := cmd.NewRpcCmd("push") + pushCmd.AddPortFlag() + pushCmd.AddPrometheusPortFlag() + if err := pushCmd.Exec(); err != nil { + panic(err.Error()) + } + if err := pushCmd.StartSvr(config.Config.RpcRegisterName.OpenImPushName, push.Start); err != nil { + panic(err.Error()) + } +} diff --git a/cmd/openim-rpc/openim-rpc-auth/Makefile b/cmd/openim-rpc/openim-rpc-auth/Makefile new file mode 100644 index 000000000..f702786ae --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-auth/Makefile @@ -0,0 +1,34 @@ +.PHONY: all build run gotool install clean help + +NAME=openim_auth +BIN_DIR=../../../bin/ + +OS:= $(or $(os),linux) +ARCH:=$(or $(arch),amd64) +all: gotool build + +ifeq ($(OS),windows) + +BINARY_NAME=${NAME}.exe + +else + +BINARY_NAME=${NAME} + +endif + +build: + CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} + +run: + @go run ./ + +gotool: + go fmt ./ + go vet ./ + +install:build + mv ${BINARY_NAME} ${BIN_DIR} + +clean: + @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim-rpc/openim-rpc-auth/deploy.Dockerfile b/cmd/openim-rpc/openim-rpc-auth/deploy.Dockerfile new file mode 100644 index 000000000..1fc1682dd --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-auth/deploy.Dockerfile @@ -0,0 +1,32 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM ubuntu + +WORKDIR /Open-IM-Server/bin + +RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ +&&apt-get install net-tools +#Non-interactive operation +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get install -y vim curl tzdata gawk +#Time zone adjusted to East eighth District +RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata +RUN apt-get -qq update \ + && apt-get -qq install -y --no-install-recommends ca-certificates curl +COPY ./openim_auth ./ + +VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] + +CMD ["./openim_auth", "--port", "10160"] diff --git a/cmd/openim-rpc/openim-rpc-auth/main.go b/cmd/openim-rpc/openim-rpc-auth/main.go new file mode 100644 index 000000000..524804988 --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-auth/main.go @@ -0,0 +1,33 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "github.com/OpenIMSDK/Open-IM-Server/internal/rpc/auth" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" +) + +func main() { + authCmd := cmd.NewRpcCmd("auth") + authCmd.AddPortFlag() + authCmd.AddPrometheusPortFlag() + if err := authCmd.Exec(); err != nil { + panic(err.Error()) + } + if err := authCmd.StartSvr(config.Config.RpcRegisterName.OpenImAuthName, auth.Start); err != nil { + panic(err.Error()) + } +} diff --git a/cmd/openim-rpc/openim-rpc-conversation/Makefile b/cmd/openim-rpc/openim-rpc-conversation/Makefile new file mode 100644 index 000000000..fa973060e --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-conversation/Makefile @@ -0,0 +1,34 @@ +.PHONY: all build run gotool install clean help + +NAME=openim_conversation +BIN_DIR=../../../bin/ + +OS:= $(or $(os),linux) +ARCH:=$(or $(arch),amd64) +all: gotool build + +ifeq ($(OS),windows) + +BINARY_NAME=${NAME}.exe + +else + +BINARY_NAME=${NAME} + +endif + +build: + CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} + +run: + @go run ./ + +gotool: + go fmt ./ + go vet ./ + +install:build + mv ${BINARY_NAME} ${BIN_DIR} + +clean: + @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim-rpc/openim-rpc-conversation/deploy.Dockerfile b/cmd/openim-rpc/openim-rpc-conversation/deploy.Dockerfile new file mode 100644 index 000000000..5aab79104 --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-conversation/deploy.Dockerfile @@ -0,0 +1,32 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM ubuntu + +WORKDIR /Open-IM-Server/bin + +RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ +&&apt-get install net-tools +#Non-interactive operation +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get install -y vim curl tzdata gawk +#Time zone adjusted to East eighth District +RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata +RUN apt-get -qq update \ + && apt-get -qq install -y --no-install-recommends ca-certificates curl +COPY ./openim_conversation ./ + +VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] + +CMD ["./openim_conversation", "--port", "10230", "--prometheus_port","20230"] diff --git a/cmd/openim-rpc/openim-rpc-conversation/main.go b/cmd/openim-rpc/openim-rpc-conversation/main.go new file mode 100644 index 000000000..fec8226f8 --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-conversation/main.go @@ -0,0 +1,33 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "github.com/OpenIMSDK/Open-IM-Server/internal/rpc/conversation" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" +) + +func main() { + rpcCmd := cmd.NewRpcCmd("conversation") + rpcCmd.AddPortFlag() + rpcCmd.AddPrometheusPortFlag() + if err := rpcCmd.Exec(); err != nil { + panic(err.Error()) + } + if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImConversationName, conversation.Start); err != nil { + panic(err.Error()) + } +} diff --git a/cmd/openim-rpc/openim-rpc-friend/Makefile b/cmd/openim-rpc/openim-rpc-friend/Makefile new file mode 100644 index 000000000..8cbb39c2b --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-friend/Makefile @@ -0,0 +1,34 @@ +.PHONY: all build run gotool install clean help + +NAME=openim_friend +BIN_DIR=../../../bin/ + +OS:= $(or $(os),linux) +ARCH:=$(or $(arch),amd64) +all: gotool build + +ifeq ($(OS),windows) + +BINARY_NAME=${NAME}.exe + +else + +BINARY_NAME=${NAME} + +endif + +build: + CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} + +run: + @go run ./ + +gotool: + go fmt ./ + go vet ./ + +install:build + mv ${BINARY_NAME} ${BIN_DIR} + +clean: + @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim-rpc/openim-rpc-friend/deploy.Dockerfile b/cmd/openim-rpc/openim-rpc-friend/deploy.Dockerfile new file mode 100644 index 000000000..3aaf86885 --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-friend/deploy.Dockerfile @@ -0,0 +1,32 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM ubuntu + +WORKDIR /Open-IM-Server/bin + +RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ +&&apt-get install net-tools +#Non-interactive operation +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get install -y vim curl tzdata gawk +#Time zone adjusted to East eighth District +RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata +RUN apt-get -qq update \ + && apt-get -qq install -y --no-install-recommends ca-certificates curl +COPY ./openim_friend ./ + +VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] + +CMD ["./openim_friend", "--port", "10120", "--prometheus_port","20120"] diff --git a/cmd/openim-rpc/openim-rpc-friend/main.go b/cmd/openim-rpc/openim-rpc-friend/main.go new file mode 100644 index 000000000..fbd44038e --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-friend/main.go @@ -0,0 +1,33 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "github.com/OpenIMSDK/Open-IM-Server/internal/rpc/friend" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" +) + +func main() { + rpcCmd := cmd.NewRpcCmd("friend") + rpcCmd.AddPortFlag() + rpcCmd.AddPrometheusPortFlag() + if err := rpcCmd.Exec(); err != nil { + panic(err.Error()) + } + if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImFriendName, friend.Start); err != nil { + panic(err.Error()) + } +} diff --git a/cmd/openim-rpc/openim-rpc-group/Makefile b/cmd/openim-rpc/openim-rpc-group/Makefile new file mode 100644 index 000000000..b33dbb259 --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-group/Makefile @@ -0,0 +1,34 @@ +.PHONY: all build run gotool install clean help + +NAME=openim_group +BIN_DIR=../../../bin/ + +OS:= $(or $(os),linux) +ARCH:=$(or $(arch),amd64) +all: gotool build + +ifeq ($(OS),windows) + +BINARY_NAME=${NAME}.exe + +else + +BINARY_NAME=${NAME} + +endif + +build: + CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} + +run: + @go run ./ + +gotool: + go fmt ./ + go vet ./ + +install:build + mv ${BINARY_NAME} ${BIN_DIR} + +clean: + @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim-rpc/openim-rpc-group/deploy.Dockerfile b/cmd/openim-rpc/openim-rpc-group/deploy.Dockerfile new file mode 100644 index 000000000..1adb00bc5 --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-group/deploy.Dockerfile @@ -0,0 +1,32 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM ubuntu + +WORKDIR /Open-IM-Server/bin + +RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ +&&apt-get install net-tools +#Non-interactive operation +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get install -y vim curl tzdata gawk +#Time zone adjusted to East eighth District +RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata +RUN apt-get -qq update \ + && apt-get -qq install -y --no-install-recommends ca-certificates curl +COPY ./openim_group ./ + +VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] + +CMD ["./openim_group", "--port", "10150", "--prometheus_port","20150"] diff --git a/cmd/openim-rpc/openim-rpc-group/main.go b/cmd/openim-rpc/openim-rpc-group/main.go new file mode 100644 index 000000000..06baac155 --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-group/main.go @@ -0,0 +1,33 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "github.com/OpenIMSDK/Open-IM-Server/internal/rpc/group" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" +) + +func main() { + rpcCmd := cmd.NewRpcCmd("group") + rpcCmd.AddPortFlag() + rpcCmd.AddPrometheusPortFlag() + if err := rpcCmd.Exec(); err != nil { + panic(err.Error()) + } + if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImGroupName, group.Start); err != nil { + panic(err.Error()) + } +} diff --git a/cmd/openim-rpc/openim-rpc-msg/Makefile b/cmd/openim-rpc/openim-rpc-msg/Makefile new file mode 100644 index 000000000..b27c5c420 --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-msg/Makefile @@ -0,0 +1,34 @@ +.PHONY: all build run gotool install clean help + +NAME=openim_msg +BIN_DIR=../../../bin/ + +OS:= $(or $(os),linux) +ARCH:=$(or $(arch),amd64) +all: gotool build + +ifeq ($(OS),windows) + +BINARY_NAME=${NAME}.exe + +else + +BINARY_NAME=${NAME} + +endif + +build: + CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} + +run: + @go run ./ + +gotool: + go fmt ./ + go vet ./ + +install:build + mv ${BINARY_NAME} ${BIN_DIR} + +clean: + @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim-rpc/openim-rpc-msg/deploy.Dockerfile b/cmd/openim-rpc/openim-rpc-msg/deploy.Dockerfile new file mode 100644 index 000000000..10fd29f0a --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-msg/deploy.Dockerfile @@ -0,0 +1,32 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM ubuntu + +WORKDIR /Open-IM-Server/bin + +RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ +&&apt-get install net-tools +#Non-interactive operation +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get install -y vim curl tzdata gawk +#Time zone adjusted to East eighth District +RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata +RUN apt-get -qq update \ + && apt-get -qq install -y --no-install-recommends ca-certificates curl +COPY ./openim_msg ./ + +VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] + +CMD ["./openim_msg", "--port", "10130", "--prometheus_port","20130"] diff --git a/cmd/openim-rpc/openim-rpc-msg/main.go b/cmd/openim-rpc/openim-rpc-msg/main.go new file mode 100644 index 000000000..356081d33 --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-msg/main.go @@ -0,0 +1,33 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "github.com/OpenIMSDK/Open-IM-Server/internal/rpc/msg" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" +) + +func main() { + rpcCmd := cmd.NewRpcCmd("msg") + rpcCmd.AddPortFlag() + rpcCmd.AddPrometheusPortFlag() + if err := rpcCmd.Exec(); err != nil { + panic(err.Error()) + } + if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImMsgName, msg.Start); err != nil { + panic(err.Error()) + } +} diff --git a/cmd/openim-rpc/openim-rpc-third/Makefile b/cmd/openim-rpc/openim-rpc-third/Makefile new file mode 100644 index 000000000..b5a7c546a --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-third/Makefile @@ -0,0 +1,34 @@ +.PHONY: all build run gotool install clean help + +NAME=openim_third +BIN_DIR=../../../bin/ + +OS:= $(or $(os),linux) +ARCH:=$(or $(arch),amd64) +all: gotool build + +ifeq ($(OS),windows) + +BINARY_NAME=${NAME}.exe + +else + +BINARY_NAME=${NAME} + +endif + +build: + CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} + +run: + @go run ./ + +gotool: + go fmt ./ + go vet ./ + +install:build + mv ${BINARY_NAME} ${BIN_DIR} + +clean: + @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim-rpc/openim-rpc-third/deploy.Dockerfile b/cmd/openim-rpc/openim-rpc-third/deploy.Dockerfile new file mode 100644 index 000000000..7b6a1dfaf --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-third/deploy.Dockerfile @@ -0,0 +1,32 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM ubuntu + +WORKDIR /Open-IM-Server/bin + +RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ +&&apt-get install net-tools +#Non-interactive operation +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get install -y vim curl tzdata gawk +#Time zone adjusted to East eighth District +RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata +RUN apt-get -qq update \ + && apt-get -qq install -y --no-install-recommends ca-certificates curl +COPY ./openim_third ./ + +VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] + +CMD ["./openim_third", "--port", "10200"] diff --git a/cmd/openim-rpc/openim-rpc-third/main.go b/cmd/openim-rpc/openim-rpc-third/main.go new file mode 100644 index 000000000..c070e6811 --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-third/main.go @@ -0,0 +1,33 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "github.com/OpenIMSDK/Open-IM-Server/internal/rpc/third" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" +) + +func main() { + rpcCmd := cmd.NewRpcCmd("third") + rpcCmd.AddPortFlag() + rpcCmd.AddPrometheusPortFlag() + if err := rpcCmd.Exec(); err != nil { + panic(err.Error()) + } + if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImThirdName, third.Start); err != nil { + panic(err.Error()) + } +} diff --git a/cmd/openim-rpc/openim-rpc-user/Makefile b/cmd/openim-rpc/openim-rpc-user/Makefile new file mode 100644 index 000000000..aef2d605a --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-user/Makefile @@ -0,0 +1,34 @@ +.PHONY: all build run gotool install clean help + +NAME=openim_user +BIN_DIR=../../../bin/ + +OS:= $(or $(os),linux) +ARCH:=$(or $(arch),amd64) +all: gotool build + +ifeq ($(OS),windows) + +BINARY_NAME=${NAME}.exe + +else + +BINARY_NAME=${NAME} + +endif + +build: + CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH}; go build -ldflags="-w -s" -o ${BINARY_NAME} + +run: + @go run ./ + +gotool: + go fmt ./ + go vet ./ + +install:build + mv ${BINARY_NAME} ${BIN_DIR} + +clean: + @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi diff --git a/cmd/openim-rpc/openim-rpc-user/deploy.Dockerfile b/cmd/openim-rpc/openim-rpc-user/deploy.Dockerfile new file mode 100644 index 000000000..0977dd634 --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-user/deploy.Dockerfile @@ -0,0 +1,32 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM ubuntu + +WORKDIR /Open-IM-Server/bin + +RUN apt-get update && apt-get install apt-transport-https && apt-get install procps\ +&&apt-get install net-tools +#Non-interactive operation +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get install -y vim curl tzdata gawk +#Time zone adjusted to East eighth District +RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata +RUN apt-get -qq update \ + && apt-get -qq install -y --no-install-recommends ca-certificates curl +COPY ./openim_user ./ + +VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] + +CMD ["./openim_user", "--port", "10110"] diff --git a/cmd/openim-rpc/openim-rpc-user/main.go b/cmd/openim-rpc/openim-rpc-user/main.go new file mode 100644 index 000000000..6d6d9008d --- /dev/null +++ b/cmd/openim-rpc/openim-rpc-user/main.go @@ -0,0 +1,33 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "github.com/OpenIMSDK/Open-IM-Server/internal/rpc/user" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" +) + +func main() { + rpcCmd := cmd.NewRpcCmd("user") + rpcCmd.AddPortFlag() + rpcCmd.AddPrometheusPortFlag() + if err := rpcCmd.Exec(); err != nil { + panic(err.Error()) + } + if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImUserName, user.Start); err != nil { + panic(err.Error()) + } +} From 049ae6eb803e1d61a488845b6b2f92380fc00b31 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Fri, 7 Jul 2023 21:27:07 +0800 Subject: [PATCH 34/73] feat: add chmod Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- .gitignore | 4 +- cmd/openim-crontask/Makefile | 2 +- cmd/openim-crontask/deploy.Dockerfile | 4 +- cmd/openim-msggateway/Makefile | 2 +- cmd/openim-msggateway/deploy.Dockerfile | 4 +- cmd/openim-msgtransfer/Makefile | 2 +- cmd/openim-msgtransfer/deploy.Dockerfile | 4 +- cmd/openim-rpc/openim-rpc-auth/Makefile | 2 +- .../openim-rpc-auth/deploy.Dockerfile | 4 +- .../openim-rpc-conversation/Makefile | 2 +- .../openim-rpc-conversation/deploy.Dockerfile | 4 +- cmd/openim-rpc/openim-rpc-friend/Makefile | 2 +- .../openim-rpc-friend/deploy.Dockerfile | 4 +- cmd/openim-rpc/openim-rpc-group/Makefile | 2 +- .../openim-rpc-group/deploy.Dockerfile | 4 +- cmd/openim-rpc/openim-rpc-msg/Makefile | 2 +- .../openim-rpc-msg/deploy.Dockerfile | 4 +- cmd/openim-rpc/openim-rpc-third/Makefile | 2 +- .../openim-rpc-third/deploy.Dockerfile | 4 +- cmd/openim-rpc/openim-rpc-user/Makefile | 2 +- .../openim-rpc-user/deploy.Dockerfile | 4 +- docs/.generated_docs | 14 +++--- scripts/batch_build_all_service.sh | 18 ++++--- scripts/batch_start_all.sh | 33 ++++++++----- scripts/build_images.sh | 25 ---------- scripts/check_all.sh | 2 +- scripts/make-rules/golang.mk | 12 ++--- scripts/msg_gateway_start.sh | 14 +++--- scripts/msg_transfer_start.sh | 30 +++++++----- scripts/path_info.cfg | 47 ++++++++++--------- scripts/start_all.sh | 7 ++- scripts/start_cron.sh | 8 ++-- scripts/start_rpc_service.sh | 26 ++++++---- scripts/style_info.cfg | 0 34 files changed, 156 insertions(+), 144 deletions(-) delete mode 100755 scripts/build_images.sh mode change 100644 => 100755 scripts/path_info.cfg mode change 100644 => 100755 scripts/style_info.cfg diff --git a/.gitignore b/.gitignore index 7ca2fe3fd..a547540a7 100644 --- a/.gitignore +++ b/.gitignore @@ -32,8 +32,8 @@ _output/ ### OpenIM deploy ### deploy/openim_demo deploy/openim-api -deploy/openim_msg_gateway -deploy/openim_msg_transfer +deploy/openim-rpc-msg_gateway +deploy/openim-rpc-msg_transfer deploy/openim-push deploy/openim_timer_task deploy/openim-rpc-user diff --git a/cmd/openim-crontask/Makefile b/cmd/openim-crontask/Makefile index 1f09bf396..58528ccb5 100644 --- a/cmd/openim-crontask/Makefile +++ b/cmd/openim-crontask/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=openim_cron_task +NAME=openim-crontask BIN_DIR=../../bin/ OS:= $(or $(os),linux) diff --git a/cmd/openim-crontask/deploy.Dockerfile b/cmd/openim-crontask/deploy.Dockerfile index 07d328474..ba5230c42 100644 --- a/cmd/openim-crontask/deploy.Dockerfile +++ b/cmd/openim-crontask/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_cron_task ./ +COPY ./openim-crontask ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./openim_cron_task"] +CMD ["./openim-crontask"] diff --git a/cmd/openim-msggateway/Makefile b/cmd/openim-msggateway/Makefile index 07cad40a9..d5238beae 100644 --- a/cmd/openim-msggateway/Makefile +++ b/cmd/openim-msggateway/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=openim_msg_gateway +NAME=openim-rpc-msg_gateway BIN_DIR=../../bin/ OS:= $(or $(os),linux) diff --git a/cmd/openim-msggateway/deploy.Dockerfile b/cmd/openim-msggateway/deploy.Dockerfile index eab3d16db..7ba0d2235 100644 --- a/cmd/openim-msggateway/deploy.Dockerfile +++ b/cmd/openim-msggateway/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_msg_gateway ./ +COPY ./openim-rpc-msg_gateway ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./openim_msg_gateway","--port", "10140" "--ws_port", "10001", "--prometheus_port", "20240"] +CMD ["./openim-rpc-msg_gateway","--port", "10140" "--ws_port", "10001", "--prometheus_port", "20240"] diff --git a/cmd/openim-msgtransfer/Makefile b/cmd/openim-msgtransfer/Makefile index dc22834de..9d4aa7dec 100644 --- a/cmd/openim-msgtransfer/Makefile +++ b/cmd/openim-msgtransfer/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=openim_msg_transfer +NAME=openim-rpc-msg_transfer BIN_DIR=../../bin/ OS:= $(or $(os),linux) diff --git a/cmd/openim-msgtransfer/deploy.Dockerfile b/cmd/openim-msgtransfer/deploy.Dockerfile index 91767adf4..747b7f422 100644 --- a/cmd/openim-msgtransfer/deploy.Dockerfile +++ b/cmd/openim-msgtransfer/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_msg_transfer ./ +COPY ./openim-rpc-msg_transfer ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./openim_msg_transfer","--prometheus_port", "21400"] +CMD ["./openim-rpc-msg_transfer","--prometheus_port", "21400"] diff --git a/cmd/openim-rpc/openim-rpc-auth/Makefile b/cmd/openim-rpc/openim-rpc-auth/Makefile index f702786ae..068d50fd8 100644 --- a/cmd/openim-rpc/openim-rpc-auth/Makefile +++ b/cmd/openim-rpc/openim-rpc-auth/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=openim_auth +NAME=openim-rpc-auth BIN_DIR=../../../bin/ OS:= $(or $(os),linux) diff --git a/cmd/openim-rpc/openim-rpc-auth/deploy.Dockerfile b/cmd/openim-rpc/openim-rpc-auth/deploy.Dockerfile index 1fc1682dd..b529860f9 100644 --- a/cmd/openim-rpc/openim-rpc-auth/deploy.Dockerfile +++ b/cmd/openim-rpc/openim-rpc-auth/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_auth ./ +COPY ./openim-rpc-auth ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./openim_auth", "--port", "10160"] +CMD ["./openim-rpc-auth", "--port", "10160"] diff --git a/cmd/openim-rpc/openim-rpc-conversation/Makefile b/cmd/openim-rpc/openim-rpc-conversation/Makefile index fa973060e..4c9726011 100644 --- a/cmd/openim-rpc/openim-rpc-conversation/Makefile +++ b/cmd/openim-rpc/openim-rpc-conversation/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=openim_conversation +NAME=openim-rpc-conversation BIN_DIR=../../../bin/ OS:= $(or $(os),linux) diff --git a/cmd/openim-rpc/openim-rpc-conversation/deploy.Dockerfile b/cmd/openim-rpc/openim-rpc-conversation/deploy.Dockerfile index 5aab79104..33e5c93c1 100644 --- a/cmd/openim-rpc/openim-rpc-conversation/deploy.Dockerfile +++ b/cmd/openim-rpc/openim-rpc-conversation/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_conversation ./ +COPY ./openim-rpc-conversation ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./openim_conversation", "--port", "10230", "--prometheus_port","20230"] +CMD ["./openim-rpc-conversation", "--port", "10230", "--prometheus_port","20230"] diff --git a/cmd/openim-rpc/openim-rpc-friend/Makefile b/cmd/openim-rpc/openim-rpc-friend/Makefile index 8cbb39c2b..e0068e97d 100644 --- a/cmd/openim-rpc/openim-rpc-friend/Makefile +++ b/cmd/openim-rpc/openim-rpc-friend/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=openim_friend +NAME=openim-rpc-friend BIN_DIR=../../../bin/ OS:= $(or $(os),linux) diff --git a/cmd/openim-rpc/openim-rpc-friend/deploy.Dockerfile b/cmd/openim-rpc/openim-rpc-friend/deploy.Dockerfile index 3aaf86885..f306a7938 100644 --- a/cmd/openim-rpc/openim-rpc-friend/deploy.Dockerfile +++ b/cmd/openim-rpc/openim-rpc-friend/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_friend ./ +COPY ./openim-rpc-friend ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./openim_friend", "--port", "10120", "--prometheus_port","20120"] +CMD ["./openim-rpc-friend", "--port", "10120", "--prometheus_port","20120"] diff --git a/cmd/openim-rpc/openim-rpc-group/Makefile b/cmd/openim-rpc/openim-rpc-group/Makefile index b33dbb259..8ba79043d 100644 --- a/cmd/openim-rpc/openim-rpc-group/Makefile +++ b/cmd/openim-rpc/openim-rpc-group/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=openim_group +NAME=openim-rpc-group BIN_DIR=../../../bin/ OS:= $(or $(os),linux) diff --git a/cmd/openim-rpc/openim-rpc-group/deploy.Dockerfile b/cmd/openim-rpc/openim-rpc-group/deploy.Dockerfile index 1adb00bc5..ed46edcf7 100644 --- a/cmd/openim-rpc/openim-rpc-group/deploy.Dockerfile +++ b/cmd/openim-rpc/openim-rpc-group/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_group ./ +COPY ./openim-rpc-group ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./openim_group", "--port", "10150", "--prometheus_port","20150"] +CMD ["./openim-rpc-group", "--port", "10150", "--prometheus_port","20150"] diff --git a/cmd/openim-rpc/openim-rpc-msg/Makefile b/cmd/openim-rpc/openim-rpc-msg/Makefile index b27c5c420..872fbfbba 100644 --- a/cmd/openim-rpc/openim-rpc-msg/Makefile +++ b/cmd/openim-rpc/openim-rpc-msg/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=openim_msg +NAME=openim-rpc-msg BIN_DIR=../../../bin/ OS:= $(or $(os),linux) diff --git a/cmd/openim-rpc/openim-rpc-msg/deploy.Dockerfile b/cmd/openim-rpc/openim-rpc-msg/deploy.Dockerfile index 10fd29f0a..61737417e 100644 --- a/cmd/openim-rpc/openim-rpc-msg/deploy.Dockerfile +++ b/cmd/openim-rpc/openim-rpc-msg/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_msg ./ +COPY ./openim-rpc-msg ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./openim_msg", "--port", "10130", "--prometheus_port","20130"] +CMD ["./openim-rpc-msg", "--port", "10130", "--prometheus_port","20130"] diff --git a/cmd/openim-rpc/openim-rpc-third/Makefile b/cmd/openim-rpc/openim-rpc-third/Makefile index b5a7c546a..28059b2c4 100644 --- a/cmd/openim-rpc/openim-rpc-third/Makefile +++ b/cmd/openim-rpc/openim-rpc-third/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=openim_third +NAME=openim-rpc-third BIN_DIR=../../../bin/ OS:= $(or $(os),linux) diff --git a/cmd/openim-rpc/openim-rpc-third/deploy.Dockerfile b/cmd/openim-rpc/openim-rpc-third/deploy.Dockerfile index 7b6a1dfaf..29bc2d068 100644 --- a/cmd/openim-rpc/openim-rpc-third/deploy.Dockerfile +++ b/cmd/openim-rpc/openim-rpc-third/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_third ./ +COPY ./openim-rpc-third ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./openim_third", "--port", "10200"] +CMD ["./openim-rpc-third", "--port", "10200"] diff --git a/cmd/openim-rpc/openim-rpc-user/Makefile b/cmd/openim-rpc/openim-rpc-user/Makefile index aef2d605a..1a7eed913 100644 --- a/cmd/openim-rpc/openim-rpc-user/Makefile +++ b/cmd/openim-rpc/openim-rpc-user/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=openim_user +NAME=openim-rpc-user BIN_DIR=../../../bin/ OS:= $(or $(os),linux) diff --git a/cmd/openim-rpc/openim-rpc-user/deploy.Dockerfile b/cmd/openim-rpc/openim-rpc-user/deploy.Dockerfile index 0977dd634..8ab7df1e7 100644 --- a/cmd/openim-rpc/openim-rpc-user/deploy.Dockerfile +++ b/cmd/openim-rpc/openim-rpc-user/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim_user ./ +COPY ./openim-rpc-user ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./openim_user", "--port", "10110"] +CMD ["./openim-rpc-user", "--port", "10110"] diff --git a/docs/.generated_docs b/docs/.generated_docs index d46d40d88..4145ed52c 100644 --- a/docs/.generated_docs +++ b/docs/.generated_docs @@ -26,12 +26,12 @@ docs/guide/en-US/cmd/openim/openim_secret_get.md docs/guide/en-US/cmd/openim/openim_secret_list.md docs/guide/en-US/cmd/openim/openim_secret_update.md docs/guide/en-US/cmd/openim/openim_set.md -docs/guide/en-US/cmd/openim/openim_user.md -docs/guide/en-US/cmd/openim/openim_user_create.md -docs/guide/en-US/cmd/openim/openim_user_delete.md -docs/guide/en-US/cmd/openim/openim_user_get.md -docs/guide/en-US/cmd/openim/openim_user_list.md -docs/guide/en-US/cmd/openim/openim_user_update.md +docs/guide/en-US/cmd/openim/openim-rpc-user.md +docs/guide/en-US/cmd/openim/openim-rpc-user_create.md +docs/guide/en-US/cmd/openim/openim-rpc-user_delete.md +docs/guide/en-US/cmd/openim/openim-rpc-user_get.md +docs/guide/en-US/cmd/openim/openim-rpc-user_list.md +docs/guide/en-US/cmd/openim/openim-rpc-user_update.md docs/guide/en-US/cmd/openim/openim_validate.md docs/guide/en-US/cmd/openim/openim_version.md docs/guide/en-US/yaml/openim/openim.yaml @@ -44,7 +44,7 @@ docs/guide/en-US/yaml/openim/openim_options.yaml docs/guide/en-US/yaml/openim/openim_policy.yaml docs/guide/en-US/yaml/openim/openim_secret.yaml docs/guide/en-US/yaml/openim/openim_set.yaml -docs/guide/en-US/yaml/openim/openim_user.yaml +docs/guide/en-US/yaml/openim/openim-rpc-user.yaml docs/guide/en-US/yaml/openim/openim_validate.yaml docs/guide/en-US/yaml/openim/openim_version.yaml docs/man/man1/iam-apiserver.1 diff --git a/scripts/batch_build_all_service.sh b/scripts/batch_build_all_service.sh index 6c74e1957..79cbb6c64 100755 --- a/scripts/batch_build_all_service.sh +++ b/scripts/batch_build_all_service.sh @@ -13,14 +13,20 @@ # See the License for the specific language governing permissions and # limitations under the License. +#Include shell font styles and some basic information +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. -source ./style_info.cfg -source ./path_info.cfg -source ./function.sh +# Include necessary files +source "$(dirname "${BASH_SOURCE[0]}")/../scripts/function" + +# Include specific functions and variables +source "$(dirname "${BASH_SOURCE[0]}")/../scripts/style_info.cfg" \ + "$OPENIM_ROOT/scripts/path_info.cfg" + +bin_dir="$OPENIM_ROOT/bin" +logs_dir="$OPENIM_ROOT/logs" +sdk_db_dir="$OPENIM_ROOT/sdk/db/" -bin_dir="../bin" -logs_dir="../logs" -sdk_db_dir="../db/sdk/" #Automatically created when there is no bin, logs folder if [ ! -d $bin_dir ]; then mkdir -p $bin_dir diff --git a/scripts/batch_start_all.sh b/scripts/batch_start_all.sh index 119e85f65..aa7713280 100755 --- a/scripts/batch_start_all.sh +++ b/scripts/batch_start_all.sh @@ -16,21 +16,28 @@ #fixme This scripts is the total startup scripts #fixme The full name of the shell scripts that needs to be started is placed in the need_to_start_server_shell array -#fixme Put the shell scripts name here +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. + +cd "$OPENIM_ROOT/scripts/" + need_to_start_server_shell=( - start_rpc_service.sh - msg_gateway_start.sh - push_start.sh - msg_transfer_start.sh + "start_rpc_service.sh" + "msg_gateway_start.sh" + "push_start.sh" + "msg_transfer_start.sh" ) -time=`date +"%Y-%m-%d %H:%M:%S"` -echo "==========================================================">>../logs/openIM.log 2>&1 & -echo "==========================================================">>../logs/openIM.log 2>&1 & -echo "==========================================================">>../logs/openIM.log 2>&1 & -echo "==========server start time:${time}===========">>../logs/openIM.log 2>&1 & -echo "==========================================================">>../logs/openIM.log 2>&1 & -echo "==========================================================">>../logs/openIM.log 2>&1 & -echo "==========================================================">>../logs/openIM.log 2>&1 & + +time=$(date +"%Y-%m-%d %H:%M:%S") + +for _ in {1..3}; do + echo "==========================================================" >> ../logs/openIM.log 2>&1 +done + +echo "==========server start time:${time}===========" >> ../logs/openIM.log 2>&1 + +for _ in {1..3}; do + echo "==========================================================" >> ../logs/openIM.log 2>&1 +done build_pid_array=() idx=0 diff --git a/scripts/build_images.sh b/scripts/build_images.sh deleted file mode 100755 index a94bc91d6..000000000 --- a/scripts/build_images.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash -# Copyright © 2023 OpenIM. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -image=openim/openim_server:v1.0.5 -rm Open-IM-Server -rf -git clone https://github.com/OpenIMSDK/Open-IM-Server.git --recursive -cd Open-IM-Server -git checkout tuoyun -cd cmd/Open-IM-SDK-Core/ -git checkout tuoyun -cd ../../ -docker build -t $image . -f deploy.Dockerfile -docker push $image \ No newline at end of file diff --git a/scripts/check_all.sh b/scripts/check_all.sh index 65f51c0cf..0bcb782dd 100755 --- a/scripts/check_all.sh +++ b/scripts/check_all.sh @@ -47,7 +47,7 @@ for i in ${service_port_name[*]}; do done #Check launched service process -check=$(ps aux | grep -w ./${msg_transfer_name} | grep -v grep | wc -l) +check=$(ps aux | grep -w ./${openim-msgtransfer} | grep -v grep | wc -l) if [ $check -eq ${msg_transfer_service_num} ]; then echo -e ${GREEN_PREFIX}"none port has been listening,belongs service is openImMsgTransfer"${COLOR_SUFFIX} else diff --git a/scripts/make-rules/golang.mk b/scripts/make-rules/golang.mk index 9602dbbbd..1b6c92ed3 100644 --- a/scripts/make-rules/golang.mk +++ b/scripts/make-rules/golang.mk @@ -73,10 +73,10 @@ EXCLUDE_TESTS=github.com/OpenIMSDK/Open-IM-Server/test github.com/OpenIMSDK/Open # ├── openim-sdk-core/ - main.go # ├── openim-api # ├── openim_cms_api -# ├── openim_cron_task +# ├── openim-crontask # ├── openim_demo -# ├── openim_msg_gateway -# ├── openim_msg_transfer +# ├── openim-rpc-msg_gateway +# ├── openim-rpc-msg_transfer # ├── openim-push # ├── rpc/openim_admin_cms/ - main.go # └── test/ - main.go @@ -84,7 +84,7 @@ EXCLUDE_TESTS=github.com/OpenIMSDK/Open-IM-Server/test github.com/OpenIMSDK/Open # PLATFORM=linux_amd64 # OS=linux # ARCH=amd64 -# BINS=openim-api openim_cms_api openim_cron_task openim_demo openim_msg_gateway openim_msg_transfer openim-push +# BINS=openim-api openim_cms_api openim-crontask openim_demo openim-rpc-msg_gateway openim-rpc-msg_transfer openim-push # BIN_DIR=/root/workspaces/OpenIM/_output/bin # ============================================================================== @@ -113,8 +113,8 @@ go.build.%: @mkdir -p $(BIN_DIR)/platforms/$(OS)/$(ARCH) @if [ "$(COMMAND)" == "openim-sdk-core" ]; then \ echo "===========> DEBUG: Compilation is not yet supported $(COMMAND)"; \ - elif [ "$(COMMAND)" == "openim_rpc" ]; then \ - for d in $(wildcard $(ROOT_DIR)/cmd/openim_rpc/*); do \ + elif [ "$(COMMAND)" == "openim-rpc" ]; then \ + for d in $(wildcard $(ROOT_DIR)/cmd/openim-rpc/*); do \ cd $${d} && CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) $(GO) build $(GO_BUILD_FLAGS) -o \ $(BIN_DIR)/platforms/$(OS)/$(ARCH)/$$(basename $${d})$(GO_OUT_EXT) $${d}/main.go; \ done; \ diff --git a/scripts/msg_gateway_start.sh b/scripts/msg_gateway_start.sh index c7cbc4823..9f2016be7 100755 --- a/scripts/msg_gateway_start.sh +++ b/scripts/msg_gateway_start.sh @@ -36,9 +36,9 @@ if [ ${#rpc_ports[@]} -ne ${#ws_ports[@]} ]; then fi #Check if the service exists #If it is exists,kill this process -check=$(ps aux | grep -w ./${msg_gateway_name} | grep -v grep | wc -l) +check=$(ps aux | grep -w ./${openim-msggateway} | grep -v grep | wc -l) if [ $check -ge 1 ]; then - oldPid=$(ps aux | grep -w ./${msg_gateway_name} | grep -v grep | awk '{print $2}') + oldPid=$(ps aux | grep -w ./${openim-msggateway} | grep -v grep | awk '{print $2}') kill -9 ${oldPid} fi #Waiting port recycling @@ -46,23 +46,23 @@ sleep 1 cd ${msg_gateway_binary_root} for ((i = 0; i < ${#ws_ports[@]}; i++)); do echo "==========================start msg_gateway server===========================">>../logs/openIM.log - nohup ./${msg_gateway_name} --port ${rpc_ports[$i]} --ws_port ${ws_ports[$i]} --prometheus_port ${prome_ports[$i]} >>../logs/openIM.log 2>&1 & + nohup ./${openim-msggateway} --port ${rpc_ports[$i]} --ws_port ${ws_ports[$i]} --prometheus_port ${prome_ports[$i]} >>../logs/openIM.log 2>&1 & done #Check launched service process sleep 3 -check=$(ps aux | grep -w ./${msg_gateway_name} | grep -v grep | wc -l) +check=$(ps aux | grep -w ./${openim-msggateway} | grep -v grep | wc -l) allPorts="" if [ $check -ge 1 ]; then - allNewPid=$(ps aux | grep -w ./${msg_gateway_name} | grep -v grep | awk '{print $2}') + allNewPid=$(ps aux | grep -w ./${openim-msggateway} | grep -v grep | awk '{print $2}') for i in $allNewPid; do ports=$(netstat -netulp | grep -w ${i} | awk '{print $4}' | awk -F '[:]' '{print $NF}') allPorts=${allPorts}"$ports " done echo -e ${SKY_BLUE_PREFIX}"SERVICE START SUCCESS"${COLOR_SUFFIX} - echo -e ${SKY_BLUE_PREFIX}"SERVICE_NAME: "${COLOR_SUFFIX}${YELLOW_PREFIX}${msg_gateway_name}${COLOR_SUFFIX} + echo -e ${SKY_BLUE_PREFIX}"SERVICE_NAME: "${COLOR_SUFFIX}${YELLOW_PREFIX}${openim-msggateway}${COLOR_SUFFIX} echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${YELLOW_PREFIX}${allNewPid}${COLOR_SUFFIX} echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${YELLOW_PREFIX}${allPorts}${COLOR_SUFFIX} else - echo -e ${YELLOW_PREFIX}${msg_gateway_name}${COLOR_SUFFIX}${RED_PREFIX}"SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} + echo -e ${YELLOW_PREFIX}${openim-msggateway}${COLOR_SUFFIX}${RED_PREFIX}"SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} fi diff --git a/scripts/msg_transfer_start.sh b/scripts/msg_transfer_start.sh index 460d2e1c8..01118af32 100755 --- a/scripts/msg_transfer_start.sh +++ b/scripts/msg_transfer_start.sh @@ -14,21 +14,29 @@ # limitations under the License. #Include shell font styles and some basic information -source ./style_info.cfg -source ./path_info.cfg -source ./function.sh +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. + +#Include shell font styles and some basic information +source $OPENIM_ROOT/scripts/style_info.cfg +source $OPENIM_ROOT/scripts/path_info.cfg +source $OPENIM_ROOT/scripts/function + +bin_dir="$OPENIM_ROOT/bin" +logs_dir="$OPENIM_ROOT/logs" +sdk_db_dir="$OPENIM_ROOT/sdk/db/" + +cd $OPENIM_ROOT list1=$(cat $config_path | grep messageTransferPrometheusPort | awk -F '[:]' '{print $NF}') list_to_string $list1 prome_ports=($ports_array) - #Check if the service exists #If it is exists,kill this process -check=`ps aux | grep -w ./${msg_transfer_name} | grep -v grep| wc -l` +check=`ps aux | grep -w ./${openim-msgtransfer} | grep -v grep| wc -l` if [ $check -ge 1 ] then -oldPid=`ps aux | grep -w ./${msg_transfer_name} | grep -v grep|awk '{print $2}'` +oldPid=`ps aux | grep -w ./${openim-msgtransfer} | grep -v grep|awk '{print $2}'` kill -9 $oldPid fi #Waiting port recycling @@ -37,7 +45,7 @@ sleep 1 cd ${msg_transfer_binary_root} for ((i = 0; i < ${msg_transfer_service_num}; i++)); do prome_port=${prome_ports[$i]} - cmd="nohup ./${msg_transfer_name}" + cmd="nohup ./${openim-msgtransfer}" if [ $prome_port != "" ]; then cmd="$cmd --prometheus_port $prome_port" fi @@ -46,15 +54,15 @@ for ((i = 0; i < ${msg_transfer_service_num}; i++)); do done #Check launched service process -check=`ps aux | grep -w ./${msg_transfer_name} | grep -v grep| wc -l` +check=`ps aux | grep -w ./${openim-msgtransfer} | grep -v grep| wc -l` if [ $check -ge 1 ] then -newPid=`ps aux | grep -w ./${msg_transfer_name} | grep -v grep|awk '{print $2}'` +newPid=`ps aux | grep -w ./${openim-msgtransfer} | grep -v grep|awk '{print $2}'` allPorts="" echo -e ${SKY_BLUE_PREFIX}"SERVICE START SUCCESS "${COLOR_SUFFIX} - echo -e ${SKY_BLUE_PREFIX}"SERVICE_NAME: "${COLOR_SUFFIX}${YELLOW_PREFIX}${msg_transfer_name}${COLOR_SUFFIX} + echo -e ${SKY_BLUE_PREFIX}"SERVICE_NAME: "${COLOR_SUFFIX}${YELLOW_PREFIX}${openim-msgtransfer}${COLOR_SUFFIX} echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${YELLOW_PREFIX}${newPid}${COLOR_SUFFIX} echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${YELLOW_PREFIX}${allPorts}${COLOR_SUFFIX} else - echo -e ${YELLOW_PREFIX}${msg_transfer_name}${COLOR_SUFFIX}${RED_PREFIX}"SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} + echo -e ${YELLOW_PREFIX}${openim-msgtransfer}${COLOR_SUFFIX}${RED_PREFIX}"SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} fi diff --git a/scripts/path_info.cfg b/scripts/path_info.cfg old mode 100644 new mode 100755 index aa36164ae..72759b2a4 --- a/scripts/path_info.cfg +++ b/scripts/path_info.cfg @@ -2,6 +2,8 @@ architecture=$(uname -m) version=$(uname -s | tr '[:upper:]' '[:lower:]') +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. + # Define the supported architectures and corresponding bin directories declare -A supported_architectures=( ["linux-amd64"]="_output/bin/platforms/linux/amd64" @@ -18,40 +20,43 @@ declare -A supported_architectures=( # Check if the architecture and version are supported if [[ -z ${supported_architectures["$version-$architecture"]} ]]; then - echo "Unsupported architecture: $architecture or version: $version" + echo "================> Unsupported architecture: $architecture or version: $version" exit 1 fi +echo "================> Architecture: $architecture" + # Set the BIN_DIR based on the architecture and version BIN_DIR=${supported_architectures["$version-$architecture"]} -echo "BIN_DIR: $BIN_DIR" +echo "================> BIN_DIR: $OPENIM_ROOT/$BIN_DIR" + # Don't put the space between "=" -msg_gateway_name="openim-msggateway" -msg_gateway_binary_root= $BIN_DIR +openim_msggateway="openim-msggateway" +msg_gateway_binary_root= $OPENIM_ROOT/$BIN_DIR/ msg_gateway_source_root="../cmd/msggateway/" -msg_name="openim_msg" -msg_binary_root=$BIN_DIR +msg_name="openim-rpc-msg" +msg_binary_root=$OPENIM_ROOT/$BIN_DIR msg_source_root="../cmd/rpc/msg/" push_name="openim-push" -push_binary_root=$BIN_DIR +push_binary_root=$OPENIM_ROOT/$BIN_DIR push_source_root="../cmd/push/" -msg_transfer_name="openim_msg_transfer" -msg_transfer_binary_root=$BIN_DIR +openim_msgtransfer="openim-rpc-msg_transfer" +msg_transfer_binary_root=$OPENIM_ROOT/$BIN_DIR msg_transfer_source_root="../cmd/msgtransfer/" msg_transfer_service_num=4 -cron_task_name="openim_cron_task" -cron_task_binary_root=$BIN_DIR +cron_task_name="openim-crontask" +cron_task_binary_root=$OPENIM_ROOT/$BIN_DIR cron_task_source_root="../cmd/crontask/" cmd_utils_name="openim_cmd_utils" -cmd_utils_binary_root=$BIN_DIR +cmd_utils_binary_root=$OPENIM_ROOT/$BIN_DIR cmd_utils_source_root="../cmd/cmduitls/" # Global configuration file default dir @@ -81,15 +86,15 @@ service_names=( # api service filename "openim-api" # rpc service filename - "openim_user" - "openim_friend" - "openim_group" - "openim_auth" - "openim_conversation" - "openim_third" - "openim_cron_task" - "${msg_gateway_name}" - "${msg_transfer_name}" + "openim-rpc-user" + "openim-rpc-friend" + "openim-rpc-group" + "openim-rpc-auth" + "openim-rpc-conversation" + "openim-rpc-third" + "openim-crontask" + "${openim_msggateway}" + "${openim_msgtransfer}" "${msg_name}" "${push_name}" # "${sdk_server_name}" diff --git a/scripts/start_all.sh b/scripts/start_all.sh index c16ae3a11..b847ef89b 100755 --- a/scripts/start_all.sh +++ b/scripts/start_all.sh @@ -20,6 +20,8 @@ source ./style_info.cfg source ./path_info.cfg +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. + # Print title echo -e "${BOLD_PREFIX}${BLUE_PREFIX}OpenIM Server Start${COLOR_SUFFIX}" @@ -35,6 +37,7 @@ echo -e "${BOLD_PREFIX}${CYAN_PREFIX}Server Start Time: ${time}${COLOR_SUFFIX}" # Print section separator echo -e "${PURPLE_PREFIX}==========================================================${COLOR_SUFFIX}" +cd $OPENIM_ROOT/scripts # FIXME Put the shell script names here need_to_start_server_shell=( start_rpc_service.sh @@ -65,7 +68,7 @@ for i in ${need_to_start_server_shell[*]}; do done # Print section separator -echo "${PURPLE_PREFIX}==========================================================${COLOR_SUFFIX}" +echo -e "${PURPLE_PREFIX}==========================================================${COLOR_SUFFIX}" # Print completion message -echo "${GREEN_PREFIX}${BOLD_PREFIX}OpenIM Server has been started successfully!${COLOR_SUFFIX}" \ No newline at end of file +echo -e "${GREEN_PREFIX}${BOLD_PREFIX}OpenIM Server has been started successfully!${COLOR_SUFFIX}" \ No newline at end of file diff --git a/scripts/start_cron.sh b/scripts/start_cron.sh index 5aa29c1c7..efe06d8f7 100755 --- a/scripts/start_cron.sh +++ b/scripts/start_cron.sh @@ -13,11 +13,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -#Include shell font styles and some basic information -source ./style_info.cfg -source ./path_info.cfg - +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +#Include shell font styles and some basic information +source $OPENIM_ROOT/scripts/style_info.cfg +source $OPENIM_ROOT/scripts/path_info.cfg #Check if the service exists #If it is exists,kill this process diff --git a/scripts/start_rpc_service.sh b/scripts/start_rpc_service.sh index 379dba474..940bf57c6 100755 --- a/scripts/start_rpc_service.sh +++ b/scripts/start_rpc_service.sh @@ -13,23 +13,31 @@ # See the License for the specific language governing permissions and # limitations under the License. +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. -source ./style_info.cfg -source ./path_info.cfg -source ./function.sh +#Include shell font styles and some basic information +source $OPENIM_ROOT/scripts/style_info.cfg +source $OPENIM_ROOT/scripts/path_info.cfg +source $OPENIM_ROOT/scripts/function + +bin_dir="$OPENIM_ROOT/bin" +logs_dir="$OPENIM_ROOT/logs" +sdk_db_dir="$OPENIM_ROOT/sdk/db/" + +cd $OPENIM_ROOT #service filename service_filename=( #api openim-api #rpc - openim_user - openim_friend - openim_group - openim_auth + openim-rpc-user + openim-rpc-friend + openim-rpc-group + openim-rpc-auth ${msg_name} - openim_conversation - openim_third + openim-rpc-conversation + openim-rpc-third ) #service config port name diff --git a/scripts/style_info.cfg b/scripts/style_info.cfg old mode 100644 new mode 100755 From 396f455c778e32c1d58c0bb192959b05d160da33 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Fri, 7 Jul 2023 21:59:28 +0800 Subject: [PATCH 35/73] feat: fix scripts Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- scripts/build_all_service.sh | 2 +- scripts/enterprise/path_info.cfg | 2 +- scripts/githooks/pre-push | 15 +++++++++++++++ scripts/msg_gateway_start.sh | 14 +++++++++++--- scripts/path_info.cfg | 2 +- scripts/push_start.sh | 15 ++++++++++++--- scripts/start_all.sh | 14 +++++++++++--- scripts/start_cron.sh | 8 ++++++++ scripts/start_rpc_service.sh | 4 ++-- scripts/stop_all.sh | 14 +++++++++++--- 10 files changed, 73 insertions(+), 17 deletions(-) diff --git a/scripts/build_all_service.sh b/scripts/build_all_service.sh index 7359b39b3..7a583c970 100755 --- a/scripts/build_all_service.sh +++ b/scripts/build_all_service.sh @@ -25,7 +25,7 @@ echo -e "\n" echo -e "${BOLD_PREFIX}_____ _ _ _____ _____ _____ _____ _____ _____ _____ _____ _____ ${COLOR_SUFFIX}" -bin_dir="../bin" +bin_dir="$BIN_DIR" logs_dir="../logs" sdk_db_dir="../db/sdk/" # Automatically created when there is no bin, logs folder diff --git a/scripts/enterprise/path_info.cfg b/scripts/enterprise/path_info.cfg index 29d3112c1..168d46049 100644 --- a/scripts/enterprise/path_info.cfg +++ b/scripts/enterprise/path_info.cfg @@ -2,7 +2,7 @@ demo_server_name="openim_chat_api" -demo_server_binary_root="../bin/" +demo_server_binary_root="$BIN_DIR/" diff --git a/scripts/githooks/pre-push b/scripts/githooks/pre-push index c8ad61a60..39c42832c 100644 --- a/scripts/githooks/pre-push +++ b/scripts/githooks/pre-push @@ -40,6 +40,21 @@ if [[ `git status --porcelain` ]]; then exit 1 fi +PURPLE_PREFIX="\033[35m" # Purple prefix +BOLD_PREFIX="\033[1m" # Bold prefix + +# Get current time +time=$(date +"%Y-%m-%d %H:%M:%S") + +# Print section separator +echo -e "${PURPLE_PREFIX}==========================================================${COLOR_SUFFIX}" + +# Print time of submission +echo -e "${BOLD_PREFIX}${CYAN_PREFIX}Time of submission: ${time}${COLOR_SUFFIX}" + +# Print section separator +echo -e "${PURPLE_PREFIX}==========================================================${COLOR_SUFFIX}" + # #printMessage "Running the Flutter analyzer" #flutter analyze diff --git a/scripts/msg_gateway_start.sh b/scripts/msg_gateway_start.sh index 9f2016be7..9f4fb5e5f 100755 --- a/scripts/msg_gateway_start.sh +++ b/scripts/msg_gateway_start.sh @@ -14,9 +14,17 @@ # limitations under the License. #Include shell font styles and some basic information -source ./style_info.cfg -source ./path_info.cfg -source ./function.sh +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. + +#Include shell font styles and some basic information +source $OPENIM_ROOT/scripts/style_info.cfg +source $OPENIM_ROOT/scripts/path_info.cfg +source $OPENIM_ROOT/scripts/function + +bin_dir="$OPENIM_ROOT/bin" +logs_dir="$OPENIM_ROOT/logs" +sdk_db_dir="$OPENIM_ROOT/sdk/db/" + ulimit -n 200000 list1=$(cat $config_path | grep openImMessageGatewayPort | awk -F '[:]' '{print $NF}') diff --git a/scripts/path_info.cfg b/scripts/path_info.cfg index 72759b2a4..a86add786 100755 --- a/scripts/path_info.cfg +++ b/scripts/path_info.cfg @@ -23,7 +23,7 @@ if [[ -z ${supported_architectures["$version-$architecture"]} ]]; then echo "================> Unsupported architecture: $architecture or version: $version" exit 1 fi - +Server Start Time echo "================> Architecture: $architecture" # Set the BIN_DIR based on the architecture and version diff --git a/scripts/push_start.sh b/scripts/push_start.sh index 80da0f575..f13056259 100755 --- a/scripts/push_start.sh +++ b/scripts/push_start.sh @@ -14,9 +14,18 @@ # limitations under the License. #Include shell font styles and some basic information -source ./style_info.cfg -source ./path_info.cfg -source ./function.sh +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. + +#Include shell font styles and some basic information +source $OPENIM_ROOT/scripts/style_info.cfg +source $OPENIM_ROOT/scripts/path_info.cfg +source $OPENIM_ROOT/scripts/function + +bin_dir="$OPENIM_ROOT/bin" +logs_dir="$OPENIM_ROOT/logs" +sdk_db_dir="$OPENIM_ROOT/sdk/db/" + +cd "$OPENIM_ROOT/scripts/" list1=$(cat $config_path | grep openImPushPort | awk -F '[:]' '{print $NF}') list2=$(cat $config_path | grep pushPrometheusPort | awk -F '[:]' '{print $NF}') diff --git a/scripts/start_all.sh b/scripts/start_all.sh index b847ef89b..92134ca44 100755 --- a/scripts/start_all.sh +++ b/scripts/start_all.sh @@ -17,11 +17,19 @@ #FIXME The full names of the shell scripts that need to be started are placed in the `need_to_start_server_shell` array. #Include shell font styles and some basic information -source ./style_info.cfg -source ./path_info.cfg - OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +#Include shell font styles and some basic information +source $OPENIM_ROOT/scripts/style_info.cfg +source $OPENIM_ROOT/scripts/path_info.cfg +source $OPENIM_ROOT/scripts/function + +bin_dir="$OPENIM_ROOT/bin" +logs_dir="$OPENIM_ROOT/logs" +sdk_db_dir="$OPENIM_ROOT/sdk/db/" + +cd "$OPENIM_ROOT/scripts/" + # Print title echo -e "${BOLD_PREFIX}${BLUE_PREFIX}OpenIM Server Start${COLOR_SUFFIX}" diff --git a/scripts/start_cron.sh b/scripts/start_cron.sh index efe06d8f7..26f9abf8b 100755 --- a/scripts/start_cron.sh +++ b/scripts/start_cron.sh @@ -13,11 +13,19 @@ # See the License for the specific language governing permissions and # limitations under the License. +#Include shell font styles and some basic information OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. #Include shell font styles and some basic information source $OPENIM_ROOT/scripts/style_info.cfg source $OPENIM_ROOT/scripts/path_info.cfg +source $OPENIM_ROOT/scripts/function + +bin_dir="$OPENIM_ROOT/bin" +logs_dir="$OPENIM_ROOT/logs" +sdk_db_dir="$OPENIM_ROOT/sdk/db/" + +cd "$OPENIM_ROOT/scripts/" #Check if the service exists #If it is exists,kill this process diff --git a/scripts/start_rpc_service.sh b/scripts/start_rpc_service.sh index 940bf57c6..e67711d49 100755 --- a/scripts/start_rpc_service.sh +++ b/scripts/start_rpc_service.sh @@ -20,7 +20,7 @@ source $OPENIM_ROOT/scripts/style_info.cfg source $OPENIM_ROOT/scripts/path_info.cfg source $OPENIM_ROOT/scripts/function -bin_dir="$OPENIM_ROOT/bin" +bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" sdk_db_dir="$OPENIM_ROOT/sdk/db/" @@ -80,7 +80,7 @@ for ((i = 0; i < ${#service_filename[*]}; i++)); do kill -9 $(eval $pid) sleep 0.5 fi - cd ../bin + cd $ #Get the rpc port in the configuration file portList=$(cat $config_path | grep ${service_port_name[$i]} | awk -F '[:]' '{print $NF}') list_to_string ${portList} diff --git a/scripts/stop_all.sh b/scripts/stop_all.sh index f04779d43..fac3d7f41 100755 --- a/scripts/stop_all.sh +++ b/scripts/stop_all.sh @@ -13,11 +13,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -#fixme This scripts is to stop the service +#Include shell font styles and some basic information +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. -source ./style_info.cfg -source ./path_info.cfg +#Include shell font styles and some basic information +source $OPENIM_ROOT/scripts/style_info.cfg +source $OPENIM_ROOT/scripts/path_info.cfg +source $OPENIM_ROOT/scripts/function +bin_dir="$OPENIM_ROOT/bin" +logs_dir="$OPENIM_ROOT/logs" +sdk_db_dir="$OPENIM_ROOT/sdk/db/" + +cd "$OPENIM_ROOT/scripts/" for i in ${service_names[*]}; do #Check whether the service exists From 44d7f79637aab649f15d3778521975dce594c45a Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Fri, 7 Jul 2023 22:00:59 +0800 Subject: [PATCH 36/73] feat: add COLOR_SUFFIX=033[0m # End all colors and special effects Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- scripts/githooks/pre-push | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/githooks/pre-push b/scripts/githooks/pre-push index 39c42832c..d28e63869 100644 --- a/scripts/githooks/pre-push +++ b/scripts/githooks/pre-push @@ -40,6 +40,7 @@ if [[ `git status --porcelain` ]]; then exit 1 fi +COLOR_SUFFIX="\033[0m" # End all colors and special effects PURPLE_PREFIX="\033[35m" # Purple prefix BOLD_PREFIX="\033[1m" # Bold prefix From 48f1580e04e3672af8dbaa398ad24b818d858651 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Fri, 7 Jul 2023 22:05:55 +0800 Subject: [PATCH 37/73] feat: add cyan Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- scripts/githooks/pre-push | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/scripts/githooks/pre-push b/scripts/githooks/pre-push index d28e63869..2553367b0 100644 --- a/scripts/githooks/pre-push +++ b/scripts/githooks/pre-push @@ -40,21 +40,48 @@ if [[ `git status --porcelain` ]]; then exit 1 fi -COLOR_SUFFIX="\033[0m" # End all colors and special effects -PURPLE_PREFIX="\033[35m" # Purple prefix -BOLD_PREFIX="\033[1m" # Bold prefix +COLOR_SUFFIX="\033[0m" + +BLACK_PREFIX="\033[30m" +RED_PREFIX="\033[31m" +GREEN_PREFIX="\033[32m" +YELLOW_PREFIX="\033[33m" +BLUE_PREFIX="\033[34m" +PURPLE_PREFIX="\033[35m" +SKY_BLUE_PREFIX="\033[36m" +WHITE_PREFIX="\033[37m" +BOLD_PREFIX="\033[1m" +UNDERLINE_PREFIX="\033[4m" +ITALIC_PREFIX="\033[3m" + +# Function to print colored text +print_color() { + local text=$1 + local color=$2 + echo -e "${color}${text}${COLOR_SUFFIX}" +} + +# Function to print section separator +print_separator() { + print_color "==========================================================" ${PURPLE_PREFIX} +} # Get current time time=$(date +"%Y-%m-%d %H:%M:%S") # Print section separator -echo -e "${PURPLE_PREFIX}==========================================================${COLOR_SUFFIX}" +print_separator # Print time of submission -echo -e "${BOLD_PREFIX}${CYAN_PREFIX}Time of submission: ${time}${COLOR_SUFFIX}" +print_color "Time of submission: ${time}" "${BOLD_PREFIX}${CYAN_PREFIX}" + +# Print additional information if needed +print_color "Repository: ${repository}" "${YELLOW_PREFIX}" +print_color "Author: ${author}" "${YELLOW_PREFIX}" # Print section separator -echo -e "${PURPLE_PREFIX}==========================================================${COLOR_SUFFIX}" +print_separator + # #printMessage "Running the Flutter analyzer" From abc538c64fa4d075cbeca48f95fa851e0de1512e Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Fri, 7 Jul 2023 22:07:52 +0800 Subject: [PATCH 38/73] feat: add cyan Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- scripts/githooks/pre-push | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/githooks/pre-push b/scripts/githooks/pre-push index 2553367b0..ad17b3c48 100644 --- a/scripts/githooks/pre-push +++ b/scripts/githooks/pre-push @@ -75,9 +75,12 @@ print_separator # Print time of submission print_color "Time of submission: ${time}" "${BOLD_PREFIX}${CYAN_PREFIX}" +author=$(git config user.name) +repository=$(basename -s .git $(git config --get remote.origin.url)) + # Print additional information if needed -print_color "Repository: ${repository}" "${YELLOW_PREFIX}" -print_color "Author: ${author}" "${YELLOW_PREFIX}" +print_color "Repository: ${repository}" "${BLUE_PREFIX}" +print_color "Author: ${author}" "${PURPLE_PREFIX}" # Print section separator print_separator From 4e648886512d4b5d13ada1bf0c2b99630b962d76 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Fri, 7 Jul 2023 22:10:39 +0800 Subject: [PATCH 39/73] feat: add cyan Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- scripts/githooks/pre-push | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/githooks/pre-push b/scripts/githooks/pre-push index ad17b3c48..8752023c6 100644 --- a/scripts/githooks/pre-push +++ b/scripts/githooks/pre-push @@ -73,13 +73,14 @@ time=$(date +"%Y-%m-%d %H:%M:%S") print_separator # Print time of submission -print_color "Time of submission: ${time}" "${BOLD_PREFIX}${CYAN_PREFIX}" - -author=$(git config user.name) -repository=$(basename -s .git $(git config --get remote.origin.url)) +print_color "PTIME: ${time}" "${BOLD_PREFIX}${CYAN_PREFIX}" +echo "" +Author=$(git config user.name) +Repository=$(basename -s .git $(git config --get remote.origin.url)) # Print additional information if needed print_color "Repository: ${repository}" "${BLUE_PREFIX}" +echo "" print_color "Author: ${author}" "${PURPLE_PREFIX}" # Print section separator From f4cef32b9552131112ddb5d76075eccc8f38f74f Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Fri, 7 Jul 2023 22:12:30 +0800 Subject: [PATCH 40/73] feat: add cyan Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- scripts/githooks/pre-push | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/scripts/githooks/pre-push b/scripts/githooks/pre-push index 8752023c6..97d426487 100644 --- a/scripts/githooks/pre-push +++ b/scripts/githooks/pre-push @@ -75,17 +75,34 @@ print_separator # Print time of submission print_color "PTIME: ${time}" "${BOLD_PREFIX}${CYAN_PREFIX}" echo "" -Author=$(git config user.name) -Repository=$(basename -s .git $(git config --get remote.origin.url)) +author=$(git config user.name) +repository=$(basename -s .git $(git config --get remote.origin.url)) # Print additional information if needed print_color "Repository: ${repository}" "${BLUE_PREFIX}" echo "" + print_color "Author: ${author}" "${PURPLE_PREFIX}" # Print section separator print_separator +# 获取变更的文件列表 +file_list=$(git diff --name-status HEAD @{u}) +added_files=$(grep -c '^A' <<< "$file_list") +modified_files=$(grep -c '^M' <<< "$file_list") +deleted_files=$(grep -c '^D' <<< "$file_list") + +# 打印变更的文件统计 +print_color "Added Files: ${added_files}" "${YELLOW_PREFIX}" +print_color "Modified Files: ${modified_files}" "${YELLOW_PREFIX}" +print_color "Deleted Files: ${deleted_files}" "${YELLOW_PREFIX}" + +# 获取最近一次提交的摘要信息 +commit_message=$(git log -1 --pretty=format:"%s") + +# 打印提交的摘要信息 +print_color "Commit Message: ${commit_message}" "${YELLOW_PREFIX}" # #printMessage "Running the Flutter analyzer" From 94bc02caa24bb6a4a88382f61453a9a687ca4ccc Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Fri, 7 Jul 2023 22:13:55 +0800 Subject: [PATCH 41/73] feat: add cyan Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- scripts/githooks/pre-push | 8 -------- 1 file changed, 8 deletions(-) diff --git a/scripts/githooks/pre-push b/scripts/githooks/pre-push index 97d426487..9087e0cb9 100644 --- a/scripts/githooks/pre-push +++ b/scripts/githooks/pre-push @@ -87,23 +87,15 @@ print_color "Author: ${author}" "${PURPLE_PREFIX}" # Print section separator print_separator -# 获取变更的文件列表 file_list=$(git diff --name-status HEAD @{u}) added_files=$(grep -c '^A' <<< "$file_list") modified_files=$(grep -c '^M' <<< "$file_list") deleted_files=$(grep -c '^D' <<< "$file_list") -# 打印变更的文件统计 print_color "Added Files: ${added_files}" "${YELLOW_PREFIX}" print_color "Modified Files: ${modified_files}" "${YELLOW_PREFIX}" print_color "Deleted Files: ${deleted_files}" "${YELLOW_PREFIX}" -# 获取最近一次提交的摘要信息 -commit_message=$(git log -1 --pretty=format:"%s") - -# 打印提交的摘要信息 -print_color "Commit Message: ${commit_message}" "${YELLOW_PREFIX}" - # #printMessage "Running the Flutter analyzer" #flutter analyze From b6e108774a0f550cb9fdae198fe0a957b20f3b51 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Sun, 9 Jul 2023 14:16:19 +0800 Subject: [PATCH 42/73] fix: directory name Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- scripts/batch_build_all_service.sh | 2 +- scripts/build_all_service.sh | 25 ++++++++++++------------ scripts/enterprise/check_all.sh | 12 +++++++++--- scripts/enterprise/path_info.cfg | 4 ++-- scripts/msg_gateway_start.sh | 2 +- scripts/msg_transfer_start.sh | 2 +- scripts/path_info.cfg | 31 +++++++++++++++--------------- scripts/push_start.sh | 2 +- scripts/start_all.sh | 2 +- scripts/start_cron.sh | 2 +- scripts/start_rpc_service.sh | 2 +- scripts/stop_all.sh | 2 +- 12 files changed, 46 insertions(+), 42 deletions(-) diff --git a/scripts/batch_build_all_service.sh b/scripts/batch_build_all_service.sh index 79cbb6c64..83aba4451 100755 --- a/scripts/batch_build_all_service.sh +++ b/scripts/batch_build_all_service.sh @@ -17,7 +17,7 @@ OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. # Include necessary files -source "$(dirname "${BASH_SOURCE[0]}")/../scripts/function" +source "$(dirname "${BASH_SOURCE[0]}")/../scripts.sh" # Include specific functions and variables source "$(dirname "${BASH_SOURCE[0]}")/../scripts/style_info.cfg" \ diff --git a/scripts/build_all_service.sh b/scripts/build_all_service.sh index 7a583c970..aed63a818 100755 --- a/scripts/build_all_service.sh +++ b/scripts/build_all_service.sh @@ -17,12 +17,12 @@ source ./style_info.cfg source ./path_info.cfg source ./function.sh -echo -e "\n" +echo -e "" echo -e "${BACKGROUND_BLUE}===============> Building all using make build binary files ${COLOR_SUFFIX}" -echo -e "\n" -echo -e "${BOLD_PREFIX}_____ _ _ _____ _____ _____ _____ _____ _____ _____ _____ _____ ${COLOR_SUFFIX}" +echo -e "" +echo -e "${BOLD_PREFIX}____________________________________________________________ ${COLOR_SUFFIX}" bin_dir="$BIN_DIR" @@ -39,14 +39,13 @@ if [ ! -d $sdk_db_dir ]; then mkdir -p $sdk_db_dir fi -# OpenIM root path +#Include shell font styles and some basic information OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. -source ./style_info.cfg -source ./path_info.cfg -source ./function.sh - -#!/bin/bash +#Include shell font styles and some basic information +source $OPENIM_ROOT/scripts/style_info.cfg +source $OPENIM_ROOT/scripts/path_info.cfg +source $OPENIM_ROOT/scripts/function.sh cd $OPENIM_ROOT @@ -102,11 +101,11 @@ for binary in $(find _output/bin/platforms/$REPO_DIR -type f); do fi done -echo -e " \n" +echo -e " " echo -e "${BOLD_PREFIX}=====================> Build Results <=====================${COLOR_SUFFIX}" -echo -e " \n" +echo -e " " if [[ "$BUILD_SUCCESS" == true ]]; then echo -e "${GREEN_PREFIX}All binaries built successfully.${COLOR_SUFFIX}" @@ -117,8 +116,8 @@ else done fi -echo -e " \n" +echo -e " " echo -e "${BOLD_PREFIX}============================================================${COLOR_SUFFIX}" -echo -e " \n" +echo -e " " diff --git a/scripts/enterprise/check_all.sh b/scripts/enterprise/check_all.sh index 893e1bf32..8c9d3a82a 100755 --- a/scripts/enterprise/check_all.sh +++ b/scripts/enterprise/check_all.sh @@ -13,10 +13,16 @@ # See the License for the specific language governing permissions and # limitations under the License. +#Include shell font styles and some basic information +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. + +#Include shell font styles and some basic information +source $OPENIM_ROOT/scripts/style_info.cfg +source $OPENIM_ROOT/scripts/path_info.cfg +source $OPENIM_ROOT/scripts/function.sh + +cd "$OPENIM_ROOT/scripts/" -source ./style_info.cfg -source ./enterprise/path_info.cfg -source ./enterprise/function.sh service_port_name=( openImChatApiPort openImAdminApiPort diff --git a/scripts/enterprise/path_info.cfg b/scripts/enterprise/path_info.cfg index 168d46049..6ce712593 100644 --- a/scripts/enterprise/path_info.cfg +++ b/scripts/enterprise/path_info.cfg @@ -15,8 +15,8 @@ service_source_root=( ../cmd/api/chat/ ../cmd/api/admin/ #rpc service file - ../cmd/rpc/admin/ - ../cmd/rpc/chat/ + ../cmd/openim-rpc/admin/ + ../cmd/openim-rpc/chat/ ) #service filename service_names=( diff --git a/scripts/msg_gateway_start.sh b/scripts/msg_gateway_start.sh index 9f4fb5e5f..5ca720aa8 100755 --- a/scripts/msg_gateway_start.sh +++ b/scripts/msg_gateway_start.sh @@ -19,7 +19,7 @@ OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. #Include shell font styles and some basic information source $OPENIM_ROOT/scripts/style_info.cfg source $OPENIM_ROOT/scripts/path_info.cfg -source $OPENIM_ROOT/scripts/function +source $OPENIM_ROOT/scripts/function.sh bin_dir="$OPENIM_ROOT/bin" logs_dir="$OPENIM_ROOT/logs" diff --git a/scripts/msg_transfer_start.sh b/scripts/msg_transfer_start.sh index 01118af32..2cd1ff8d7 100755 --- a/scripts/msg_transfer_start.sh +++ b/scripts/msg_transfer_start.sh @@ -19,7 +19,7 @@ OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. #Include shell font styles and some basic information source $OPENIM_ROOT/scripts/style_info.cfg source $OPENIM_ROOT/scripts/path_info.cfg -source $OPENIM_ROOT/scripts/function +source $OPENIM_ROOT/scripts/function.sh bin_dir="$OPENIM_ROOT/bin" logs_dir="$OPENIM_ROOT/logs" diff --git a/scripts/path_info.cfg b/scripts/path_info.cfg index a86add786..6c031c8b3 100755 --- a/scripts/path_info.cfg +++ b/scripts/path_info.cfg @@ -23,7 +23,7 @@ if [[ -z ${supported_architectures["$version-$architecture"]} ]]; then echo "================> Unsupported architecture: $architecture or version: $version" exit 1 fi -Server Start Time + echo "================> Architecture: $architecture" # Set the BIN_DIR based on the architecture and version @@ -31,32 +31,31 @@ BIN_DIR=${supported_architectures["$version-$architecture"]} echo "================> BIN_DIR: $OPENIM_ROOT/$BIN_DIR" - # Don't put the space between "=" openim_msggateway="openim-msggateway" -msg_gateway_binary_root= $OPENIM_ROOT/$BIN_DIR/ +msg_gateway_binary_root="$OPENIM_ROOT/$BIN_DIR" msg_gateway_source_root="../cmd/msggateway/" msg_name="openim-rpc-msg" -msg_binary_root=$OPENIM_ROOT/$BIN_DIR -msg_source_root="../cmd/rpc/msg/" +msg_binary_root="$OPENIM_ROOT/$BIN_DIR" +msg_source_root="../cmd/openim-rpc/msg/" push_name="openim-push" -push_binary_root=$OPENIM_ROOT/$BIN_DIR +push_binary_root="$OPENIM_ROOT/$BIN_DIR" push_source_root="../cmd/push/" openim_msgtransfer="openim-rpc-msg_transfer" -msg_transfer_binary_root=$OPENIM_ROOT/$BIN_DIR +msg_transfer_binary_root="$OPENIM_ROOT/$BIN_DIR" msg_transfer_source_root="../cmd/msgtransfer/" msg_transfer_service_num=4 cron_task_name="openim-crontask" -cron_task_binary_root=$OPENIM_ROOT/$BIN_DIR +cron_task_binary_root="$OPENIM_ROOT/$BIN_DIR" cron_task_source_root="../cmd/crontask/" cmd_utils_name="openim_cmd_utils" -cmd_utils_binary_root=$OPENIM_ROOT/$BIN_DIR +cmd_utils_binary_root="$OPENIM_ROOT/$BIN_DIR" cmd_utils_source_root="../cmd/cmduitls/" # Global configuration file default dir @@ -67,13 +66,13 @@ service_source_root=( # api service file "../cmd/api/" # rpc service file - "../cmd/rpc/user/" - "../cmd/rpc/friend/" - "../cmd/rpc/group/" - "../cmd/rpc/auth/" - "../cmd/rpc/conversation/" - "../cmd/rpc/third/" - "../cmd/crontask" + "../cmd/openim-rpc/openim-rpc-user/" + "../cmd/openim-rpc/openim-rpc-friend/" + "../cmd/openim-rpc/openim-rpc-group/" + "../cmd/openim-rpc/openim-rpc-auth/" + "../cmd/openim-rpc/openim-rpc-conversation/" + "../cmd/openim-rpc/openim-rpc-third/" + "../cmd/openim-crontask" "${msg_gateway_source_root}" "${msg_transfer_source_root}" "${msg_source_root}" diff --git a/scripts/push_start.sh b/scripts/push_start.sh index f13056259..c2a8e7d36 100755 --- a/scripts/push_start.sh +++ b/scripts/push_start.sh @@ -19,7 +19,7 @@ OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. #Include shell font styles and some basic information source $OPENIM_ROOT/scripts/style_info.cfg source $OPENIM_ROOT/scripts/path_info.cfg -source $OPENIM_ROOT/scripts/function +source $OPENIM_ROOT/scripts/function.sh bin_dir="$OPENIM_ROOT/bin" logs_dir="$OPENIM_ROOT/logs" diff --git a/scripts/start_all.sh b/scripts/start_all.sh index 92134ca44..7a9fd656c 100755 --- a/scripts/start_all.sh +++ b/scripts/start_all.sh @@ -22,7 +22,7 @@ OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. #Include shell font styles and some basic information source $OPENIM_ROOT/scripts/style_info.cfg source $OPENIM_ROOT/scripts/path_info.cfg -source $OPENIM_ROOT/scripts/function +source $OPENIM_ROOT/scripts/function.sh bin_dir="$OPENIM_ROOT/bin" logs_dir="$OPENIM_ROOT/logs" diff --git a/scripts/start_cron.sh b/scripts/start_cron.sh index 26f9abf8b..b18d19478 100755 --- a/scripts/start_cron.sh +++ b/scripts/start_cron.sh @@ -19,7 +19,7 @@ OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. #Include shell font styles and some basic information source $OPENIM_ROOT/scripts/style_info.cfg source $OPENIM_ROOT/scripts/path_info.cfg -source $OPENIM_ROOT/scripts/function +source $OPENIM_ROOT/scripts/function.sh bin_dir="$OPENIM_ROOT/bin" logs_dir="$OPENIM_ROOT/logs" diff --git a/scripts/start_rpc_service.sh b/scripts/start_rpc_service.sh index e67711d49..028bdc8c8 100755 --- a/scripts/start_rpc_service.sh +++ b/scripts/start_rpc_service.sh @@ -18,7 +18,7 @@ OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. #Include shell font styles and some basic information source $OPENIM_ROOT/scripts/style_info.cfg source $OPENIM_ROOT/scripts/path_info.cfg -source $OPENIM_ROOT/scripts/function +source $OPENIM_ROOT/scripts/function.sh bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" diff --git a/scripts/stop_all.sh b/scripts/stop_all.sh index fac3d7f41..133bb9577 100755 --- a/scripts/stop_all.sh +++ b/scripts/stop_all.sh @@ -19,7 +19,7 @@ OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. #Include shell font styles and some basic information source $OPENIM_ROOT/scripts/style_info.cfg source $OPENIM_ROOT/scripts/path_info.cfg -source $OPENIM_ROOT/scripts/function +source $OPENIM_ROOT/scripts/function.sh bin_dir="$OPENIM_ROOT/bin" logs_dir="$OPENIM_ROOT/logs" From 7426ee0abe4c605e6256612fe9bb808e70a48ee9 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Sun, 9 Jul 2023 15:28:19 +0800 Subject: [PATCH 43/73] feat: sets the absolute path of the script Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- init_docker.sh | 33 ++++++++++++++++++++++++------ scripts/batch_build_all_service.sh | 2 +- scripts/init_pwd.sh | 8 ++------ scripts/msg_gateway_start.sh | 2 +- scripts/msg_transfer_start.sh | 2 +- scripts/path_info.cfg | 23 ++++++++++++++++----- scripts/push_start.sh | 2 +- scripts/start_all.sh | 23 ++++++++++++--------- scripts/start_cron.sh | 2 +- scripts/start_rpc_service.sh | 16 +++++++++++---- scripts/stop_all.sh | 2 +- 11 files changed, 78 insertions(+), 37 deletions(-) diff --git a/init_docker.sh b/init_docker.sh index edc1df58d..09213bf50 100644 --- a/init_docker.sh +++ b/init_docker.sh @@ -1,9 +1,30 @@ #!/usr/bin/env bash -cd scripts ; -chmod +x *.sh ; -./env_check.sh; -cd .. ; -docker-compose up -d; -cd scripts ; +set -e + +# Change directory to the 'scripts' folder +cd scripts + +# Grant execute permissions to all shell scripts in the 'scripts' folder +chmod +x *.sh + +# Run the 'env_check.sh' script for environment checks +./env_check.sh + +# Move back to the parent directory +cd .. + +# Check if Docker is installed +if ! command -v docker >/dev/null 2>&1; then + echo "Error: Docker is not installed. Please install Docker before running this script." + exit 1 +fi + +# Start Docker services using docker-compose +docker-compose up -d + +# Move back to the 'scripts' folder +cd scripts + +# Run the 'docker_check_service.sh' script for Docker service checks ./docker_check_service.sh diff --git a/scripts/batch_build_all_service.sh b/scripts/batch_build_all_service.sh index 83aba4451..7bb75de77 100755 --- a/scripts/batch_build_all_service.sh +++ b/scripts/batch_build_all_service.sh @@ -23,7 +23,7 @@ source "$(dirname "${BASH_SOURCE[0]}")/../scripts.sh" source "$(dirname "${BASH_SOURCE[0]}")/../scripts/style_info.cfg" \ "$OPENIM_ROOT/scripts/path_info.cfg" -bin_dir="$OPENIM_ROOT/bin" +bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" sdk_db_dir="$OPENIM_ROOT/sdk/db/" diff --git a/scripts/init_pwd.sh b/scripts/init_pwd.sh index fa8296724..25ee559a3 100755 --- a/scripts/init_pwd.sh +++ b/scripts/init_pwd.sh @@ -1,3 +1,5 @@ +#!/bin/bash + # Copyright © 2023 OpenIM. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,7 +16,6 @@ source ../.env - # Check if PASSWORD only contains letters and numbers if [[ "$PASSWORD" =~ ^[a-zA-Z0-9]+$ ]] then @@ -24,15 +25,11 @@ else exit fi - echo "your user is:$USER" echo "your password is:$PASSWORD" echo "your minio endPoint is:$MINIO_ENDPOINT" echo "your data dir is $DATA_DIR" - -#!/bin/bash - # Specify the config file config_file='../config/config.yaml' @@ -57,4 +54,3 @@ sed -i '/minio:/,/endpoint:/s|endpoint: .*|endpoint: '${MINIO_ENDPOINT}'|' $conf # Replace secret for token sed -i "s/secret: .*/secret: $PASSWORD/" $config_file - diff --git a/scripts/msg_gateway_start.sh b/scripts/msg_gateway_start.sh index 5ca720aa8..62a8479c9 100755 --- a/scripts/msg_gateway_start.sh +++ b/scripts/msg_gateway_start.sh @@ -21,7 +21,7 @@ source $OPENIM_ROOT/scripts/style_info.cfg source $OPENIM_ROOT/scripts/path_info.cfg source $OPENIM_ROOT/scripts/function.sh -bin_dir="$OPENIM_ROOT/bin" +bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" sdk_db_dir="$OPENIM_ROOT/sdk/db/" diff --git a/scripts/msg_transfer_start.sh b/scripts/msg_transfer_start.sh index 2cd1ff8d7..50c01cb9d 100755 --- a/scripts/msg_transfer_start.sh +++ b/scripts/msg_transfer_start.sh @@ -21,7 +21,7 @@ source $OPENIM_ROOT/scripts/style_info.cfg source $OPENIM_ROOT/scripts/path_info.cfg source $OPENIM_ROOT/scripts/function.sh -bin_dir="$OPENIM_ROOT/bin" +bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" sdk_db_dir="$OPENIM_ROOT/sdk/db/" diff --git a/scripts/path_info.cfg b/scripts/path_info.cfg index 6c031c8b3..57f328a47 100755 --- a/scripts/path_info.cfg +++ b/scripts/path_info.cfg @@ -2,7 +2,20 @@ architecture=$(uname -m) version=$(uname -s | tr '[:upper:]' '[:lower:]') -OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +#Include shell font styles and some basic information +SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +OPENIM_ROOT=$(dirname "${SCRIPTS_ROOT}")/.. + +#Include shell font styles and some basic information +source $SCRIPTS_ROOT/style_info.cfg +source $SCRIPTS_ROOT/path_info.cfg +source $SCRIPTS_ROOT/function.sh + +cd $SCRIPTS_ROOT + +echo -e "${BACKGROUND_YELLOW}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_YELLOW}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_YELLOW}=======>pwd=$PWD${COLOR_SUFFIX}" # Define the supported architectures and corresponding bin directories declare -A supported_architectures=( @@ -46,20 +59,20 @@ push_source_root="../cmd/push/" openim_msgtransfer="openim-rpc-msg_transfer" msg_transfer_binary_root="$OPENIM_ROOT/$BIN_DIR" -msg_transfer_source_root="../cmd/msgtransfer/" +msg_transfer_source_root="$OPENIM_ROOT/cmd/msgtransfer/" msg_transfer_service_num=4 cron_task_name="openim-crontask" cron_task_binary_root="$OPENIM_ROOT/$BIN_DIR" -cron_task_source_root="../cmd/crontask/" +cron_task_source_root="$OPENIM_ROOT/cmd/crontask/" cmd_utils_name="openim_cmd_utils" cmd_utils_binary_root="$OPENIM_ROOT/$BIN_DIR" -cmd_utils_source_root="../cmd/cmduitls/" +cmd_utils_source_root="$OPENIM_ROOT/cmd/cmduitls/" # Global configuration file default dir -config_path="../config/config.yaml" +config_path="$OPENIM_ROOT/config/config.yaml" # servicefile dir path service_source_root=( diff --git a/scripts/push_start.sh b/scripts/push_start.sh index c2a8e7d36..204f710e7 100755 --- a/scripts/push_start.sh +++ b/scripts/push_start.sh @@ -21,7 +21,7 @@ source $OPENIM_ROOT/scripts/style_info.cfg source $OPENIM_ROOT/scripts/path_info.cfg source $OPENIM_ROOT/scripts/function.sh -bin_dir="$OPENIM_ROOT/bin" +bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" sdk_db_dir="$OPENIM_ROOT/sdk/db/" diff --git a/scripts/start_all.sh b/scripts/start_all.sh index 7a9fd656c..44c613479 100755 --- a/scripts/start_all.sh +++ b/scripts/start_all.sh @@ -17,21 +17,26 @@ #FIXME The full names of the shell scripts that need to be started are placed in the `need_to_start_server_shell` array. #Include shell font styles and some basic information -OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +OPENIM_ROOT=$(dirname "${SCRIPTS_ROOT}")/.. #Include shell font styles and some basic information -source $OPENIM_ROOT/scripts/style_info.cfg -source $OPENIM_ROOT/scripts/path_info.cfg -source $OPENIM_ROOT/scripts/function.sh +source $SCRIPTS_ROOT/style_info.cfg +source $SCRIPTS_ROOT/path_info.cfg +source $SCRIPTS_ROOT/function.sh -bin_dir="$OPENIM_ROOT/bin" +cd $SCRIPTS_ROOT + +echo -e "${BACKGROUND_YELLOW}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_YELLOW}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_YELLOW}=======>pwd=$PWD${COLOR_SUFFIX}" + +bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" sdk_db_dir="$OPENIM_ROOT/sdk/db/" -cd "$OPENIM_ROOT/scripts/" - # Print title -echo -e "${BOLD_PREFIX}${BLUE_PREFIX}OpenIM Server Start${COLOR_SUFFIX}" +echo -e "${BOLD_PREFIX}${BLUE_PREFIX}================> OpenIM Server Start${COLOR_SUFFIX}" # Get current time time=$(date +"%Y-%m-%d %H:%M:%S") @@ -45,7 +50,6 @@ echo -e "${BOLD_PREFIX}${CYAN_PREFIX}Server Start Time: ${time}${COLOR_SUFFIX}" # Print section separator echo -e "${PURPLE_PREFIX}==========================================================${COLOR_SUFFIX}" -cd $OPENIM_ROOT/scripts # FIXME Put the shell script names here need_to_start_server_shell=( start_rpc_service.sh @@ -55,7 +59,6 @@ need_to_start_server_shell=( start_cron.sh ) - # Loop through the script names and execute them for i in ${need_to_start_server_shell[*]}; do chmod +x $i diff --git a/scripts/start_cron.sh b/scripts/start_cron.sh index b18d19478..c7ce4e451 100755 --- a/scripts/start_cron.sh +++ b/scripts/start_cron.sh @@ -21,7 +21,7 @@ source $OPENIM_ROOT/scripts/style_info.cfg source $OPENIM_ROOT/scripts/path_info.cfg source $OPENIM_ROOT/scripts/function.sh -bin_dir="$OPENIM_ROOT/bin" +bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" sdk_db_dir="$OPENIM_ROOT/sdk/db/" diff --git a/scripts/start_rpc_service.sh b/scripts/start_rpc_service.sh index 028bdc8c8..2d0e70f63 100755 --- a/scripts/start_rpc_service.sh +++ b/scripts/start_rpc_service.sh @@ -13,12 +13,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +#Include shell font styles and some basic information +SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +OPENIM_ROOT=$(dirname "${SCRIPTS_ROOT}")/.. #Include shell font styles and some basic information -source $OPENIM_ROOT/scripts/style_info.cfg -source $OPENIM_ROOT/scripts/path_info.cfg -source $OPENIM_ROOT/scripts/function.sh +source $SCRIPTS_ROOT/style_info.cfg +source $SCRIPTS_ROOT/path_info.cfg +source $SCRIPTS_ROOT/function.sh + +cd $SCRIPTS_ROOT + +echo -e "${BACKGROUND_YELLOW}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_YELLOW}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_YELLOW}=======>pwd=$PWD${COLOR_SUFFIX}" bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" diff --git a/scripts/stop_all.sh b/scripts/stop_all.sh index 133bb9577..7bec5b53f 100755 --- a/scripts/stop_all.sh +++ b/scripts/stop_all.sh @@ -21,7 +21,7 @@ source $OPENIM_ROOT/scripts/style_info.cfg source $OPENIM_ROOT/scripts/path_info.cfg source $OPENIM_ROOT/scripts/function.sh -bin_dir="$OPENIM_ROOT/bin" +bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" sdk_db_dir="$OPENIM_ROOT/sdk/db/" From d41ad35b212be822d1e93fddc4a1d2cf470206e6 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Sun, 9 Jul 2023 16:51:43 +0800 Subject: [PATCH 44/73] feat: add all command Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- README-zh_CN.md | 2 +- README.md | 2 +- cmd/openim-cmdutils/Makefile | 2 +- config/config.yaml | 3 +- scripts/batch_build_all_service.sh | 6 ++-- scripts/batch_start_all.sh | 21 +++++++++--- scripts/build_all_service.sh | 8 ++--- scripts/build_push_k8s_images.sh | 2 +- scripts/check_all.sh | 26 +++++++++----- scripts/docker_start_all.sh | 14 ++++---- scripts/enterprise/check_all.sh | 17 +++++---- scripts/env_check.sh | 2 +- scripts/function.sh | 30 +++++++++------- scripts/githooks/pre-push | 8 ++--- scripts/msg_gateway_start.sh | 25 +++++++++----- scripts/msg_transfer_start.sh | 25 +++++++++----- scripts/{path_info.cfg => path_info.sh} | 42 ++++++++++------------- scripts/push_start.sh | 25 +++++++++----- scripts/start_all.sh | 12 +++---- scripts/start_cron.sh | 27 +++++++++------ scripts/start_rpc_service.sh | 24 ++++++------- scripts/stop_all.sh | 4 +-- scripts/{style_info.cfg => style_info.sh} | 2 ++ 23 files changed, 189 insertions(+), 140 deletions(-) rename scripts/{path_info.cfg => path_info.sh} (73%) rename scripts/{style_info.cfg => style_info.sh} (98%) diff --git a/README-zh_CN.md b/README-zh_CN.md index 87d675c88..1cdd0a52e 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -142,7 +142,7 @@ Open-IM-Server 是一款即时通讯服务器,使用纯 Golang 开发,采用 > Open-IM 脚本提供服务编译、启动和停止脚本。有四个 Open-IM 脚本启动模块,一个是 http+rpc 服务启动模块,第二个是 WebSocket 服务启动模块,然后是 msg_transfer 模块,最后是 push 模块。 -+ path_info.cfg&&style_info.cfg&& ++ path_info.sh&&style_info.sh&& functions.sh diff --git a/README.md b/README.md index d3d880d41..e8da2ab5e 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ all services build success > Open-IM scripts provides service compilation, start, and stop scripts. There are four Open-IM scripts start modules, one is the http+rpc service start module, the second is the websocket service start module, then the msg_transfer module, and the last is the push module -- path_info.cfg&&style_info.cfg&&functions.sh +- path_info.sh&&style_info.sh&&functions.sh - Contains the path information of each module, including the path where the source code is located, the name of the service startup, the shell print font style, and some functions for processing shell strings - build_all_service.sh - Compile the module, compile all the source code of Open-IM into a binary file and put it into the bin directory diff --git a/cmd/openim-cmdutils/Makefile b/cmd/openim-cmdutils/Makefile index 9c51aee84..e78ac33f6 100644 --- a/cmd/openim-cmdutils/Makefile +++ b/cmd/openim-cmdutils/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=openim_cmd_utils +NAME=openim-cmdutils BIN_DIR=../../bin/ OS:= $(or $(os),linux) diff --git a/config/config.yaml b/config/config.yaml index def91d2b5..ea6f8772d 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -274,5 +274,4 @@ prometheus: #prometheus每个服务的端口数量需要和rpcPort保持对应 conversationPrometheusPort: [ 20230 ] rtcPrometheusPort: [ 21300 ] thirdPrometheusPort: [ 21301 ] - messageTransferPrometheusPort: [ 21400, 21401, 21402, 21403 ] #端口数量需要和script/path_info.cfg中的msg_transfer_service_num保持一致 - + messageTransferPrometheusPort: [ 21400, 21401, 21402, 21403 ] #端口数量需要和script/path_info.sh中的msg_transfer_service_num保持一致 diff --git a/scripts/batch_build_all_service.sh b/scripts/batch_build_all_service.sh index 7bb75de77..f3bd791cf 100755 --- a/scripts/batch_build_all_service.sh +++ b/scripts/batch_build_all_service.sh @@ -20,8 +20,8 @@ OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. source "$(dirname "${BASH_SOURCE[0]}")/../scripts.sh" # Include specific functions and variables -source "$(dirname "${BASH_SOURCE[0]}")/../scripts/style_info.cfg" \ - "$OPENIM_ROOT/scripts/path_info.cfg" +source "$(dirname "${BASH_SOURCE[0]}")/../scripts/style_info.sh" \ + "$OPENIM_ROOT/scripts/path_info.sh" bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" @@ -76,5 +76,5 @@ done echo "success_num" $success_num "service num:" ${#service_source_root[*]} if [ $success_num == ${#service_source_root[*]} ] then - echo -e ${YELLOW_PREFIX}"all services build success"${COLOR_SUFFIX} + echo -e ${BACKGROUND_GREEN}"all services build success"${COLOR_SUFFIX} fi diff --git a/scripts/batch_start_all.sh b/scripts/batch_start_all.sh index aa7713280..e291819f4 100755 --- a/scripts/batch_start_all.sh +++ b/scripts/batch_start_all.sh @@ -16,9 +16,20 @@ #fixme This scripts is the total startup scripts #fixme The full name of the shell scripts that needs to be started is placed in the need_to_start_server_shell array +#Include shell font styles and some basic information +SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. -cd "$OPENIM_ROOT/scripts/" +#Include shell font styles and some basic information +source $SCRIPTS_ROOT/style_info.sh +source $SCRIPTS_ROOT/path_info.sh +source $SCRIPTS_ROOT/function.sh + +cd $SCRIPTS_ROOT + +echo -e "${BACKGROUND_GREEN}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_GREEN}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_GREEN}=======>pwd=$PWD${COLOR_SUFFIX}" need_to_start_server_shell=( "start_rpc_service.sh" @@ -30,13 +41,13 @@ need_to_start_server_shell=( time=$(date +"%Y-%m-%d %H:%M:%S") for _ in {1..3}; do - echo "==========================================================" >> ../logs/openIM.log 2>&1 + echo "==========================================================" >> $OPENIM_ROOT/logs/openIM.log 2>&1 done -echo "==========server start time:${time}===========" >> ../logs/openIM.log 2>&1 +echo "==========server start time:${time}===========" >> $OPENIM_ROOT/logs/openIM.log 2>&1 for _ in {1..3}; do - echo "==========================================================" >> ../logs/openIM.log 2>&1 + echo "==========================================================" >> $OPENIM_ROOT/logs/openIM.log 2>&1 done build_pid_array=() @@ -74,5 +85,5 @@ done echo "success_num" $success_num "service num:" ${#need_to_start_server_shell[*]} if [ $success_num == ${#need_to_start_server_shell[*]} ] then - echo -e ${YELLOW_PREFIX}"all services build success"${COLOR_SUFFIX} + echo -e ${BACKGROUND_GREEN}"all services build success"${COLOR_SUFFIX} fi diff --git a/scripts/build_all_service.sh b/scripts/build_all_service.sh index aed63a818..fd196e48b 100755 --- a/scripts/build_all_service.sh +++ b/scripts/build_all_service.sh @@ -14,8 +14,8 @@ # limitations under the License. -source ./style_info.cfg -source ./path_info.cfg +source ./style_info.sh +source ./path_info.sh source ./function.sh echo -e "" @@ -43,8 +43,8 @@ fi OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. #Include shell font styles and some basic information -source $OPENIM_ROOT/scripts/style_info.cfg -source $OPENIM_ROOT/scripts/path_info.cfg +source $OPENIM_ROOT/scripts/style_info.sh +source $OPENIM_ROOT/scripts/path_info.sh source $OPENIM_ROOT/scripts/function.sh cd $OPENIM_ROOT diff --git a/scripts/build_push_k8s_images.sh b/scripts/build_push_k8s_images.sh index ff9887c56..a620c7847 100755 --- a/scripts/build_push_k8s_images.sh +++ b/scripts/build_push_k8s_images.sh @@ -23,7 +23,7 @@ fi set +e echo "repository: ${repository}" -source ./path_info.cfg +source ./path_info.sh echo "start to build docker images" currentPwd=`pwd` echo ${currentPwd} diff --git a/scripts/check_all.sh b/scripts/check_all.sh index 0bcb782dd..db420dd21 100755 --- a/scripts/check_all.sh +++ b/scripts/check_all.sh @@ -13,10 +13,20 @@ # See the License for the specific language governing permissions and # limitations under the License. +#Include shell font styles and some basic information +SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. -source ./style_info.cfg -source ./path_info.cfg -source ./function.sh +#Include shell font styles and some basic information +source $SCRIPTS_ROOT/style_info.sh +source $SCRIPTS_ROOT/path_info.sh +source $SCRIPTS_ROOT/function.sh + +cd $SCRIPTS_ROOT + +echo -e "${BACKGROUND_GREEN}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_GREEN}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_GREEN}=======>pwd=$PWD${COLOR_SUFFIX}" service_port_name=( openImWsPort @@ -37,8 +47,8 @@ for i in ${service_port_name[*]}; do for j in ${ports_array}; do port=$(ss -tunlp| grep openim | awk '{print $5}' | grep -w ${j} | awk -F '[:]' '{print $NF}') if [[ ${port} -ne ${j} ]]; then - echo -e ${YELLOW_PREFIX}${i}${COLOR_SUFFIX}${RED_PREFIX}" service does not start normally,not initiated port is "${COLOR_SUFFIX}${YELLOW_PREFIX}${j}${COLOR_SUFFIX} - echo -e ${RED_PREFIX}"please check ../logs/openIM.log "${COLOR_SUFFIX} + echo -e ${BACKGROUND_GREEN}${i}${COLOR_SUFFIX}${RED_PREFIX}" service does not start normally,not initiated port is "${COLOR_SUFFIX}${BACKGROUND_GREEN}${j}${COLOR_SUFFIX} + echo -e ${RED_PREFIX}"please check $OPENIM_ROOT/logs/openIM.log "${COLOR_SUFFIX} exit -1 else echo -e ${j}${GREEN_PREFIX}" port has been listening,belongs service is "${i}${COLOR_SUFFIX} @@ -52,7 +62,7 @@ if [ $check -eq ${msg_transfer_service_num} ]; then echo -e ${GREEN_PREFIX}"none port has been listening,belongs service is openImMsgTransfer"${COLOR_SUFFIX} else echo -e ${RED_PREFIX}"openImMsgTransfer service does not start normally, num err"${COLOR_SUFFIX} - echo -e ${RED_PREFIX}"please check ../logs/openIM.log "${COLOR_SUFFIX} + echo -e ${RED_PREFIX}"please check $OPENIM_ROOT/logs/openIM.log "${COLOR_SUFFIX} exit -1 fi @@ -62,8 +72,8 @@ if [ $check -ge 1 ]; then echo -e ${GREEN_PREFIX}"none port has been listening,belongs service is openImCronTask"${COLOR_SUFFIX} else echo -e ${RED_PREFIX}"cron_task_name service does not start normally"${COLOR_SUFFIX} - echo -e ${RED_PREFIX}"please check ../logs/openIM.log "${COLOR_SUFFIX} + echo -e ${RED_PREFIX}"please check $OPENIM_ROOT/logs/openIM.log "${COLOR_SUFFIX} exit -1 fi -echo -e ${YELLOW_PREFIX}"all services launch success"${COLOR_SUFFIX} +echo -e ${BACKGROUND_GREEN}"all services launch success"${COLOR_SUFFIX} diff --git a/scripts/docker_start_all.sh b/scripts/docker_start_all.sh index 8ed198cae..98e5a131c 100755 --- a/scripts/docker_start_all.sh +++ b/scripts/docker_start_all.sh @@ -29,13 +29,13 @@ need_to_start_server_shell=( sleep 10 time=`date +"%Y-%m-%d %H:%M:%S"` -echo "==========================================================">>../logs/openIM.log 2>&1 & -echo "==========================================================">>../logs/openIM.log 2>&1 & -echo "==========================================================">>../logs/openIM.log 2>&1 & -echo "==========server start time:${time}===========">>../logs/openIM.log 2>&1 & -echo "==========================================================">>../logs/openIM.log 2>&1 & -echo "==========================================================">>../logs/openIM.log 2>&1 & -echo "==========================================================">>../logs/openIM.log 2>&1 & +echo "==========================================================">>$OPENIM_ROOT/logs/openIM.log 2>&1 & +echo "==========================================================">>$OPENIM_ROOT/logs/openIM.log 2>&1 & +echo "==========================================================">>$OPENIM_ROOT/logs/openIM.log 2>&1 & +echo "==========server start time:${time}===========">>$OPENIM_ROOT/logs/openIM.log 2>&1 & +echo "==========================================================">>$OPENIM_ROOT/logs/openIM.log 2>&1 & +echo "==========================================================">>$OPENIM_ROOT/logs/openIM.log 2>&1 & +echo "==========================================================">>$OPENIM_ROOT/logs/openIM.log 2>&1 & for i in ${need_to_start_server_shell[*]}; do chmod +x $i ./$i diff --git a/scripts/enterprise/check_all.sh b/scripts/enterprise/check_all.sh index 8c9d3a82a..7b8a3c5da 100755 --- a/scripts/enterprise/check_all.sh +++ b/scripts/enterprise/check_all.sh @@ -14,14 +14,19 @@ # limitations under the License. #Include shell font styles and some basic information +SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. #Include shell font styles and some basic information -source $OPENIM_ROOT/scripts/style_info.cfg -source $OPENIM_ROOT/scripts/path_info.cfg -source $OPENIM_ROOT/scripts/function.sh +source $SCRIPTS_ROOT/style_info.sh +source $SCRIPTS_ROOT/path_info.sh +source $SCRIPTS_ROOT/function.sh -cd "$OPENIM_ROOT/scripts/" +cd $SCRIPTS_ROOT + +echo -e "${BACKGROUND_GREEN}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_GREEN}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_GREEN}=======>pwd=$PWD${COLOR_SUFFIX}" service_port_name=( openImChatApiPort @@ -37,8 +42,8 @@ for i in ${service_port_name[*]}; do for j in ${ports_array}; do port=$(ss -tunlp| grep openim | awk '{print $5}' | grep -w ${j} | awk -F '[:]' '{print $NF}') if [[ ${port} -ne ${j} ]]; then - echo -e ${YELLOW_PREFIX}${i}${COLOR_SUFFIX}${RED_PREFIX}" service does not start normally,not initiated port is "${COLOR_SUFFIX}${YELLOW_PREFIX}${j}${COLOR_SUFFIX} - echo -e ${RED_PREFIX}"please check ../logs/openIM.log "${COLOR_SUFFIX} + echo -e ${BACKGROUND_GREEN}${i}${COLOR_SUFFIX}${RED_PREFIX}" service does not start normally,not initiated port is "${COLOR_SUFFIX}${BACKGROUND_GREEN}${j}${COLOR_SUFFIX} + echo -e ${RED_PREFIX}"please check $OPENIM_ROOT/logs/openIM.log "${COLOR_SUFFIX} exit -1 else echo -e ${j}${GREEN_PREFIX}" port has been listening,belongs service is "${i}${COLOR_SUFFIX} diff --git a/scripts/env_check.sh b/scripts/env_check.sh index c6cfaf4bb..8c84567b7 100755 --- a/scripts/env_check.sh +++ b/scripts/env_check.sh @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -source ./style_info.cfg +source ./style_info.sh echo -e "check time synchronize.................................." t=`curl http://time.akamai.com/?iso -s` diff --git a/scripts/function.sh b/scripts/function.sh index e2dc96a25..e351c1ddb 100755 --- a/scripts/function.sh +++ b/scripts/function.sh @@ -13,17 +13,21 @@ # See the License for the specific language governing permissions and # limitations under the License. -#input:[10023,2323,3434] -#output:10023 2323 3434 -list_to_string(){ -ports_list=$* -sub_s1=`echo $ports_list | sed 's/ //g'` -sub_s2=${sub_s1//,/ } -sub_s3=${sub_s2#*[} -sub_s4=${sub_s3%]*} -ports_array=$sub_s4 +# input: [10023, 2323, 3434] +# output: 10023 2323 3434 + +# 函数功能:将列表转换为字符串,去除空格和括号 +list_to_string() { + ports_list=$* # 获取传入的参数列表 + sub_s1=$(echo $ports_list | sed 's/ //g') # 去除空格 + sub_s2=${sub_s1//,/ } # 将逗号替换为空格 + sub_s3=${sub_s2#*[} # 去除左括号及其之前的内容 + sub_s4=${sub_s3%]*} # 去除右括号及其之后的内容 + ports_array=$sub_s4 # 将处理后的字符串赋值给变量 ports_array +} + +# 函数功能:去除字符串中的空格 +remove_space() { + value=$* # 获取传入的参数 + result=$(echo $value | sed 's/ //g') # 去除空格 } -remove_space(){ - value=$* - result=`echo $value | sed 's/ //g'` -} \ No newline at end of file diff --git a/scripts/githooks/pre-push b/scripts/githooks/pre-push index 9087e0cb9..2ed9736be 100644 --- a/scripts/githooks/pre-push +++ b/scripts/githooks/pre-push @@ -45,7 +45,7 @@ COLOR_SUFFIX="\033[0m" BLACK_PREFIX="\033[30m" RED_PREFIX="\033[31m" GREEN_PREFIX="\033[32m" -YELLOW_PREFIX="\033[33m" +BACKGROUND_GREEN="\033[33m" BLUE_PREFIX="\033[34m" PURPLE_PREFIX="\033[35m" SKY_BLUE_PREFIX="\033[36m" @@ -92,9 +92,9 @@ added_files=$(grep -c '^A' <<< "$file_list") modified_files=$(grep -c '^M' <<< "$file_list") deleted_files=$(grep -c '^D' <<< "$file_list") -print_color "Added Files: ${added_files}" "${YELLOW_PREFIX}" -print_color "Modified Files: ${modified_files}" "${YELLOW_PREFIX}" -print_color "Deleted Files: ${deleted_files}" "${YELLOW_PREFIX}" +print_color "Added Files: ${added_files}" "${BACKGROUND_GREEN}" +print_color "Modified Files: ${modified_files}" "${BACKGROUND_GREEN}" +print_color "Deleted Files: ${deleted_files}" "${BACKGROUND_GREEN}" # #printMessage "Running the Flutter analyzer" diff --git a/scripts/msg_gateway_start.sh b/scripts/msg_gateway_start.sh index 62a8479c9..7df840b21 100755 --- a/scripts/msg_gateway_start.sh +++ b/scripts/msg_gateway_start.sh @@ -14,12 +14,19 @@ # limitations under the License. #Include shell font styles and some basic information +SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. #Include shell font styles and some basic information -source $OPENIM_ROOT/scripts/style_info.cfg -source $OPENIM_ROOT/scripts/path_info.cfg -source $OPENIM_ROOT/scripts/function.sh +source $SCRIPTS_ROOT/style_info.sh +source $SCRIPTS_ROOT/path_info.sh +source $SCRIPTS_ROOT/function.sh + +cd $SCRIPTS_ROOT + +echo -e "${BACKGROUND_GREEN}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_GREEN}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_GREEN}=======>pwd=$PWD${COLOR_SUFFIX}" bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" @@ -53,8 +60,8 @@ fi sleep 1 cd ${msg_gateway_binary_root} for ((i = 0; i < ${#ws_ports[@]}; i++)); do - echo "==========================start msg_gateway server===========================">>../logs/openIM.log - nohup ./${openim-msggateway} --port ${rpc_ports[$i]} --ws_port ${ws_ports[$i]} --prometheus_port ${prome_ports[$i]} >>../logs/openIM.log 2>&1 & + echo "==========================start msg_gateway server===========================">>$OPENIM_ROOT/logs/openIM.log + nohup ./${openim-msggateway} --port ${rpc_ports[$i]} --ws_port ${ws_ports[$i]} --prometheus_port ${prome_ports[$i]} >>$OPENIM_ROOT/logs/openIM.log 2>&1 & done #Check launched service process @@ -68,9 +75,9 @@ if [ $check -ge 1 ]; then allPorts=${allPorts}"$ports " done echo -e ${SKY_BLUE_PREFIX}"SERVICE START SUCCESS"${COLOR_SUFFIX} - echo -e ${SKY_BLUE_PREFIX}"SERVICE_NAME: "${COLOR_SUFFIX}${YELLOW_PREFIX}${openim-msggateway}${COLOR_SUFFIX} - echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${YELLOW_PREFIX}${allNewPid}${COLOR_SUFFIX} - echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${YELLOW_PREFIX}${allPorts}${COLOR_SUFFIX} + echo -e ${SKY_BLUE_PREFIX}"SERVICE_NAME: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${openim-msggateway}${COLOR_SUFFIX} + echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${allNewPid}${COLOR_SUFFIX} + echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${allPorts}${COLOR_SUFFIX} else - echo -e ${YELLOW_PREFIX}${openim-msggateway}${COLOR_SUFFIX}${RED_PREFIX}"SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} + echo -e ${BACKGROUND_GREEN}${openim-msggateway}${COLOR_SUFFIX}${RED_PREFIX}"SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} fi diff --git a/scripts/msg_transfer_start.sh b/scripts/msg_transfer_start.sh index 50c01cb9d..b038d125a 100755 --- a/scripts/msg_transfer_start.sh +++ b/scripts/msg_transfer_start.sh @@ -14,12 +14,19 @@ # limitations under the License. #Include shell font styles and some basic information +SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. #Include shell font styles and some basic information -source $OPENIM_ROOT/scripts/style_info.cfg -source $OPENIM_ROOT/scripts/path_info.cfg -source $OPENIM_ROOT/scripts/function.sh +source $SCRIPTS_ROOT/style_info.sh +source $SCRIPTS_ROOT/path_info.sh +source $SCRIPTS_ROOT/function.sh + +cd $SCRIPTS_ROOT + +echo -e "${BACKGROUND_GREEN}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_GREEN}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_GREEN}=======>pwd=$PWD${COLOR_SUFFIX}" bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" @@ -49,8 +56,8 @@ for ((i = 0; i < ${msg_transfer_service_num}; i++)); do if [ $prome_port != "" ]; then cmd="$cmd --prometheus_port $prome_port" fi - echo "==========================start msg_transfer server===========================">>../logs/openIM.log - $cmd >>../logs/openIM.log 2>&1 & + echo "==========================start msg_transfer server===========================">>$OPENIM_ROOT/logs/openIM.log + $cmd >>$OPENIM_ROOT/logs/openIM.log 2>&1 & done #Check launched service process @@ -60,9 +67,9 @@ then newPid=`ps aux | grep -w ./${openim-msgtransfer} | grep -v grep|awk '{print $2}'` allPorts="" echo -e ${SKY_BLUE_PREFIX}"SERVICE START SUCCESS "${COLOR_SUFFIX} - echo -e ${SKY_BLUE_PREFIX}"SERVICE_NAME: "${COLOR_SUFFIX}${YELLOW_PREFIX}${openim-msgtransfer}${COLOR_SUFFIX} - echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${YELLOW_PREFIX}${newPid}${COLOR_SUFFIX} - echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${YELLOW_PREFIX}${allPorts}${COLOR_SUFFIX} + echo -e ${SKY_BLUE_PREFIX}"SERVICE_NAME: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${openim-msgtransfer}${COLOR_SUFFIX} + echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${newPid}${COLOR_SUFFIX} + echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${allPorts}${COLOR_SUFFIX} else - echo -e ${YELLOW_PREFIX}${openim-msgtransfer}${COLOR_SUFFIX}${RED_PREFIX}"SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} + echo -e ${BACKGROUND_GREEN}${openim-msgtransfer}${COLOR_SUFFIX}${RED_PREFIX}"SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} fi diff --git a/scripts/path_info.cfg b/scripts/path_info.sh similarity index 73% rename from scripts/path_info.cfg rename to scripts/path_info.sh index 57f328a47..0edc0b5b2 100755 --- a/scripts/path_info.cfg +++ b/scripts/path_info.sh @@ -1,22 +1,17 @@ +#!/usr/bin/env bash # Determine the architecture and version architecture=$(uname -m) version=$(uname -s | tr '[:upper:]' '[:lower:]') #Include shell font styles and some basic information SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) -OPENIM_ROOT=$(dirname "${SCRIPTS_ROOT}")/.. +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. #Include shell font styles and some basic information -source $SCRIPTS_ROOT/style_info.cfg -source $SCRIPTS_ROOT/path_info.cfg -source $SCRIPTS_ROOT/function.sh +source $SCRIPTS_ROOT/style_info.sh cd $SCRIPTS_ROOT -echo -e "${BACKGROUND_YELLOW}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_YELLOW}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_YELLOW}=======>pwd=$PWD${COLOR_SUFFIX}" - # Define the supported architectures and corresponding bin directories declare -A supported_architectures=( ["linux-amd64"]="_output/bin/platforms/linux/amd64" @@ -47,29 +42,28 @@ echo "================> BIN_DIR: $OPENIM_ROOT/$BIN_DIR" # Don't put the space between "=" openim_msggateway="openim-msggateway" msg_gateway_binary_root="$OPENIM_ROOT/$BIN_DIR" -msg_gateway_source_root="../cmd/msggateway/" +msg_gateway_source_root="$OPENIM_ROOT/cmd/msggateway/" msg_name="openim-rpc-msg" msg_binary_root="$OPENIM_ROOT/$BIN_DIR" -msg_source_root="../cmd/openim-rpc/msg/" +msg_source_root="$OPENIM_ROOT/cmd/openim-rpc/msg/" push_name="openim-push" push_binary_root="$OPENIM_ROOT/$BIN_DIR" -push_source_root="../cmd/push/" +push_source_root="$OPENIM_ROOT/cmd/push/" openim_msgtransfer="openim-rpc-msg_transfer" msg_transfer_binary_root="$OPENIM_ROOT/$BIN_DIR" -msg_transfer_source_root="$OPENIM_ROOT/cmd/msgtransfer/" +msg_transfer_source_root="$OPENIM_ROOT/cmd/openim-msgtransfer/" msg_transfer_service_num=4 cron_task_name="openim-crontask" cron_task_binary_root="$OPENIM_ROOT/$BIN_DIR" -cron_task_source_root="$OPENIM_ROOT/cmd/crontask/" - +cron_task_source_root="$OPENIM_ROOT/cmd/openim-crontask/" -cmd_utils_name="openim_cmd_utils" +cmd_utils_name="openim-cmdutils" cmd_utils_binary_root="$OPENIM_ROOT/$BIN_DIR" -cmd_utils_source_root="$OPENIM_ROOT/cmd/cmduitls/" +cmd_utils_source_root="$OPENIM_ROOT/cmd/openim-cmdutils/" # Global configuration file default dir config_path="$OPENIM_ROOT/config/config.yaml" @@ -77,15 +71,15 @@ config_path="$OPENIM_ROOT/config/config.yaml" # servicefile dir path service_source_root=( # api service file - "../cmd/api/" + "$OPENIM_ROOT/cmd/api/" # rpc service file - "../cmd/openim-rpc/openim-rpc-user/" - "../cmd/openim-rpc/openim-rpc-friend/" - "../cmd/openim-rpc/openim-rpc-group/" - "../cmd/openim-rpc/openim-rpc-auth/" - "../cmd/openim-rpc/openim-rpc-conversation/" - "../cmd/openim-rpc/openim-rpc-third/" - "../cmd/openim-crontask" + "$OPENIM_ROOT/cmd/openim-rpc/openim-rpc-user/" + "$OPENIM_ROOT/cmd/openim-rpc/openim-rpc-friend/" + "$OPENIM_ROOT/cmd/openim-rpc/openim-rpc-group/" + "$OPENIM_ROOT/cmd/openim-rpc/openim-rpc-auth/" + "$OPENIM_ROOT/cmd/openim-rpc/openim-rpc-conversation/" + "$OPENIM_ROOT/cmd/openim-rpc/openim-rpc-third/" + "$OPENIM_ROOT/cmd/openim-crontask" "${msg_gateway_source_root}" "${msg_transfer_source_root}" "${msg_source_root}" diff --git a/scripts/push_start.sh b/scripts/push_start.sh index 204f710e7..55ac28bb3 100755 --- a/scripts/push_start.sh +++ b/scripts/push_start.sh @@ -14,12 +14,19 @@ # limitations under the License. #Include shell font styles and some basic information +SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. #Include shell font styles and some basic information -source $OPENIM_ROOT/scripts/style_info.cfg -source $OPENIM_ROOT/scripts/path_info.cfg -source $OPENIM_ROOT/scripts/function.sh +source $SCRIPTS_ROOT/style_info.sh +source $SCRIPTS_ROOT/path_info.sh +source $SCRIPTS_ROOT/function.sh + +cd $SCRIPTS_ROOT + +echo -e "${BACKGROUND_GREEN}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_GREEN}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_GREEN}=======>pwd=$PWD${COLOR_SUFFIX}" bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" @@ -46,8 +53,8 @@ sleep 1 cd ${push_binary_root} for ((i = 0; i < ${#rpc_ports[@]}; i++)); do - echo "==========================start push server===========================">>../logs/openIM.log - nohup ./${push_name} --port ${rpc_ports[$i]} --prometheus_port ${prome_ports[$i]} >>../logs/openIM.log 2>&1 & + echo "==========================start push server===========================">>$OPENIM_ROOT/logs/openIM.log + nohup ./${push_name} --port ${rpc_ports[$i]} --prometheus_port ${prome_ports[$i]} >>$OPENIM_ROOT/logs/openIM.log 2>&1 & done sleep 3 @@ -62,9 +69,9 @@ if [ $check -ge 1 ]; then allPorts=${allPorts}"$i " done echo -e ${SKY_BLUE_PREFIX}"SERVICE START SUCCESS "${COLOR_SUFFIX} - echo -e ${SKY_BLUE_PREFIX}"SERVICE_NAME: "${COLOR_SUFFIX}${YELLOW_PREFIX}${push_name}${COLOR_SUFFIX} - echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${YELLOW_PREFIX}${newPid}${COLOR_SUFFIX} - echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${YELLOW_PREFIX}${allPorts}${COLOR_SUFFIX} + echo -e ${SKY_BLUE_PREFIX}"SERVICE_NAME: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${push_name}${COLOR_SUFFIX} + echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${newPid}${COLOR_SUFFIX} + echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${allPorts}${COLOR_SUFFIX} else - echo -e ${YELLOW_PREFIX}${push_name}${COLOR_SUFFIX}${RED_PREFIX}"SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} + echo -e ${BACKGROUND_GREEN}${push_name}${COLOR_SUFFIX}${RED_PREFIX}"SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} fi diff --git a/scripts/start_all.sh b/scripts/start_all.sh index 44c613479..976320dad 100755 --- a/scripts/start_all.sh +++ b/scripts/start_all.sh @@ -21,15 +21,15 @@ SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) OPENIM_ROOT=$(dirname "${SCRIPTS_ROOT}")/.. #Include shell font styles and some basic information -source $SCRIPTS_ROOT/style_info.cfg -source $SCRIPTS_ROOT/path_info.cfg +source $SCRIPTS_ROOT/style_info.sh +source $SCRIPTS_ROOT/path_info.sh source $SCRIPTS_ROOT/function.sh cd $SCRIPTS_ROOT -echo -e "${BACKGROUND_YELLOW}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_YELLOW}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_YELLOW}=======>pwd=$PWD${COLOR_SUFFIX}" +echo -e "${BACKGROUND_GREEN}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_GREEN}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_GREEN}=======>pwd=$PWD${COLOR_SUFFIX}" bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" @@ -65,7 +65,7 @@ for i in ${need_to_start_server_shell[*]}; do echo -e "" # Print script execution message - echo -e "=========> ${YELLOW_PREFIX}Executing ${i}...${COLOR_SUFFIX}" + echo -e "=========> ${BACKGROUND_GREEN}Executing ${i}...${COLOR_SUFFIX}" echo -e "" ./$i diff --git a/scripts/start_cron.sh b/scripts/start_cron.sh index c7ce4e451..1fccedcf2 100755 --- a/scripts/start_cron.sh +++ b/scripts/start_cron.sh @@ -14,19 +14,24 @@ # limitations under the License. #Include shell font styles and some basic information +SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. #Include shell font styles and some basic information -source $OPENIM_ROOT/scripts/style_info.cfg -source $OPENIM_ROOT/scripts/path_info.cfg -source $OPENIM_ROOT/scripts/function.sh +source $SCRIPTS_ROOT/style_info.sh +source $SCRIPTS_ROOT/path_info.sh +source $SCRIPTS_ROOT/function.sh + +cd $SCRIPTS_ROOT + +echo -e "${BACKGROUND_GREEN}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_GREEN}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_GREEN}=======>pwd=$PWD${COLOR_SUFFIX}" bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" sdk_db_dir="$OPENIM_ROOT/sdk/db/" -cd "$OPENIM_ROOT/scripts/" - #Check if the service exists #If it is exists,kill this process check=`ps aux | grep -w ./${cron_task_name} | grep -v grep| wc -l` @@ -40,8 +45,8 @@ sleep 1 cd ${cron_task_binary_root} #for ((i = 0; i < ${cron_task_service_num}; i++)); do - echo "==========================start cron_task process===========================">>../logs/openIM.log -nohup ./${cron_task_name} >>../logs/openIM.log 2>&1 & + echo "==========================start cron_task process===========================">>$OPENIM_ROOT/logs/openIM.log +nohup ./${cron_task_name} >>$OPENIM_ROOT/logs/openIM.log 2>&1 & #done #Check launched service process @@ -51,9 +56,9 @@ then newPid=`ps aux | grep -w ./${cron_task_name} | grep -v grep|awk '{print $2}'` allPorts="" echo -e ${SKY_BLUE_PREFIX}"SERVICE START SUCCESS "${COLOR_SUFFIX} - echo -e ${SKY_BLUE_PREFIX}"SERVICE_NAME: "${COLOR_SUFFIX}${YELLOW_PREFIX}${cron_task_name}${COLOR_SUFFIX} - echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${YELLOW_PREFIX}${newPid}${COLOR_SUFFIX} - echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${YELLOW_PREFIX}${allPorts}${COLOR_SUFFIX} + echo -e ${SKY_BLUE_PREFIX}"SERVICE_NAME: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${cron_task_name}${COLOR_SUFFIX} + echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${newPid}${COLOR_SUFFIX} + echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${allPorts}${COLOR_SUFFIX} else - echo -e ${YELLOW_PREFIX}${cron_task_name}${COLOR_SUFFIX}${RED_PREFIX}"SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} + echo -e ${BACKGROUND_GREEN}${cron_task_name}${COLOR_SUFFIX}${RED_PREFIX}"SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} fi diff --git a/scripts/start_rpc_service.sh b/scripts/start_rpc_service.sh index 2d0e70f63..1cafd2b1c 100755 --- a/scripts/start_rpc_service.sh +++ b/scripts/start_rpc_service.sh @@ -15,25 +15,22 @@ #Include shell font styles and some basic information SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) -OPENIM_ROOT=$(dirname "${SCRIPTS_ROOT}")/.. +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. #Include shell font styles and some basic information -source $SCRIPTS_ROOT/style_info.cfg -source $SCRIPTS_ROOT/path_info.cfg -source $SCRIPTS_ROOT/function.sh +source $SCRIPTS_ROOT/style_info.sh +source $SCRIPTS_ROOT/path_info.sh cd $SCRIPTS_ROOT -echo -e "${BACKGROUND_YELLOW}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_YELLOW}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_YELLOW}=======>pwd=$PWD${COLOR_SUFFIX}" +echo -e "${BACKGROUND_GREEN}${CYAN_PREFIX}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_GREEN}${CYAN_PREFIX}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${BACKGROUND_GREEN}${CYAN_PREFIX}=======>pwd=$PWD${COLOR_SUFFIX}" bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" sdk_db_dir="$OPENIM_ROOT/sdk/db/" -cd $OPENIM_ROOT - #service filename service_filename=( #api @@ -88,8 +85,9 @@ for ((i = 0; i < ${#service_filename[*]}; i++)); do kill -9 $(eval $pid) sleep 0.5 fi - cd $ - #Get the rpc port in the configuration file + cd $OPENIM_ROOT + cd $BIN_DIR + # Get the rpc port in the configuration file portList=$(cat $config_path | grep ${service_port_name[$i]} | awk -F '[:]' '{print $NF}') list_to_string ${portList} service_ports=($ports_array) @@ -109,8 +107,8 @@ for ((i = 0; i < ${#service_filename[*]}; i++)); do cmd="./${service_filename[$i]} --port ${service_ports[$j]}" fi echo $cmd - echo "=====================start ${service_filename[$i]}======================">>../logs/openIM.log - nohup $cmd >>../logs/openIM.log 2>&1 & + echo "=====================start ${service_filename[$i]}======================">>$OPENIM_ROOT/logs/openIM.log + nohup $cmd >>$OPENIM_ROOT/logs/openIM.log 2>&1 & sleep 1 pid="netstat -ntlp|grep $j |awk '{printf \$7}'|cut -d/ -f1" echo -e "${GREEN_PREFIX}${service_filename[$i]} start success,port number:${service_ports[$j]} pid:$(eval $pid)$COLOR_SUFFIX" diff --git a/scripts/stop_all.sh b/scripts/stop_all.sh index 7bec5b53f..4e3537ea7 100755 --- a/scripts/stop_all.sh +++ b/scripts/stop_all.sh @@ -17,8 +17,8 @@ OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. #Include shell font styles and some basic information -source $OPENIM_ROOT/scripts/style_info.cfg -source $OPENIM_ROOT/scripts/path_info.cfg +source $OPENIM_ROOT/scripts/style_info.sh +source $OPENIM_ROOT/scripts/path_info.sh source $OPENIM_ROOT/scripts/function.sh bin_dir="$BIN_DIR" diff --git a/scripts/style_info.cfg b/scripts/style_info.sh similarity index 98% rename from scripts/style_info.cfg rename to scripts/style_info.sh index f0b6a32db..631f8a5f1 100755 --- a/scripts/style_info.cfg +++ b/scripts/style_info.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + COLOR_SUFFIX="\033[0m" # End all colors and special effects BLACK_PREFIX="\033[30m" # Black prefix From 826c86907cc4bde6a21c4ef7fc7af96517021bcb Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Sun, 9 Jul 2023 17:12:16 +0800 Subject: [PATCH 45/73] feat: complete the basic build Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- .gitignore | 2 +- cmd/openim-msgtransfer/Makefile | 2 +- cmd/openim-msgtransfer/deploy.Dockerfile | 4 ++-- scripts/make-rules/golang.mk | 4 ++-- scripts/path_info.sh | 8 ++++---- scripts/start_rpc_service.sh | 1 + 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index a547540a7..6e29bad2c 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,7 @@ _output/ deploy/openim_demo deploy/openim-api deploy/openim-rpc-msg_gateway -deploy/openim-rpc-msg_transfer +deploy/openim-msgtransfer deploy/openim-push deploy/openim_timer_task deploy/openim-rpc-user diff --git a/cmd/openim-msgtransfer/Makefile b/cmd/openim-msgtransfer/Makefile index 9d4aa7dec..e2d058f6d 100644 --- a/cmd/openim-msgtransfer/Makefile +++ b/cmd/openim-msgtransfer/Makefile @@ -1,6 +1,6 @@ .PHONY: all build run gotool install clean help -NAME=openim-rpc-msg_transfer +NAME=openim-msgtransfer BIN_DIR=../../bin/ OS:= $(or $(os),linux) diff --git a/cmd/openim-msgtransfer/deploy.Dockerfile b/cmd/openim-msgtransfer/deploy.Dockerfile index 747b7f422..78f1f955c 100644 --- a/cmd/openim-msgtransfer/deploy.Dockerfile +++ b/cmd/openim-msgtransfer/deploy.Dockerfile @@ -25,8 +25,8 @@ RUN apt-get install -y vim curl tzdata gawk RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata RUN apt-get -qq update \ && apt-get -qq install -y --no-install-recommends ca-certificates curl -COPY ./openim-rpc-msg_transfer ./ +COPY ./openim-msgtransfer ./ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config"] -CMD ["./openim-rpc-msg_transfer","--prometheus_port", "21400"] +CMD ["./openim-msgtransfer","--prometheus_port", "21400"] diff --git a/scripts/make-rules/golang.mk b/scripts/make-rules/golang.mk index 1b6c92ed3..2a6fd7f0d 100644 --- a/scripts/make-rules/golang.mk +++ b/scripts/make-rules/golang.mk @@ -76,7 +76,7 @@ EXCLUDE_TESTS=github.com/OpenIMSDK/Open-IM-Server/test github.com/OpenIMSDK/Open # ├── openim-crontask # ├── openim_demo # ├── openim-rpc-msg_gateway -# ├── openim-rpc-msg_transfer +# ├── openim-msgtransfer # ├── openim-push # ├── rpc/openim_admin_cms/ - main.go # └── test/ - main.go @@ -84,7 +84,7 @@ EXCLUDE_TESTS=github.com/OpenIMSDK/Open-IM-Server/test github.com/OpenIMSDK/Open # PLATFORM=linux_amd64 # OS=linux # ARCH=amd64 -# BINS=openim-api openim_cms_api openim-crontask openim_demo openim-rpc-msg_gateway openim-rpc-msg_transfer openim-push +# BINS=openim-api openim_cms_api openim-crontask openim_demo openim-rpc-msg_gateway openim-msgtransfer openim-push # BIN_DIR=/root/workspaces/OpenIM/_output/bin # ============================================================================== diff --git a/scripts/path_info.sh b/scripts/path_info.sh index 0edc0b5b2..ab6f2b2df 100755 --- a/scripts/path_info.sh +++ b/scripts/path_info.sh @@ -42,17 +42,17 @@ echo "================> BIN_DIR: $OPENIM_ROOT/$BIN_DIR" # Don't put the space between "=" openim_msggateway="openim-msggateway" msg_gateway_binary_root="$OPENIM_ROOT/$BIN_DIR" -msg_gateway_source_root="$OPENIM_ROOT/cmd/msggateway/" +msg_gateway_source_root="$OPENIM_ROOT/cmd/openim-msggateway/" msg_name="openim-rpc-msg" msg_binary_root="$OPENIM_ROOT/$BIN_DIR" -msg_source_root="$OPENIM_ROOT/cmd/openim-rpc/msg/" +msg_source_root="$OPENIM_ROOT/cmd/openim-rpc/openim-rpc-msg/" push_name="openim-push" push_binary_root="$OPENIM_ROOT/$BIN_DIR" -push_source_root="$OPENIM_ROOT/cmd/push/" +push_source_root="$OPENIM_ROOT/cmd/openim-push/" -openim_msgtransfer="openim-rpc-msg_transfer" +openim_msgtransfer="openim-msgtransfer" msg_transfer_binary_root="$OPENIM_ROOT/$BIN_DIR" msg_transfer_source_root="$OPENIM_ROOT/cmd/openim-msgtransfer/" msg_transfer_service_num=4 diff --git a/scripts/start_rpc_service.sh b/scripts/start_rpc_service.sh index 1cafd2b1c..132afdfe5 100755 --- a/scripts/start_rpc_service.sh +++ b/scripts/start_rpc_service.sh @@ -20,6 +20,7 @@ OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. #Include shell font styles and some basic information source $SCRIPTS_ROOT/style_info.sh source $SCRIPTS_ROOT/path_info.sh +source $SCRIPTS_ROOT/function.sh cd $SCRIPTS_ROOT From 3c73ba03ba4ee3528ac48c362b1998001d1be785 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Sun, 9 Jul 2023 18:46:42 +0800 Subject: [PATCH 46/73] feat: fix code Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- internal/msgtransfer/persistent_msg_handler.go | 7 +------ pkg/common/kafka/consumer_group.go | 6 ------ pkg/utils/strings.go | 6 ------ pkg/utils/time_format.go | 6 ------ scripts/msg_gateway_start.sh | 2 +- scripts/msg_transfer_start.sh | 2 +- scripts/push_start.sh | 2 +- scripts/start_cron.sh | 2 +- 8 files changed, 5 insertions(+), 28 deletions(-) diff --git a/internal/msgtransfer/persistent_msg_handler.go b/internal/msgtransfer/persistent_msg_handler.go index 30b739f7e..eaf47dee9 100644 --- a/internal/msgtransfer/persistent_msg_handler.go +++ b/internal/msgtransfer/persistent_msg_handler.go @@ -1,9 +1,4 @@ -/* -** description(""). -** copyright('tuoyun,www.tuoyun.net'). -** author("fg,Gordon@tuoyun.net"). -** time(2021/5/11 15:37). - */package msgtransfer +package msgtransfer import ( "context" diff --git a/pkg/common/kafka/consumer_group.go b/pkg/common/kafka/consumer_group.go index 065fc6277..2e0155f67 100644 --- a/pkg/common/kafka/consumer_group.go +++ b/pkg/common/kafka/consumer_group.go @@ -1,9 +1,3 @@ -/* -** description(""). -** copyright('tuoyun,www.tuoyun.net'). -** author("fg,Gordon@tuoyun.net"). -** time(2021/5/11 9:36). - */ package kafka import ( diff --git a/pkg/utils/strings.go b/pkg/utils/strings.go index 5c985d724..106c6a112 100644 --- a/pkg/utils/strings.go +++ b/pkg/utils/strings.go @@ -1,9 +1,3 @@ -/* -** description(""). -** copyright('tuoyun,www.tuoyun.net'). -** author("fg,Gordon@tuoyun.net"). -** time(2021/4/8 15:09). - */ package utils import ( diff --git a/pkg/utils/time_format.go b/pkg/utils/time_format.go index 59abb90c4..af1be7f04 100644 --- a/pkg/utils/time_format.go +++ b/pkg/utils/time_format.go @@ -1,9 +1,3 @@ -/* -** description(""). -** copyright('tuoyun,www.tuoyun.net'). -** author("fg,Gordon@tuoyun.net"). -** time(2021/2/22 11:52). - */ package utils import ( diff --git a/scripts/msg_gateway_start.sh b/scripts/msg_gateway_start.sh index 7df840b21..164d993ae 100755 --- a/scripts/msg_gateway_start.sh +++ b/scripts/msg_gateway_start.sh @@ -79,5 +79,5 @@ if [ $check -ge 1 ]; then echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${allNewPid}${COLOR_SUFFIX} echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${allPorts}${COLOR_SUFFIX} else - echo -e ${BACKGROUND_GREEN}${openim-msggateway}${COLOR_SUFFIX}${RED_PREFIX}"SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} + echo -e ${BACKGROUND_GREEN}${openim-msggateway}${COLOR_SUFFIX}${RED_PREFIX}"\n SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} fi diff --git a/scripts/msg_transfer_start.sh b/scripts/msg_transfer_start.sh index b038d125a..741485356 100755 --- a/scripts/msg_transfer_start.sh +++ b/scripts/msg_transfer_start.sh @@ -71,5 +71,5 @@ allPorts="" echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${newPid}${COLOR_SUFFIX} echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${allPorts}${COLOR_SUFFIX} else - echo -e ${BACKGROUND_GREEN}${openim-msgtransfer}${COLOR_SUFFIX}${RED_PREFIX}"SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} + echo -e ${BACKGROUND_GREEN}${openim-msgtransfer}${COLOR_SUFFIX}${RED_PREFIX}"\n SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} fi diff --git a/scripts/push_start.sh b/scripts/push_start.sh index 55ac28bb3..ea353cd56 100755 --- a/scripts/push_start.sh +++ b/scripts/push_start.sh @@ -73,5 +73,5 @@ if [ $check -ge 1 ]; then echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${newPid}${COLOR_SUFFIX} echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${allPorts}${COLOR_SUFFIX} else - echo -e ${BACKGROUND_GREEN}${push_name}${COLOR_SUFFIX}${RED_PREFIX}"SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} + echo -e ${BACKGROUND_GREEN}${push_name}${COLOR_SUFFIX}${RED_PREFIX}"\n SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} fi diff --git a/scripts/start_cron.sh b/scripts/start_cron.sh index 1fccedcf2..bef9ca36e 100755 --- a/scripts/start_cron.sh +++ b/scripts/start_cron.sh @@ -60,5 +60,5 @@ allPorts="" echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${newPid}${COLOR_SUFFIX} echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${allPorts}${COLOR_SUFFIX} else - echo -e ${BACKGROUND_GREEN}${cron_task_name}${COLOR_SUFFIX}${RED_PREFIX}"SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} + echo -e ${BACKGROUND_GREEN}${cron_task_name}${COLOR_SUFFIX}${RED_PREFIX}"\n SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} fi From eaaf309b30193bcac7f1c6a5c63a7994591cbf81 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Mon, 10 Jul 2023 11:51:47 +0800 Subject: [PATCH 47/73] feat: add link file Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- CHANGELOG.md | 1 + CHANGELOG/.chglog/CHANGELOG.tpl.md | 38 +++-- CHANGELOG/.chglog/config.yml | 22 +-- CHANGELOG/CHANGELOG-0.0.0.md | 227 ----------------------------- CHANGELOG/CHANGELOG-1.0.0.md | 161 ++++++++++++++++++++ CHANGELOG/CHANGELOG-2.0.0.md | 169 +++++++++++++++++++++ CHANGELOG/CHANGELOG-3.0.0.md | 153 +++++++++++++++++++ CHANGELOG/CHANGELOG.md | 18 ++- 8 files changed, 539 insertions(+), 250 deletions(-) create mode 120000 CHANGELOG.md delete mode 100644 CHANGELOG/CHANGELOG-0.0.0.md create mode 100644 CHANGELOG/CHANGELOG-1.0.0.md create mode 100644 CHANGELOG/CHANGELOG-2.0.0.md create mode 100644 CHANGELOG/CHANGELOG-3.0.0.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 120000 index 000000000..83b694704 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ +CHANGELOG.md \ No newline at end of file diff --git a/CHANGELOG/.chglog/CHANGELOG.tpl.md b/CHANGELOG/.chglog/CHANGELOG.tpl.md index a1618de92..5683d039d 100644 --- a/CHANGELOG/.chglog/CHANGELOG.tpl.md +++ b/CHANGELOG/.chglog/CHANGELOG.tpl.md @@ -1,40 +1,56 @@ -{{ range .Versions }} - -## {{ if .Tag.Previous }}[{{ .Tag.Name }}]({{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}){{ else }}{{ .Tag.Name }}{{ end }} +{{ if .Versions -}} + +## [Unreleased] -> {{ datetime "2006-01-02" .Tag.Date }} +{{ if .Unreleased.CommitGroups -}} +{{ range .Unreleased.CommitGroups -}} +### {{ .Title }} +{{ range .Commits -}} +- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} +{{ end }} +{{ end -}} +{{ end -}} +{{ end -}} +{{ range .Versions }} + +## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }} {{ range .CommitGroups -}} ### {{ .Title }} - {{ range .Commits -}} -* {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} +- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} {{ end }} {{ end -}} {{- if .RevertCommits -}} ### Reverts - {{ range .RevertCommits -}} -* {{ .Revert.Header }} +- {{ .Revert.Header }} {{ end }} {{ end -}} {{- if .MergeCommits -}} ### Pull Requests - {{ range .MergeCommits -}} -* {{ .Header }} +- {{ .Header }} {{ end }} {{ end -}} {{- if .NoteGroups -}} {{ range .NoteGroups -}} ### {{ .Title }} - {{ range .Notes }} {{ .Body }} {{ end }} {{ end -}} {{ end -}} +{{ end -}} + +{{- if .Versions }} +[Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD +{{ range .Versions -}} +{{ if .Tag.Previous -}} +[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }} +{{ end -}} +{{ end -}} {{ end -}} \ No newline at end of file diff --git a/CHANGELOG/.chglog/config.yml b/CHANGELOG/.chglog/config.yml index b1b42dc1a..1eed7863b 100644 --- a/CHANGELOG/.chglog/config.yml +++ b/CHANGELOG/.chglog/config.yml @@ -5,18 +5,18 @@ info: repository_url: https://github.com/OpenIMSDK/Open-IM-Server options: commits: - # filters: - # Type: - # - feat - # - fix - # - perf - # - refactor + filters: + Type: + - feat + - fix + - perf + - refactor commit_groups: - # title_maps: - # feat: Features - # fix: Bug Fixes - # perf: Performance Improvements - # refactor: Code Refactoring + title_maps: + feat: Features + fix: Bug Fixes + perf: Performance Improvements + refactor: Code Refactoring header: pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$" pattern_maps: diff --git a/CHANGELOG/CHANGELOG-0.0.0.md b/CHANGELOG/CHANGELOG-0.0.0.md deleted file mode 100644 index 65a0ab1b1..000000000 --- a/CHANGELOG/CHANGELOG-0.0.0.md +++ /dev/null @@ -1,227 +0,0 @@ - - -## [v2.3.3](https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.3.2...v2.3.3) - -> 2022-09-18 - - - -## [v2.3.2](https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.3.0-rc2...v2.3.2) - -> 2022-09-09 - -### GetSelfUserInfo - -* Handle the case where the user does not exist - -### Pull Requests - -* Merge pull request [#267](https://github.com/OpenIMSDK/Open-IM-Server/issues/267) from ouyangshi/del -* Merge pull request [#265](https://github.com/OpenIMSDK/Open-IM-Server/issues/265) from ouyangshi/del -* Merge pull request [#252](https://github.com/OpenIMSDK/Open-IM-Server/issues/252) from x-shadow-man/config-perf -* Merge pull request [#263](https://github.com/OpenIMSDK/Open-IM-Server/issues/263) from ouyangshi/main -* Merge pull request [#258](https://github.com/OpenIMSDK/Open-IM-Server/issues/258) from ouyangshi/main -* Merge pull request [#261](https://github.com/OpenIMSDK/Open-IM-Server/issues/261) from ouyangshi/v2.3.0release - - - -## [v2.3.0-rc2](https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.3.0-rc1...v2.3.0-rc2) - -> 2022-07-29 - - - -## [v2.3.0-rc1](https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.3.0-rc0...v2.3.0-rc1) - -> 2022-07-25 - - - -## [v2.3.0-rc0](https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.2.0...v2.3.0-rc0) - -> 2022-07-15 - - - -## [v2.2.0](https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.1.0...v2.2.0) - -> 2022-07-01 - - - -## [v2.1.0](https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.10...v2.1.0) - -> 2022-06-17 - - - -## [v2.0.10](https://github.com/OpenIMSDK/Open-IM-Server/compare/list...v2.0.10) - -> 2022-05-13 - - - -## [list](https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.9...list) - -> 2022-04-29 - - - -## [v2.0.9](https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.7...v2.0.9) - -> 2022-04-29 - -### Reverts - -* update etcd to v3.5.2 ([#206](https://github.com/OpenIMSDK/Open-IM-Server/issues/206)) - - - -## [v2.0.7](https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.6...v2.0.7) - -> 2022-04-08 - - - -## [v2.0.6](https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.5...v2.0.6) - -> 2022-04-01 - -### Fix - -* json tag value of UserIDResult.UserID ([#178](https://github.com/OpenIMSDK/Open-IM-Server/issues/178)) - -### Pull Requests - -* Merge pull request [#173](https://github.com/OpenIMSDK/Open-IM-Server/issues/173) from OpenIMSDK/tuoyun - - - -## [v2.0.5](https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.04...v2.0.5) - -> 2022-03-24 - - - -## [v2.04](https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.3...v2.04) - -> 2022-03-18 - - - -## [v2.0.3](https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.2...v2.0.3) - -> 2022-03-11 - - - -## [v2.0.2](https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.1...v2.0.2) - -> 2022-03-04 - -### Version - -* "3" -* "2" - - - -## [v2.0.1](https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.0...v2.0.1) - -> 2022-02-25 - -### DbMysqlDatabaseName - -* openIM_v2 - - - -## [v2.0.0](https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.7...v2.0.0) - -> 2022-02-23 - -### Pb - -* openim_sdk.OfflinePushInfo - -### Pull Requests - -* Merge pull request [#131](https://github.com/OpenIMSDK/Open-IM-Server/issues/131) from OpenIMSDK/cms-dev - - - -## [v1.0.7](https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.6...v1.0.7) - -> 2021-12-17 - - - -## [v1.0.6](https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.5...v1.0.6) - -> 2021-12-10 - -### Pb - -* openim_sdk.OfflinePushInfo - - - -## [v1.0.5](https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.4...v1.0.5) - -> 2021-12-03 - - - -## [v1.0.4](https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.3...v1.0.4) - -> 2021-11-25 - - - -## [v1.0.3](https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.1...v1.0.3) - -> 2021-11-12 - -### Feat - -* test utils ([#26](https://github.com/OpenIMSDK/Open-IM-Server/issues/26)) - -### Fix - -* Startup error ([#11](https://github.com/OpenIMSDK/Open-IM-Server/issues/11)) - -### Pull Requests - -* Merge pull request [#12](https://github.com/OpenIMSDK/Open-IM-Server/issues/12) from njulk/main -* Merge pull request [#9](https://github.com/OpenIMSDK/Open-IM-Server/issues/9) from xmcy0011/dev -* Merge pull request [#6](https://github.com/OpenIMSDK/Open-IM-Server/issues/6) from Bloomingg/int - - - -## [v1.0.1](https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.0...v1.0.1) - -> 2021-11-04 - - - -## v1.0.0 - -> 2021-10-28 - -### Ci - -* ignore files created by docker-compose ([#19](https://github.com/OpenIMSDK/Open-IM-Server/issues/19)) - -### Feat - -* optimise get server ip ([#20](https://github.com/OpenIMSDK/Open-IM-Server/issues/20)) - -### Reverts - -* friend modify -* update - -### Pull Requests - -* Merge pull request [#7](https://github.com/OpenIMSDK/Open-IM-Server/issues/7) from memory-qianxiao/docker-compose_update -* Merge pull request [#4](https://github.com/OpenIMSDK/Open-IM-Server/issues/4) from wujingke/patch-1 diff --git a/CHANGELOG/CHANGELOG-1.0.0.md b/CHANGELOG/CHANGELOG-1.0.0.md new file mode 100644 index 000000000..966bf1895 --- /dev/null +++ b/CHANGELOG/CHANGELOG-1.0.0.md @@ -0,0 +1,161 @@ + +## [Unreleased] + +### Bug Fixes +- Fix make Help +- fix the cicd error of the robot +- binary name modification +- optimization details +- remove test file +- rename open_im to openim +- details optimize +- fix cmd filename +- fix scripts +- script -> scripts +- script -> scripts +- env file +- remote +- comment invite trigger actions +- docker compose +- version ploblem +- fix branch git hooks +- Improved initial docker scripts +- Update CONTRIBUTING.md +- Update README.md file +- docker compose +- comment invite trigger actions +- gosec help us audit the Go code +- changelog error +- cover adjustment in some actions +- make up for small problems with pre-push D +- optimize codeql ananlysis workflows +- file name quest +- binary name modification +- copyright owner +- readme styles +- markfown lint test cicd actions deplpy +- designing and supplementing gnore +- robot name +- readme file +- fixed some connection display issues +- Update README image address +- directory name +- **gnore:** remove .github + +### Code Refactoring +- add openim-sdk-core submodule + +### Features +- add actions form org +- add deployments readme file +- add all command +- sets the absolute path of the script +- add cyan +- add cyan +- add cyan +- add cyan +- add cyan +- add COLOR_SUFFIX=033[0m # End all colors and special effects +- fix scripts +- add chmod +- add all page +- add make file +- add scripts +- add code comment +- add make +- add actions form org +- add license +- add makefile bug +- add more feature in scripts +- add git hook sign +- add codeowners file +- add release +- add common scripts +- add copyright +- add scripts lib +- use robot to migrate code +- add feature +- add code ql system +- fix openim ci +- fix code +- set scripts +- set scripts +- add chmod script +- ver3 branch +- add scripts chmod +- add scripts +- docker compose logs unified directory +- docker compose logs unified directory +- add copyright permission +- optimize scripts and makefiles +- optimize scripts and makefiles +- set up golang ci +- refactoring main +- migrate the original main branch code +- add code comment +- complete the basic build +- adding actions ([#461](https://github.com/OpenIMSDK/Open-IM-Server/issues/461)) +- project actions +- service architecture +- service architecture +- Automatic PR title AI fix +- vscode gitignore +- sync file to * +- sync file to * +- sync core branch +- copy license +- make multiarch +- sync robot +- tools optimize +- resolving initialization +- exchange tokens for the robot +- provide robot for use +- support for closing issues +- comment invite trigger actions +- add codecov context +- super log dergen +- set the structure of the rebase +- adjust the install go-gitlint role +- complete the githook design +- designing and supplementing gnore +- add recvID/groupID for msg modification callback ([#351](https://github.com/OpenIMSDK/Open-IM-Server/issues/351)) +- extensions ignore files +- a feat of Makefile large-scale engineering design ([#370](https://github.com/OpenIMSDK/Open-IM-Server/issues/370)) +- zh-reamde +- ADOPTERS +- readme add shields +- readme add shields +- super dependencies makefile help +- Improve tools information +- Improve copyright information +- Improve copyright information +- Improve help information +- good frist issue readme +- good frist issue readme +- good frist issue readme +- slack +- **githook:** make sure to trigger githook when you make +- **main:** add stale labels +- **main:** dockerfile fix +- **main:** update readme in v3 +- **make:** define the common implementation of the base +- **make:** define common githook +- **make:** add file name pre-commit for githook +- **make:** determine the base constant setting for common + + + +## v1.0.0 - 2021-10-28 +### Features +- optimise get server ip ([#20](https://github.com/OpenIMSDK/Open-IM-Server/issues/20)) + +### Reverts +- friend modify +- update + +### Pull Requests +- Merge pull request [#7](https://github.com/OpenIMSDK/Open-IM-Server/issues/7) from memory-qianxiao/docker-compose_update +- Merge pull request [#4](https://github.com/OpenIMSDK/Open-IM-Server/issues/4) from wujingke/patch-1 + + +[Unreleased]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.0...HEAD diff --git a/CHANGELOG/CHANGELOG-2.0.0.md b/CHANGELOG/CHANGELOG-2.0.0.md new file mode 100644 index 000000000..18f2eeb48 --- /dev/null +++ b/CHANGELOG/CHANGELOG-2.0.0.md @@ -0,0 +1,169 @@ + +## [Unreleased] + +### Bug Fixes +- Fix make Help +- fix the cicd error of the robot +- binary name modification +- optimization details +- remove test file +- rename open_im to openim +- details optimize +- fix cmd filename +- fix scripts +- script -> scripts +- script -> scripts +- env file +- remote +- comment invite trigger actions +- docker compose +- version ploblem +- fix branch git hooks +- Improved initial docker scripts +- Update CONTRIBUTING.md +- Update README.md file +- docker compose +- comment invite trigger actions +- gosec help us audit the Go code +- changelog error +- cover adjustment in some actions +- make up for small problems with pre-push D +- optimize codeql ananlysis workflows +- file name quest +- binary name modification +- copyright owner +- readme styles +- markfown lint test cicd actions deplpy +- designing and supplementing gnore +- robot name +- readme file +- fixed some connection display issues +- Update README image address +- directory name +- **gnore:** remove .github + +### Code Refactoring +- add openim-sdk-core submodule + +### Features +- add actions form org +- add deployments readme file +- add all command +- sets the absolute path of the script +- add cyan +- add cyan +- add cyan +- add cyan +- add cyan +- add COLOR_SUFFIX=033[0m # End all colors and special effects +- fix scripts +- add chmod +- add all page +- add make file +- add scripts +- add code comment +- add make +- add actions form org +- add license +- add makefile bug +- add more feature in scripts +- add git hook sign +- add codeowners file +- add release +- add common scripts +- add copyright +- add scripts lib +- use robot to migrate code +- add feature +- add code ql system +- fix openim ci +- fix code +- set scripts +- set scripts +- add chmod script +- ver3 branch +- add scripts chmod +- add scripts +- docker compose logs unified directory +- docker compose logs unified directory +- add copyright permission +- optimize scripts and makefiles +- optimize scripts and makefiles +- set up golang ci +- refactoring main +- migrate the original main branch code +- add code comment +- complete the basic build +- adding actions ([#461](https://github.com/OpenIMSDK/Open-IM-Server/issues/461)) +- project actions +- service architecture +- service architecture +- Automatic PR title AI fix +- vscode gitignore +- sync file to * +- sync file to * +- sync core branch +- copy license +- make multiarch +- sync robot +- tools optimize +- resolving initialization +- exchange tokens for the robot +- provide robot for use +- support for closing issues +- comment invite trigger actions +- add codecov context +- super log dergen +- set the structure of the rebase +- adjust the install go-gitlint role +- complete the githook design +- designing and supplementing gnore +- add recvID/groupID for msg modification callback ([#351](https://github.com/OpenIMSDK/Open-IM-Server/issues/351)) +- extensions ignore files +- a feat of Makefile large-scale engineering design ([#370](https://github.com/OpenIMSDK/Open-IM-Server/issues/370)) +- zh-reamde +- ADOPTERS +- readme add shields +- readme add shields +- super dependencies makefile help +- Improve tools information +- Improve copyright information +- Improve copyright information +- Improve help information +- good frist issue readme +- good frist issue readme +- good frist issue readme +- slack +- **githook:** make sure to trigger githook when you make +- **main:** add stale labels +- **main:** dockerfile fix +- **main:** update readme in v3 +- **make:** define the common implementation of the base +- **make:** define common githook +- **make:** add file name pre-commit for githook +- **make:** determine the base constant setting for common + + + +## v2.0.0 - 2022-02-23 +### Bug Fixes +- Startup error ([#11](https://github.com/OpenIMSDK/Open-IM-Server/issues/11)) + +### Features +- test utils ([#26](https://github.com/OpenIMSDK/Open-IM-Server/issues/26)) +- optimise get server ip ([#20](https://github.com/OpenIMSDK/Open-IM-Server/issues/20)) + +### Reverts +- friend modify +- update + +### Pull Requests +- Merge pull request [#131](https://github.com/OpenIMSDK/Open-IM-Server/issues/131) from OpenIMSDK/cms-dev +- Merge pull request [#12](https://github.com/OpenIMSDK/Open-IM-Server/issues/12) from njulk/main +- Merge pull request [#9](https://github.com/OpenIMSDK/Open-IM-Server/issues/9) from xmcy0011/dev +- Merge pull request [#7](https://github.com/OpenIMSDK/Open-IM-Server/issues/7) from memory-qianxiao/docker-compose_update +- Merge pull request [#6](https://github.com/OpenIMSDK/Open-IM-Server/issues/6) from Bloomingg/int +- Merge pull request [#4](https://github.com/OpenIMSDK/Open-IM-Server/issues/4) from wujingke/patch-1 + + +[Unreleased]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.0...HEAD diff --git a/CHANGELOG/CHANGELOG-3.0.0.md b/CHANGELOG/CHANGELOG-3.0.0.md new file mode 100644 index 000000000..c826243cc --- /dev/null +++ b/CHANGELOG/CHANGELOG-3.0.0.md @@ -0,0 +1,153 @@ + +## [Unreleased] + + + +## v3.0.0 - 2023-07-10 +### Bug Fixes +- Fix make Help +- fix the cicd error of the robot +- binary name modification +- optimization details +- remove test file +- rename open_im to openim +- details optimize +- fix cmd filename +- fix scripts +- script -> scripts +- script -> scripts +- env file +- remote +- comment invite trigger actions +- docker compose +- version ploblem +- fix branch git hooks +- Improved initial docker scripts +- Update CONTRIBUTING.md +- Update README.md file +- docker compose +- comment invite trigger actions +- gosec help us audit the Go code +- changelog error +- cover adjustment in some actions +- make up for small problems with pre-push D +- optimize codeql ananlysis workflows +- file name quest +- binary name modification +- copyright owner +- readme styles +- markfown lint test cicd actions deplpy +- designing and supplementing gnore +- robot name +- readme file +- fixed some connection display issues +- Update README image address +- directory name +- **gnore:** remove .github + +### Code Refactoring +- add openim-sdk-core submodule + +### Features +- add actions form org +- add deployments readme file +- add all command +- sets the absolute path of the script +- add cyan +- add cyan +- add cyan +- add cyan +- add cyan +- add COLOR_SUFFIX=033[0m # End all colors and special effects +- fix scripts +- add chmod +- add all page +- add make file +- add scripts +- add code comment +- add make +- add actions form org +- add license +- add makefile bug +- add more feature in scripts +- add git hook sign +- add codeowners file +- add release +- add common scripts +- add copyright +- add scripts lib +- use robot to migrate code +- add feature +- add code ql system +- fix openim ci +- fix code +- set scripts +- set scripts +- add chmod script +- ver3 branch +- add scripts chmod +- add scripts +- docker compose logs unified directory +- docker compose logs unified directory +- add copyright permission +- optimize scripts and makefiles +- optimize scripts and makefiles +- set up golang ci +- refactoring main +- migrate the original main branch code +- add code comment +- complete the basic build +- adding actions ([#461](https://github.com/OpenIMSDK/Open-IM-Server/issues/461)) +- project actions +- service architecture +- service architecture +- Automatic PR title AI fix +- vscode gitignore +- sync file to * +- sync file to * +- sync core branch +- copy license +- make multiarch +- sync robot +- tools optimize +- resolving initialization +- exchange tokens for the robot +- provide robot for use +- support for closing issues +- comment invite trigger actions +- add codecov context +- super log dergen +- set the structure of the rebase +- adjust the install go-gitlint role +- complete the githook design +- designing and supplementing gnore +- add recvID/groupID for msg modification callback ([#351](https://github.com/OpenIMSDK/Open-IM-Server/issues/351)) +- extensions ignore files +- a feat of Makefile large-scale engineering design ([#370](https://github.com/OpenIMSDK/Open-IM-Server/issues/370)) +- zh-reamde +- ADOPTERS +- readme add shields +- readme add shields +- super dependencies makefile help +- Improve tools information +- Improve copyright information +- Improve copyright information +- Improve help information +- good frist issue readme +- good frist issue readme +- good frist issue readme +- slack +- **githook:** make sure to trigger githook when you make +- **main:** add stale labels +- **main:** dockerfile fix +- **main:** update readme in v3 +- **make:** define the common implementation of the base +- **make:** define common githook +- **make:** add file name pre-commit for githook +- **make:** determine the base constant setting for common + +### Reverts +- update etcd to v3.5.2 ([#206](https://github.com/OpenIMSDK/Open-IM-Server/issues/206)) + + +[Unreleased]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v3.0.0...HEAD diff --git a/CHANGELOG/CHANGELOG.md b/CHANGELOG/CHANGELOG.md index 0f9b12831..79c05c44a 100644 --- a/CHANGELOG/CHANGELOG.md +++ b/CHANGELOG/CHANGELOG.md @@ -1,3 +1,19 @@ # Changelog -All notable changes to this project will be documented in this file. \ No newline at end of file +All notable changes to this project will be documented in this file. + + +## command + +```bash +git-chglog --tag-filter-pattern 'v3.0.0' -o CHANGELOG-3.0.1.md +``` + + +## create next tag + +```bash +git-chglog --next-tag 2.0.0 -o CHANGELOG.md +git commit -am "release 2.0.0" +git tag 2.0.0 +``` From a5191bac950b42438c367bf7795495c1e012eb9d Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Mon, 10 Jul 2023 11:58:11 +0800 Subject: [PATCH 48/73] feat: add link file Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83b694704..c72a9ced0 120000 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1 @@ -CHANGELOG.md \ No newline at end of file +CHANGELOG/CHANGELOG.md \ No newline at end of file From 5b05152f9a24284e4c843a90269efcf9786a32ba Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Mon, 10 Jul 2023 14:56:52 +0800 Subject: [PATCH 49/73] feat: optimize scripts Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- CHANGELOG/.chglog/config.yml | 1 + scripts/build_all_service.sh | 13 ++++++++++--- scripts/msg_gateway_start.sh | 14 +++++++------- scripts/msg_transfer_start.sh | 14 +++++++------- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/CHANGELOG/.chglog/config.yml b/CHANGELOG/.chglog/config.yml index 1eed7863b..fe6214ab9 100644 --- a/CHANGELOG/.chglog/config.yml +++ b/CHANGELOG/.chglog/config.yml @@ -1,3 +1,4 @@ +bin: git style: github template: CHANGELOG.tpl.md info: diff --git a/scripts/build_all_service.sh b/scripts/build_all_service.sh index fd196e48b..5aea8b1d9 100755 --- a/scripts/build_all_service.sh +++ b/scripts/build_all_service.sh @@ -13,10 +13,17 @@ # See the License for the specific language governing permissions and # limitations under the License. +#Include shell font styles and some basic information +SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. + +cd $SCRIPTS_ROOT + +#Include shell font styles and some basic information +source $SCRIPTS_ROOT/style_info.sh +source $SCRIPTS_ROOT/path_info.sh +source $SCRIPTS_ROOT/function.sh -source ./style_info.sh -source ./path_info.sh -source ./function.sh echo -e "" echo -e "${BACKGROUND_BLUE}===============> Building all using make build binary files ${COLOR_SUFFIX}" diff --git a/scripts/msg_gateway_start.sh b/scripts/msg_gateway_start.sh index 164d993ae..b3b71d220 100755 --- a/scripts/msg_gateway_start.sh +++ b/scripts/msg_gateway_start.sh @@ -51,9 +51,9 @@ if [ ${#rpc_ports[@]} -ne ${#ws_ports[@]} ]; then fi #Check if the service exists #If it is exists,kill this process -check=$(ps aux | grep -w ./${openim-msggateway} | grep -v grep | wc -l) +check=$(ps aux | grep -w ./${openim_msggateway} | grep -v grep | wc -l) if [ $check -ge 1 ]; then - oldPid=$(ps aux | grep -w ./${openim-msggateway} | grep -v grep | awk '{print $2}') + oldPid=$(ps aux | grep -w ./${openim_msggateway} | grep -v grep | awk '{print $2}') kill -9 ${oldPid} fi #Waiting port recycling @@ -61,23 +61,23 @@ sleep 1 cd ${msg_gateway_binary_root} for ((i = 0; i < ${#ws_ports[@]}; i++)); do echo "==========================start msg_gateway server===========================">>$OPENIM_ROOT/logs/openIM.log - nohup ./${openim-msggateway} --port ${rpc_ports[$i]} --ws_port ${ws_ports[$i]} --prometheus_port ${prome_ports[$i]} >>$OPENIM_ROOT/logs/openIM.log 2>&1 & + nohup ./${openim_msggateway} --port ${rpc_ports[$i]} --ws_port ${ws_ports[$i]} --prometheus_port ${prome_ports[$i]} >>$OPENIM_ROOT/logs/openIM.log 2>&1 & done #Check launched service process sleep 3 -check=$(ps aux | grep -w ./${openim-msggateway} | grep -v grep | wc -l) +check=$(ps aux | grep -w ./${openim_msggateway} | grep -v grep | wc -l) allPorts="" if [ $check -ge 1 ]; then - allNewPid=$(ps aux | grep -w ./${openim-msggateway} | grep -v grep | awk '{print $2}') + allNewPid=$(ps aux | grep -w ./${openim_msggateway} | grep -v grep | awk '{print $2}') for i in $allNewPid; do ports=$(netstat -netulp | grep -w ${i} | awk '{print $4}' | awk -F '[:]' '{print $NF}') allPorts=${allPorts}"$ports " done echo -e ${SKY_BLUE_PREFIX}"SERVICE START SUCCESS"${COLOR_SUFFIX} - echo -e ${SKY_BLUE_PREFIX}"SERVICE_NAME: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${openim-msggateway}${COLOR_SUFFIX} + echo -e ${SKY_BLUE_PREFIX}"SERVICE_NAME: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${openim_msggateway}${COLOR_SUFFIX} echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${allNewPid}${COLOR_SUFFIX} echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${allPorts}${COLOR_SUFFIX} else - echo -e ${BACKGROUND_GREEN}${openim-msggateway}${COLOR_SUFFIX}${RED_PREFIX}"\n SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} + echo -e ${BACKGROUND_GREEN}${openim_msggateway}${COLOR_SUFFIX}${RED_PREFIX}"\n SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} fi diff --git a/scripts/msg_transfer_start.sh b/scripts/msg_transfer_start.sh index 741485356..541c82910 100755 --- a/scripts/msg_transfer_start.sh +++ b/scripts/msg_transfer_start.sh @@ -40,10 +40,10 @@ prome_ports=($ports_array) #Check if the service exists #If it is exists,kill this process -check=`ps aux | grep -w ./${openim-msgtransfer} | grep -v grep| wc -l` +check=`ps aux | grep -w ./${openim_msgtransfer} | grep -v grep| wc -l` if [ $check -ge 1 ] then -oldPid=`ps aux | grep -w ./${openim-msgtransfer} | grep -v grep|awk '{print $2}'` +oldPid=`ps aux | grep -w ./${openim_msgtransfer} | grep -v grep|awk '{print $2}'` kill -9 $oldPid fi #Waiting port recycling @@ -52,7 +52,7 @@ sleep 1 cd ${msg_transfer_binary_root} for ((i = 0; i < ${msg_transfer_service_num}; i++)); do prome_port=${prome_ports[$i]} - cmd="nohup ./${openim-msgtransfer}" + cmd="nohup ./${openim_msgtransfer}" if [ $prome_port != "" ]; then cmd="$cmd --prometheus_port $prome_port" fi @@ -61,15 +61,15 @@ for ((i = 0; i < ${msg_transfer_service_num}; i++)); do done #Check launched service process -check=`ps aux | grep -w ./${openim-msgtransfer} | grep -v grep| wc -l` +check=`ps aux | grep -w ./${openim_msgtransfer} | grep -v grep| wc -l` if [ $check -ge 1 ] then -newPid=`ps aux | grep -w ./${openim-msgtransfer} | grep -v grep|awk '{print $2}'` +newPid=`ps aux | grep -w ./${openim_msgtransfer} | grep -v grep|awk '{print $2}'` allPorts="" echo -e ${SKY_BLUE_PREFIX}"SERVICE START SUCCESS "${COLOR_SUFFIX} - echo -e ${SKY_BLUE_PREFIX}"SERVICE_NAME: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${openim-msgtransfer}${COLOR_SUFFIX} + echo -e ${SKY_BLUE_PREFIX}"SERVICE_NAME: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${openim_msgtransfer}${COLOR_SUFFIX} echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${newPid}${COLOR_SUFFIX} echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${allPorts}${COLOR_SUFFIX} else - echo -e ${BACKGROUND_GREEN}${openim-msgtransfer}${COLOR_SUFFIX}${RED_PREFIX}"\n SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} + echo -e ${BACKGROUND_GREEN}${openim_msgtransfer}${COLOR_SUFFIX}${RED_PREFIX}"\n SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX} fi From b4dcbe7202f771c8e35f29b957d667dfc4b4298a Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Mon, 10 Jul 2023 16:30:21 +0800 Subject: [PATCH 50/73] feature: release version logs Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- CHANGELOG/.chglog/CHANGELOG.tpl.md | 6 + CHANGELOG/.chglog/config.yml | 52 +++++- CHANGELOG/CHANGELOG-1.0.0.md | 161 ------------------ CHANGELOG/CHANGELOG-1.0.md | 56 +++++++ CHANGELOG/CHANGELOG-2.0.0.md | 169 ------------------- CHANGELOG/CHANGELOG-2.0.md | 121 ++++++++++++++ CHANGELOG/CHANGELOG-2.1.md | 50 ++++++ CHANGELOG/CHANGELOG-2.2.md | 50 ++++++ CHANGELOG/CHANGELOG-2.3.md | 70 ++++++++ CHANGELOG/CHANGELOG-2.9.md | 40 +++++ CHANGELOG/CHANGELOG-3.0.0.md | 153 ----------------- CHANGELOG/CHANGELOG-3.0.md | 255 +++++++++++++++++++++++++++++ CHANGELOG/CHANGELOG.md | 3 +- 13 files changed, 694 insertions(+), 492 deletions(-) delete mode 100644 CHANGELOG/CHANGELOG-1.0.0.md create mode 100644 CHANGELOG/CHANGELOG-1.0.md delete mode 100644 CHANGELOG/CHANGELOG-2.0.0.md create mode 100644 CHANGELOG/CHANGELOG-2.0.md create mode 100644 CHANGELOG/CHANGELOG-2.1.md create mode 100644 CHANGELOG/CHANGELOG-2.2.md create mode 100644 CHANGELOG/CHANGELOG-2.3.md create mode 100644 CHANGELOG/CHANGELOG-2.9.md delete mode 100644 CHANGELOG/CHANGELOG-3.0.0.md create mode 100644 CHANGELOG/CHANGELOG-3.0.md diff --git a/CHANGELOG/.chglog/CHANGELOG.tpl.md b/CHANGELOG/.chglog/CHANGELOG.tpl.md index 5683d039d..100a29ed8 100644 --- a/CHANGELOG/.chglog/CHANGELOG.tpl.md +++ b/CHANGELOG/.chglog/CHANGELOG.tpl.md @@ -1,3 +1,9 @@ +# Version logging for OpenIM + + + + + {{ if .Versions -}} ## [Unreleased] diff --git a/CHANGELOG/.chglog/config.yml b/CHANGELOG/.chglog/config.yml index fe6214ab9..296ca2eb7 100644 --- a/CHANGELOG/.chglog/config.yml +++ b/CHANGELOG/.chglog/config.yml @@ -5,6 +5,9 @@ info: title: CHANGELOG repository_url: https://github.com/OpenIMSDK/Open-IM-Server options: + tag_filter_pattern: '^v' + sort: "date" + commits: filters: Type: @@ -12,18 +15,53 @@ options: - fix - perf - refactor + - docs + - test + - chore + - ci + - build + sort_by: Scope + commit_groups: + group_by: Type + sort_by: Title + title_order: + - feat + - fix + - perf + - refactor + - docs + - test + - chore + - ci + - build title_maps: feat: Features - fix: Bug Fixes - perf: Performance Improvements - refactor: Code Refactoring + header: - pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$" + pattern: "" pattern_maps: - - Type - - Scope - - Subject + - PropName + + issues: + prefix: + - # + + refs: + actions: + - Closes + - Fixes + + merges: + pattern: "^Merge branch '(\\w+)'$" + pattern_maps: + - Source + + reverts: + pattern: "^Revert \"([\\s\\S]*)\"$" + pattern_maps: + - Header + notes: keywords: - BREAKING CHANGE \ No newline at end of file diff --git a/CHANGELOG/CHANGELOG-1.0.0.md b/CHANGELOG/CHANGELOG-1.0.0.md deleted file mode 100644 index 966bf1895..000000000 --- a/CHANGELOG/CHANGELOG-1.0.0.md +++ /dev/null @@ -1,161 +0,0 @@ - -## [Unreleased] - -### Bug Fixes -- Fix make Help -- fix the cicd error of the robot -- binary name modification -- optimization details -- remove test file -- rename open_im to openim -- details optimize -- fix cmd filename -- fix scripts -- script -> scripts -- script -> scripts -- env file -- remote -- comment invite trigger actions -- docker compose -- version ploblem -- fix branch git hooks -- Improved initial docker scripts -- Update CONTRIBUTING.md -- Update README.md file -- docker compose -- comment invite trigger actions -- gosec help us audit the Go code -- changelog error -- cover adjustment in some actions -- make up for small problems with pre-push D -- optimize codeql ananlysis workflows -- file name quest -- binary name modification -- copyright owner -- readme styles -- markfown lint test cicd actions deplpy -- designing and supplementing gnore -- robot name -- readme file -- fixed some connection display issues -- Update README image address -- directory name -- **gnore:** remove .github - -### Code Refactoring -- add openim-sdk-core submodule - -### Features -- add actions form org -- add deployments readme file -- add all command -- sets the absolute path of the script -- add cyan -- add cyan -- add cyan -- add cyan -- add cyan -- add COLOR_SUFFIX=033[0m # End all colors and special effects -- fix scripts -- add chmod -- add all page -- add make file -- add scripts -- add code comment -- add make -- add actions form org -- add license -- add makefile bug -- add more feature in scripts -- add git hook sign -- add codeowners file -- add release -- add common scripts -- add copyright -- add scripts lib -- use robot to migrate code -- add feature -- add code ql system -- fix openim ci -- fix code -- set scripts -- set scripts -- add chmod script -- ver3 branch -- add scripts chmod -- add scripts -- docker compose logs unified directory -- docker compose logs unified directory -- add copyright permission -- optimize scripts and makefiles -- optimize scripts and makefiles -- set up golang ci -- refactoring main -- migrate the original main branch code -- add code comment -- complete the basic build -- adding actions ([#461](https://github.com/OpenIMSDK/Open-IM-Server/issues/461)) -- project actions -- service architecture -- service architecture -- Automatic PR title AI fix -- vscode gitignore -- sync file to * -- sync file to * -- sync core branch -- copy license -- make multiarch -- sync robot -- tools optimize -- resolving initialization -- exchange tokens for the robot -- provide robot for use -- support for closing issues -- comment invite trigger actions -- add codecov context -- super log dergen -- set the structure of the rebase -- adjust the install go-gitlint role -- complete the githook design -- designing and supplementing gnore -- add recvID/groupID for msg modification callback ([#351](https://github.com/OpenIMSDK/Open-IM-Server/issues/351)) -- extensions ignore files -- a feat of Makefile large-scale engineering design ([#370](https://github.com/OpenIMSDK/Open-IM-Server/issues/370)) -- zh-reamde -- ADOPTERS -- readme add shields -- readme add shields -- super dependencies makefile help -- Improve tools information -- Improve copyright information -- Improve copyright information -- Improve help information -- good frist issue readme -- good frist issue readme -- good frist issue readme -- slack -- **githook:** make sure to trigger githook when you make -- **main:** add stale labels -- **main:** dockerfile fix -- **main:** update readme in v3 -- **make:** define the common implementation of the base -- **make:** define common githook -- **make:** add file name pre-commit for githook -- **make:** determine the base constant setting for common - - - -## v1.0.0 - 2021-10-28 -### Features -- optimise get server ip ([#20](https://github.com/OpenIMSDK/Open-IM-Server/issues/20)) - -### Reverts -- friend modify -- update - -### Pull Requests -- Merge pull request [#7](https://github.com/OpenIMSDK/Open-IM-Server/issues/7) from memory-qianxiao/docker-compose_update -- Merge pull request [#4](https://github.com/OpenIMSDK/Open-IM-Server/issues/4) from wujingke/patch-1 - - -[Unreleased]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.0...HEAD diff --git a/CHANGELOG/CHANGELOG-1.0.md b/CHANGELOG/CHANGELOG-1.0.md new file mode 100644 index 000000000..04980fbd4 --- /dev/null +++ b/CHANGELOG/CHANGELOG-1.0.md @@ -0,0 +1,56 @@ +# Version logging for OpenIM + +> **Note**: +> Deprecated version logging for OpenIM, please refer to [CHANGELOG.md](../CHANGELOG.md) + + + +- [Version logging for OpenIM](#version-logging-for-openim) + - [Unreleased](#unreleased) + - [v1.0.7 - 2021-12-17](#v107---2021-12-17) + - [v1.0.6 - 2021-12-10](#v106---2021-12-10) + - [v1.0.5 - 2021-12-03](#v105---2021-12-03) + - [v1.0.4 - 2021-11-25](#v104---2021-11-25) + - [v1.0.3 - 2021-11-12](#v103---2021-11-12) + - [v1.0.1 - 2021-11-04](#v101---2021-11-04) + - [v1.0.0 - 2021-10-28](#v100---2021-10-28) + - [Reverts](#reverts) + + + + +## [Unreleased] + + + +## [v1.0.7] - 2021-12-17 + + +## [v1.0.6] - 2021-12-10 + + +## [v1.0.5] - 2021-12-03 + + +## [v1.0.4] - 2021-11-25 + + +## [v1.0.3] - 2021-11-12 + + +## [v1.0.1] - 2021-11-04 + + +## v1.0.0 - 2021-10-28 +### Reverts +- friend modify +- update + + +[Unreleased]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.7...HEAD +[v1.0.7]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.6...v1.0.7 +[v1.0.6]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.5...v1.0.6 +[v1.0.5]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.4...v1.0.5 +[v1.0.4]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.3...v1.0.4 +[v1.0.3]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.1...v1.0.3 +[v1.0.1]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.0...v1.0.1 diff --git a/CHANGELOG/CHANGELOG-2.0.0.md b/CHANGELOG/CHANGELOG-2.0.0.md deleted file mode 100644 index 18f2eeb48..000000000 --- a/CHANGELOG/CHANGELOG-2.0.0.md +++ /dev/null @@ -1,169 +0,0 @@ - -## [Unreleased] - -### Bug Fixes -- Fix make Help -- fix the cicd error of the robot -- binary name modification -- optimization details -- remove test file -- rename open_im to openim -- details optimize -- fix cmd filename -- fix scripts -- script -> scripts -- script -> scripts -- env file -- remote -- comment invite trigger actions -- docker compose -- version ploblem -- fix branch git hooks -- Improved initial docker scripts -- Update CONTRIBUTING.md -- Update README.md file -- docker compose -- comment invite trigger actions -- gosec help us audit the Go code -- changelog error -- cover adjustment in some actions -- make up for small problems with pre-push D -- optimize codeql ananlysis workflows -- file name quest -- binary name modification -- copyright owner -- readme styles -- markfown lint test cicd actions deplpy -- designing and supplementing gnore -- robot name -- readme file -- fixed some connection display issues -- Update README image address -- directory name -- **gnore:** remove .github - -### Code Refactoring -- add openim-sdk-core submodule - -### Features -- add actions form org -- add deployments readme file -- add all command -- sets the absolute path of the script -- add cyan -- add cyan -- add cyan -- add cyan -- add cyan -- add COLOR_SUFFIX=033[0m # End all colors and special effects -- fix scripts -- add chmod -- add all page -- add make file -- add scripts -- add code comment -- add make -- add actions form org -- add license -- add makefile bug -- add more feature in scripts -- add git hook sign -- add codeowners file -- add release -- add common scripts -- add copyright -- add scripts lib -- use robot to migrate code -- add feature -- add code ql system -- fix openim ci -- fix code -- set scripts -- set scripts -- add chmod script -- ver3 branch -- add scripts chmod -- add scripts -- docker compose logs unified directory -- docker compose logs unified directory -- add copyright permission -- optimize scripts and makefiles -- optimize scripts and makefiles -- set up golang ci -- refactoring main -- migrate the original main branch code -- add code comment -- complete the basic build -- adding actions ([#461](https://github.com/OpenIMSDK/Open-IM-Server/issues/461)) -- project actions -- service architecture -- service architecture -- Automatic PR title AI fix -- vscode gitignore -- sync file to * -- sync file to * -- sync core branch -- copy license -- make multiarch -- sync robot -- tools optimize -- resolving initialization -- exchange tokens for the robot -- provide robot for use -- support for closing issues -- comment invite trigger actions -- add codecov context -- super log dergen -- set the structure of the rebase -- adjust the install go-gitlint role -- complete the githook design -- designing and supplementing gnore -- add recvID/groupID for msg modification callback ([#351](https://github.com/OpenIMSDK/Open-IM-Server/issues/351)) -- extensions ignore files -- a feat of Makefile large-scale engineering design ([#370](https://github.com/OpenIMSDK/Open-IM-Server/issues/370)) -- zh-reamde -- ADOPTERS -- readme add shields -- readme add shields -- super dependencies makefile help -- Improve tools information -- Improve copyright information -- Improve copyright information -- Improve help information -- good frist issue readme -- good frist issue readme -- good frist issue readme -- slack -- **githook:** make sure to trigger githook when you make -- **main:** add stale labels -- **main:** dockerfile fix -- **main:** update readme in v3 -- **make:** define the common implementation of the base -- **make:** define common githook -- **make:** add file name pre-commit for githook -- **make:** determine the base constant setting for common - - - -## v2.0.0 - 2022-02-23 -### Bug Fixes -- Startup error ([#11](https://github.com/OpenIMSDK/Open-IM-Server/issues/11)) - -### Features -- test utils ([#26](https://github.com/OpenIMSDK/Open-IM-Server/issues/26)) -- optimise get server ip ([#20](https://github.com/OpenIMSDK/Open-IM-Server/issues/20)) - -### Reverts -- friend modify -- update - -### Pull Requests -- Merge pull request [#131](https://github.com/OpenIMSDK/Open-IM-Server/issues/131) from OpenIMSDK/cms-dev -- Merge pull request [#12](https://github.com/OpenIMSDK/Open-IM-Server/issues/12) from njulk/main -- Merge pull request [#9](https://github.com/OpenIMSDK/Open-IM-Server/issues/9) from xmcy0011/dev -- Merge pull request [#7](https://github.com/OpenIMSDK/Open-IM-Server/issues/7) from memory-qianxiao/docker-compose_update -- Merge pull request [#6](https://github.com/OpenIMSDK/Open-IM-Server/issues/6) from Bloomingg/int -- Merge pull request [#4](https://github.com/OpenIMSDK/Open-IM-Server/issues/4) from wujingke/patch-1 - - -[Unreleased]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.0...HEAD diff --git a/CHANGELOG/CHANGELOG-2.0.md b/CHANGELOG/CHANGELOG-2.0.md new file mode 100644 index 000000000..0340bd168 --- /dev/null +++ b/CHANGELOG/CHANGELOG-2.0.md @@ -0,0 +1,121 @@ +# Version logging for OpenIM:v2.0 + +> **Note**: +> Deprecated version logging for OpenIM, please refer to [CHANGELOG.md](../CHANGELOG.md) + + + +- [Version logging for OpenIM:v2.0](#version-logging-for-openimv20) + - [\[Unreleased\]](#unreleased) + - [\[v2.0.10\] - 2022-05-13](#v2010---2022-05-13) + - [\[v2.0.9\] - 2022-04-29](#v209---2022-04-29) + - [Reverts](#reverts) + - [Pull Requests](#pull-requests) + - [\[v2.0.8\] - 2022-04-24](#v208---2022-04-24) + - [Pull Requests](#pull-requests-1) + - [\[v2.0.7\] - 2022-04-08](#v207---2022-04-08) + - [Pull Requests](#pull-requests-2) + - [\[v2.0.6\] - 2022-04-01](#v206---2022-04-01) + - [Pull Requests](#pull-requests-3) + - [\[v2.0.5\] - 2022-03-24](#v205---2022-03-24) + - [\[v2.04\] - 2022-03-18](#v204---2022-03-18) + - [\[v2.0.3\] - 2022-03-11](#v203---2022-03-11) + - [\[v2.0.2\] - 2022-03-04](#v202---2022-03-04) + - [Pull Requests](#pull-requests-4) + - [\[v2.0.1\] - 2022-02-25](#v201---2022-02-25) + - [v2.0.0 - 2022-02-23](#v200---2022-02-23) + - [Reverts](#reverts-1) + + + + + + +## [Unreleased] + + + + +## [v2.0.10] - 2022-05-13 + + + +## [v2.0.9] - 2022-04-29 + +### Reverts + +- update etcd to v3.5.2 ([#206](https://github.com/OpenIMSDK/Open-IM-Server/issues/206)) + +### Pull Requests + +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' + + + + +## [v2.0.8] - 2022-04-24 + +### Pull Requests + +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' + + + + +## [v2.0.7] - 2022-04-08 + +### Pull Requests + +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' + + + + +## [v2.0.6] - 2022-04-01 + +### Pull Requests + +- Merge branch 'tuoyun' + + + + +## [v2.0.5] - 2022-03-24 + + + +## [v2.04] - 2022-03-18 + + + +## [v2.0.3] - 2022-03-11 + + + +## [v2.0.2] - 2022-03-04 + +### Pull Requests + +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' + + + + +## [v2.0.1] - 2022-02-25 + + + +## v2.0.0 - 2022-02-23 + +### Reverts diff --git a/CHANGELOG/CHANGELOG-2.1.md b/CHANGELOG/CHANGELOG-2.1.md new file mode 100644 index 000000000..32effd64d --- /dev/null +++ b/CHANGELOG/CHANGELOG-2.1.md @@ -0,0 +1,50 @@ +# Version logging for OpenIM:v2.1 + +> **Note**: +> Deprecated version logging for OpenIM, please refer to [CHANGELOG.md](../CHANGELOG.md) + + + +- [Version logging for OpenIM:v2.1](#version-logging-for-openimv21) + - [Unreleased](#unreleased) + - [v2.1.0 - 2022-06-17](#v210---2022-06-17) + - [Reverts](#reverts) + - [Pull Requests](#pull-requests) + + + + + +## [Unreleased] + + + +## v2.1.0 - 2022-06-17 +### Reverts +- update etcd to v3.5.2 ([#206](https://github.com/OpenIMSDK/Open-IM-Server/issues/206)) +- friend modify +- update + +### Pull Requests +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' + + +[Unreleased]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.1.0...HEAD diff --git a/CHANGELOG/CHANGELOG-2.2.md b/CHANGELOG/CHANGELOG-2.2.md new file mode 100644 index 000000000..e00204316 --- /dev/null +++ b/CHANGELOG/CHANGELOG-2.2.md @@ -0,0 +1,50 @@ +# Version logging for OpenIM:v2.2 + +> **Note**: +> Deprecated version logging for OpenIM, please refer to [CHANGELOG.md](../CHANGELOG.md) + + + +- [Version logging for OpenIM:v2.2](#version-logging-for-openimv22) + - [Unreleased](#unreleased) + - [v2.2.0 - 2022-07-01](#v220---2022-07-01) + - [Reverts](#reverts) + - [Pull Requests](#pull-requests) + + + + + +## [Unreleased] + + + +## v2.2.0 - 2022-07-01 +### Reverts +- update etcd to v3.5.2 ([#206](https://github.com/OpenIMSDK/Open-IM-Server/issues/206)) +- friend modify +- update + +### Pull Requests +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' + + +[Unreleased]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.2.0...HEAD diff --git a/CHANGELOG/CHANGELOG-2.3.md b/CHANGELOG/CHANGELOG-2.3.md new file mode 100644 index 000000000..c33b81a9a --- /dev/null +++ b/CHANGELOG/CHANGELOG-2.3.md @@ -0,0 +1,70 @@ +# Version logging for OpenIM:v2.3 + +> **Note**: +> Deprecated version logging for OpenIM, please refer to [CHANGELOG.md](../CHANGELOG.md) + + + +- [Version logging for OpenIM:v2.3](#version-logging-for-openimv23) + - [Unreleased](#unreleased) + - [v2.3.3 - 2022-09-18](#v233---2022-09-18) + - [v2.3.2 - 2022-09-09](#v232---2022-09-09) + - [v2.3.0-rc2 - 2022-07-29](#v230-rc2---2022-07-29) + - [v2.3.0-rc1 - 2022-07-25](#v230-rc1---2022-07-25) + - [v2.3.0-rc0 - 2022-07-15](#v230-rc0---2022-07-15) + - [Reverts](#reverts) + - [Pull Requests](#pull-requests) + + + + + +## [Unreleased] + + + +## [v2.3.3] - 2022-09-18 + + +## [v2.3.2] - 2022-09-09 + + +## [v2.3.0-rc2] - 2022-07-29 + + +## [v2.3.0-rc1] - 2022-07-25 + + +## v2.3.0-rc0 - 2022-07-15 +### Reverts +- update etcd to v3.5.2 ([#206](https://github.com/OpenIMSDK/Open-IM-Server/issues/206)) +- friend modify +- update + +### Pull Requests +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' + + +[Unreleased]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.3.3...HEAD +[v2.3.3]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.3.2...v2.3.3 +[v2.3.2]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.3.0-rc2...v2.3.2 +[v2.3.0-rc2]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.3.0-rc1...v2.3.0-rc2 +[v2.3.0-rc1]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.3.0-rc0...v2.3.0-rc1 diff --git a/CHANGELOG/CHANGELOG-2.9.md b/CHANGELOG/CHANGELOG-2.9.md new file mode 100644 index 000000000..174286f0d --- /dev/null +++ b/CHANGELOG/CHANGELOG-2.9.md @@ -0,0 +1,40 @@ +# Version logging for OpenIM + + + + + + +## [Unreleased] + + + +## [v2.9.0+1.839643f] - 2023-07-07 + + +## [v2.9.0+2.35f07fe] - 2023-07-06 + + +## [v2.9.0+1.b5072b1] - 2023-07-05 + + +## [v2.9.0+3.2667a3a] - 2023-07-05 + + +## [v2.9.0+7.04818ca] - 2023-07-05 + + +## v2.9.0 - 2023-07-04 +### Reverts +- update etcd to v3.5.2 ([#206](https://github.com/OpenIMSDK/Open-IM-Server/issues/206)) + +### Pull Requests +- Merge branch 'tuoyun' + + +[Unreleased]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.9.0+1.839643f...HEAD +[v2.9.0+1.839643f]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.9.0+2.35f07fe...v2.9.0+1.839643f +[v2.9.0+2.35f07fe]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.9.0+1.b5072b1...v2.9.0+2.35f07fe +[v2.9.0+1.b5072b1]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.9.0+3.2667a3a...v2.9.0+1.b5072b1 +[v2.9.0+3.2667a3a]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.9.0+7.04818ca...v2.9.0+3.2667a3a +[v2.9.0+7.04818ca]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.9.0...v2.9.0+7.04818ca diff --git a/CHANGELOG/CHANGELOG-3.0.0.md b/CHANGELOG/CHANGELOG-3.0.0.md deleted file mode 100644 index c826243cc..000000000 --- a/CHANGELOG/CHANGELOG-3.0.0.md +++ /dev/null @@ -1,153 +0,0 @@ - -## [Unreleased] - - - -## v3.0.0 - 2023-07-10 -### Bug Fixes -- Fix make Help -- fix the cicd error of the robot -- binary name modification -- optimization details -- remove test file -- rename open_im to openim -- details optimize -- fix cmd filename -- fix scripts -- script -> scripts -- script -> scripts -- env file -- remote -- comment invite trigger actions -- docker compose -- version ploblem -- fix branch git hooks -- Improved initial docker scripts -- Update CONTRIBUTING.md -- Update README.md file -- docker compose -- comment invite trigger actions -- gosec help us audit the Go code -- changelog error -- cover adjustment in some actions -- make up for small problems with pre-push D -- optimize codeql ananlysis workflows -- file name quest -- binary name modification -- copyright owner -- readme styles -- markfown lint test cicd actions deplpy -- designing and supplementing gnore -- robot name -- readme file -- fixed some connection display issues -- Update README image address -- directory name -- **gnore:** remove .github - -### Code Refactoring -- add openim-sdk-core submodule - -### Features -- add actions form org -- add deployments readme file -- add all command -- sets the absolute path of the script -- add cyan -- add cyan -- add cyan -- add cyan -- add cyan -- add COLOR_SUFFIX=033[0m # End all colors and special effects -- fix scripts -- add chmod -- add all page -- add make file -- add scripts -- add code comment -- add make -- add actions form org -- add license -- add makefile bug -- add more feature in scripts -- add git hook sign -- add codeowners file -- add release -- add common scripts -- add copyright -- add scripts lib -- use robot to migrate code -- add feature -- add code ql system -- fix openim ci -- fix code -- set scripts -- set scripts -- add chmod script -- ver3 branch -- add scripts chmod -- add scripts -- docker compose logs unified directory -- docker compose logs unified directory -- add copyright permission -- optimize scripts and makefiles -- optimize scripts and makefiles -- set up golang ci -- refactoring main -- migrate the original main branch code -- add code comment -- complete the basic build -- adding actions ([#461](https://github.com/OpenIMSDK/Open-IM-Server/issues/461)) -- project actions -- service architecture -- service architecture -- Automatic PR title AI fix -- vscode gitignore -- sync file to * -- sync file to * -- sync core branch -- copy license -- make multiarch -- sync robot -- tools optimize -- resolving initialization -- exchange tokens for the robot -- provide robot for use -- support for closing issues -- comment invite trigger actions -- add codecov context -- super log dergen -- set the structure of the rebase -- adjust the install go-gitlint role -- complete the githook design -- designing and supplementing gnore -- add recvID/groupID for msg modification callback ([#351](https://github.com/OpenIMSDK/Open-IM-Server/issues/351)) -- extensions ignore files -- a feat of Makefile large-scale engineering design ([#370](https://github.com/OpenIMSDK/Open-IM-Server/issues/370)) -- zh-reamde -- ADOPTERS -- readme add shields -- readme add shields -- super dependencies makefile help -- Improve tools information -- Improve copyright information -- Improve copyright information -- Improve help information -- good frist issue readme -- good frist issue readme -- good frist issue readme -- slack -- **githook:** make sure to trigger githook when you make -- **main:** add stale labels -- **main:** dockerfile fix -- **main:** update readme in v3 -- **make:** define the common implementation of the base -- **make:** define common githook -- **make:** add file name pre-commit for githook -- **make:** determine the base constant setting for common - -### Reverts -- update etcd to v3.5.2 ([#206](https://github.com/OpenIMSDK/Open-IM-Server/issues/206)) - - -[Unreleased]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v3.0.0...HEAD diff --git a/CHANGELOG/CHANGELOG-3.0.md b/CHANGELOG/CHANGELOG-3.0.md new file mode 100644 index 000000000..72cc56bbf --- /dev/null +++ b/CHANGELOG/CHANGELOG-3.0.md @@ -0,0 +1,255 @@ +# Version logging for OpenIM + +**3.0 Major refactoring** + + + +- [Version logging for OpenIM](#version-logging-for-openim) + - [\[v3.0\]](#v30) + - [v3.0.0 - 2023-07-10](#v300---2023-07-10) + - [v2.9.0+1.839643f - 2023-07-07](#v2901839643f---2023-07-07) + - [v2.9.0+2.35f07fe - 2023-07-06](#v290235f07fe---2023-07-06) + - [v2.9.0+1.b5072b1 - 2023-07-05](#v2901b5072b1---2023-07-05) + - [v2.9.0+3.2667a3a - 2023-07-05](#v29032667a3a---2023-07-05) + - [v2.9.0+7.04818ca - 2023-07-05](#v290704818ca---2023-07-05) + - [v2.9.0 - 2023-07-04](#v290---2023-07-04) + - [v0.0.0+1.3714b4f - 2023-07-04](#v00013714b4f---2023-07-04) + - [v0.0.0+635.8b92c90 - 2023-07-04](#v0006358b92c90---2023-07-04) + - [v0.0.0+1.78a6d03 - 2023-07-04](#v000178a6d03---2023-07-04) + - [v0.0.0+2.e057c18 - 2023-07-04](#v0002e057c18---2023-07-04) + - [v0.0.0+630.b55ac4a - 2023-07-04](#v000630b55ac4a---2023-07-04) + - [Reverts](#reverts) + - [Pull Requests](#pull-requests) + - [v2.3.3 - 2022-09-18](#v233---2022-09-18) + - [v2.3.2 - 2022-09-09](#v232---2022-09-09) + - [v2.3.0-rc2 - 2022-07-29](#v230-rc2---2022-07-29) + - [v2.3.0-rc1 - 2022-07-25](#v230-rc1---2022-07-25) + - [v2.3.0-rc0 - 2022-07-15](#v230-rc0---2022-07-15) + - [v2.2.0 - 2022-07-01](#v220---2022-07-01) + - [v2.1.0 - 2022-06-17](#v210---2022-06-17) + - [Pull Requests](#pull-requests-1) + - [v2.0.10 - 2022-05-13](#v2010---2022-05-13) + - [v2.0.9 - 2022-04-29](#v209---2022-04-29) + - [Reverts](#reverts-1) + - [Pull Requests](#pull-requests-2) + - [v2.0.8 - 2022-04-24](#v208---2022-04-24) + - [Pull Requests](#pull-requests-3) + - [v2.0.7 - 2022-04-08](#v207---2022-04-08) + - [Pull Requests](#pull-requests-4) + - [v2.0.6 - 2022-04-01](#v206---2022-04-01) + - [Pull Requests](#pull-requests-5) + - [v2.0.5 - 2022-03-24](#v205---2022-03-24) + - [v2.04 - 2022-03-18](#v204---2022-03-18) + - [v2.0.3 - 2022-03-11](#v203---2022-03-11) + - [v2.0.2 - 2022-03-04](#v202---2022-03-04) + - [Pull Requests](#pull-requests-6) + - [v2.0.1 - 2022-02-25](#v201---2022-02-25) + - [v2.0.0 - 2022-02-23](#v200---2022-02-23) + - [v1.0.7 - 2021-12-17](#v107---2021-12-17) + - [v1.0.6 - 2021-12-10](#v106---2021-12-10) + - [v1.0.5 - 2021-12-03](#v105---2021-12-03) + - [v1.0.4 - 2021-11-25](#v104---2021-11-25) + - [v1.0.3 - 2021-11-12](#v103---2021-11-12) + - [v1.0.1 - 2021-11-04](#v101---2021-11-04) + - [v1.0.0 - 2021-10-28](#v100---2021-10-28) + - [Reverts](#reverts-2) + + + + + +## [v3.0] + + + +## [v3.0.0] - 2023-07-10 + + +## [v2.9.0+1.839643f] - 2023-07-07 + + +## [v2.9.0+2.35f07fe] - 2023-07-06 + + +## [v2.9.0+1.b5072b1] - 2023-07-05 + + +## [v2.9.0+3.2667a3a] - 2023-07-05 + + +## [v2.9.0+7.04818ca] - 2023-07-05 + + +## [v2.9.0] - 2023-07-04 + + +## [v0.0.0+1.3714b4f] - 2023-07-04 + + +## [v0.0.0+635.8b92c90] - 2023-07-04 + + +## [v0.0.0+1.78a6d03] - 2023-07-04 + + +## [v0.0.0+2.e057c18] - 2023-07-04 + + +## [v0.0.0+630.b55ac4a] - 2023-07-04 +### Reverts +- update etcd to v3.5.2 ([#206](https://github.com/OpenIMSDK/Open-IM-Server/issues/206)) + +### Pull Requests +- Merge branch 'tuoyun' + + + +## [v2.3.3] - 2022-09-18 + + +## [v2.3.2] - 2022-09-09 + + +## [v2.3.0-rc2] - 2022-07-29 + + +## [v2.3.0-rc1] - 2022-07-25 + + +## [v2.3.0-rc0] - 2022-07-15 + + +## [v2.2.0] - 2022-07-01 + + +## [v2.1.0] - 2022-06-17 +### Pull Requests +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' + + + +## [v2.0.10] - 2022-05-13 + + +## [v2.0.9] - 2022-04-29 +### Reverts +- update etcd to v3.5.2 ([#206](https://github.com/OpenIMSDK/Open-IM-Server/issues/206)) + +### Pull Requests +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' + + + +## [v2.0.8] - 2022-04-24 +### Pull Requests +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' + + + +## [v2.0.7] - 2022-04-08 +### Pull Requests +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' + + + +## [v2.0.6] - 2022-04-01 +### Pull Requests +- Merge branch 'tuoyun' + + + +## [v2.0.5] - 2022-03-24 + + +## [v2.04] - 2022-03-18 + + +## [v2.0.3] - 2022-03-11 + + +## [v2.0.2] - 2022-03-04 +### Pull Requests +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' +- Merge branch 'tuoyun' + + + +## [v2.0.1] - 2022-02-25 + + +## [v2.0.0] - 2022-02-23 + + +## [v1.0.7] - 2021-12-17 + + +## [v1.0.6] - 2021-12-10 + + +## [v1.0.5] - 2021-12-03 + + +## [v1.0.4] - 2021-11-25 + + +## [v1.0.3] - 2021-11-12 + + +## [v1.0.1] - 2021-11-04 + + +## v1.0.0 - 2021-10-28 +### Reverts +- friend modify +- update + + +[Unreleased]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v3.0.0...HEAD +[v3.0.0]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.9.0+1.839643f...v3.0.0 +[v2.9.0+1.839643f]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.9.0+2.35f07fe...v2.9.0+1.839643f +[v2.9.0+2.35f07fe]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.9.0+1.b5072b1...v2.9.0+2.35f07fe +[v2.9.0+1.b5072b1]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.9.0+3.2667a3a...v2.9.0+1.b5072b1 +[v2.9.0+3.2667a3a]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.9.0+7.04818ca...v2.9.0+3.2667a3a +[v2.9.0+7.04818ca]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.9.0...v2.9.0+7.04818ca +[v2.9.0]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v0.0.0+1.3714b4f...v2.9.0 +[v0.0.0+1.3714b4f]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v0.0.0+635.8b92c90...v0.0.0+1.3714b4f +[v0.0.0+635.8b92c90]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v0.0.0+1.78a6d03...v0.0.0+635.8b92c90 +[v0.0.0+1.78a6d03]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v0.0.0+2.e057c18...v0.0.0+1.78a6d03 +[v0.0.0+2.e057c18]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v0.0.0+630.b55ac4a...v0.0.0+2.e057c18 +[v0.0.0+630.b55ac4a]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.3.3...v0.0.0+630.b55ac4a +[v2.3.3]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.3.2...v2.3.3 +[v2.3.2]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.3.0-rc2...v2.3.2 +[v2.3.0-rc2]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.3.0-rc1...v2.3.0-rc2 +[v2.3.0-rc1]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.3.0-rc0...v2.3.0-rc1 +[v2.3.0-rc0]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.2.0...v2.3.0-rc0 +[v2.2.0]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.1.0...v2.2.0 +[v2.1.0]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.10...v2.1.0 +[v2.0.10]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.9...v2.0.10 +[v2.0.9]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.8...v2.0.9 +[v2.0.8]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.7...v2.0.8 +[v2.0.7]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.6...v2.0.7 +[v2.0.6]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.5...v2.0.6 +[v2.0.5]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.04...v2.0.5 +[v2.04]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.3...v2.04 +[v2.0.3]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.2...v2.0.3 +[v2.0.2]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.1...v2.0.2 +[v2.0.1]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v2.0.0...v2.0.1 +[v2.0.0]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.7...v2.0.0 +[v1.0.7]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.6...v1.0.7 +[v1.0.6]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.5...v1.0.6 +[v1.0.5]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.4...v1.0.5 +[v1.0.4]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.3...v1.0.4 +[v1.0.3]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.1...v1.0.3 +[v1.0.1]: https://github.com/OpenIMSDK/Open-IM-Server/compare/v1.0.0...v1.0.1 diff --git a/CHANGELOG/CHANGELOG.md b/CHANGELOG/CHANGELOG.md index 79c05c44a..67f28d959 100644 --- a/CHANGELOG/CHANGELOG.md +++ b/CHANGELOG/CHANGELOG.md @@ -6,10 +6,9 @@ All notable changes to this project will be documented in this file. ## command ```bash -git-chglog --tag-filter-pattern 'v3.0.0' -o CHANGELOG-3.0.1.md +git-chglog --tag-filter-pattern 'v2.0.*' -o CHANGELOG-2.0.md ``` - ## create next tag ```bash From 29d422b71d4b45a52c1d42995df14bae4e89612d Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Mon, 10 Jul 2023 17:49:28 +0800 Subject: [PATCH 51/73] feat: add scripts and fix Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- scripts/batch_start_all.sh | 6 +++--- scripts/build_all_service.sh | 8 ++++++-- scripts/check_all.sh | 6 +++--- scripts/enterprise/check_all.sh | 6 +++--- scripts/msg_gateway_start.sh | 6 +++--- scripts/msg_transfer_start.sh | 6 +++--- scripts/path_info.sh | 6 +++--- scripts/push_start.sh | 6 +++--- scripts/start_all.sh | 6 +++--- scripts/start_cron.sh | 6 +++--- 10 files changed, 33 insertions(+), 29 deletions(-) diff --git a/scripts/batch_start_all.sh b/scripts/batch_start_all.sh index e291819f4..45ec813c5 100755 --- a/scripts/batch_start_all.sh +++ b/scripts/batch_start_all.sh @@ -27,9 +27,9 @@ source $SCRIPTS_ROOT/function.sh cd $SCRIPTS_ROOT -echo -e "${BACKGROUND_GREEN}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_GREEN}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_GREEN}=======>pwd=$PWD${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>pwd=$PWD${COLOR_SUFFIX}" need_to_start_server_shell=( "start_rpc_service.sh" diff --git a/scripts/build_all_service.sh b/scripts/build_all_service.sh index 5aea8b1d9..ebbc02dfa 100755 --- a/scripts/build_all_service.sh +++ b/scripts/build_all_service.sh @@ -17,13 +17,17 @@ SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. -cd $SCRIPTS_ROOT - #Include shell font styles and some basic information source $SCRIPTS_ROOT/style_info.sh source $SCRIPTS_ROOT/path_info.sh source $SCRIPTS_ROOT/function.sh +echo -e "${YELLOW_PREFIX}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>pwd=$PWD${COLOR_SUFFIX}" + +cd $SCRIPTS_ROOT + echo -e "" echo -e "${BACKGROUND_BLUE}===============> Building all using make build binary files ${COLOR_SUFFIX}" diff --git a/scripts/check_all.sh b/scripts/check_all.sh index db420dd21..c4f5dd490 100755 --- a/scripts/check_all.sh +++ b/scripts/check_all.sh @@ -24,9 +24,9 @@ source $SCRIPTS_ROOT/function.sh cd $SCRIPTS_ROOT -echo -e "${BACKGROUND_GREEN}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_GREEN}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_GREEN}=======>pwd=$PWD${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>pwd=$PWD${COLOR_SUFFIX}" service_port_name=( openImWsPort diff --git a/scripts/enterprise/check_all.sh b/scripts/enterprise/check_all.sh index 7b8a3c5da..973a6a962 100755 --- a/scripts/enterprise/check_all.sh +++ b/scripts/enterprise/check_all.sh @@ -24,9 +24,9 @@ source $SCRIPTS_ROOT/function.sh cd $SCRIPTS_ROOT -echo -e "${BACKGROUND_GREEN}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_GREEN}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_GREEN}=======>pwd=$PWD${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>pwd=$PWD${COLOR_SUFFIX}" service_port_name=( openImChatApiPort diff --git a/scripts/msg_gateway_start.sh b/scripts/msg_gateway_start.sh index b3b71d220..c66bd8460 100755 --- a/scripts/msg_gateway_start.sh +++ b/scripts/msg_gateway_start.sh @@ -24,9 +24,9 @@ source $SCRIPTS_ROOT/function.sh cd $SCRIPTS_ROOT -echo -e "${BACKGROUND_GREEN}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_GREEN}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_GREEN}=======>pwd=$PWD${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>pwd=$PWD${COLOR_SUFFIX}" bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" diff --git a/scripts/msg_transfer_start.sh b/scripts/msg_transfer_start.sh index 541c82910..e61492f34 100755 --- a/scripts/msg_transfer_start.sh +++ b/scripts/msg_transfer_start.sh @@ -24,9 +24,9 @@ source $SCRIPTS_ROOT/function.sh cd $SCRIPTS_ROOT -echo -e "${BACKGROUND_GREEN}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_GREEN}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_GREEN}=======>pwd=$PWD${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>pwd=$PWD${COLOR_SUFFIX}" bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" diff --git a/scripts/path_info.sh b/scripts/path_info.sh index ab6f2b2df..5351a43db 100755 --- a/scripts/path_info.sh +++ b/scripts/path_info.sh @@ -28,16 +28,16 @@ declare -A supported_architectures=( # Check if the architecture and version are supported if [[ -z ${supported_architectures["$version-$architecture"]} ]]; then - echo "================> Unsupported architecture: $architecture or version: $version" + echo -e "${BLUE_PREFIX}================> Unsupported architecture: $architecture or version: $version${COLOR_SUFFIX}" exit 1 fi -echo "================> Architecture: $architecture" +echo -e "${BLUE_PREFIX}================> Architecture: $architecture${COLOR_SUFFIX}" # Set the BIN_DIR based on the architecture and version BIN_DIR=${supported_architectures["$version-$architecture"]} -echo "================> BIN_DIR: $OPENIM_ROOT/$BIN_DIR" +echo -e "${BLUE_PREFIX}================> BIN_DIR: $OPENIM_ROOT/$BIN_DIR${COLOR_SUFFIX}" # Don't put the space between "=" openim_msggateway="openim-msggateway" diff --git a/scripts/push_start.sh b/scripts/push_start.sh index ea353cd56..79d877c79 100755 --- a/scripts/push_start.sh +++ b/scripts/push_start.sh @@ -24,9 +24,9 @@ source $SCRIPTS_ROOT/function.sh cd $SCRIPTS_ROOT -echo -e "${BACKGROUND_GREEN}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_GREEN}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_GREEN}=======>pwd=$PWD${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>pwd=$PWD${COLOR_SUFFIX}" bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" diff --git a/scripts/start_all.sh b/scripts/start_all.sh index 976320dad..4e7db6e3e 100755 --- a/scripts/start_all.sh +++ b/scripts/start_all.sh @@ -27,9 +27,9 @@ source $SCRIPTS_ROOT/function.sh cd $SCRIPTS_ROOT -echo -e "${BACKGROUND_GREEN}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_GREEN}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_GREEN}=======>pwd=$PWD${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>pwd=$PWD${COLOR_SUFFIX}" bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" diff --git a/scripts/start_cron.sh b/scripts/start_cron.sh index bef9ca36e..79b5b9c31 100755 --- a/scripts/start_cron.sh +++ b/scripts/start_cron.sh @@ -24,9 +24,9 @@ source $SCRIPTS_ROOT/function.sh cd $SCRIPTS_ROOT -echo -e "${BACKGROUND_GREEN}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_GREEN}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" -echo -e "${BACKGROUND_GREEN}=======>pwd=$PWD${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>pwd=$PWD${COLOR_SUFFIX}" bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" From ae029ebdfa4a8a8e9f8271eea47b5e54cb869945 Mon Sep 17 00:00:00 2001 From: "Xinwei Xiong(cubxxw-openim)" <3293172751nss@gmail.com> Date: Mon, 10 Jul 2023 18:11:11 +0800 Subject: [PATCH 52/73] feat: add scripts and fix Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- CHANGELOG/CHANGELOG-2.9.md | 12 ++++++++++++ CHANGELOG/CHANGELOG.md | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/CHANGELOG/CHANGELOG-2.9.md b/CHANGELOG/CHANGELOG-2.9.md index 174286f0d..b51212ba8 100644 --- a/CHANGELOG/CHANGELOG-2.9.md +++ b/CHANGELOG/CHANGELOG-2.9.md @@ -2,6 +2,18 @@ +- [Version logging for OpenIM](#version-logging-for-openim) + - [Unreleased](#unreleased) + - [v2.9.0+1.839643f - 2023-07-07](#v2901839643f---2023-07-07) + - [v2.9.0+2.35f07fe - 2023-07-06](#v290235f07fe---2023-07-06) + - [v2.9.0+1.b5072b1 - 2023-07-05](#v2901b5072b1---2023-07-05) + - [v2.9.0+3.2667a3a - 2023-07-05](#v29032667a3a---2023-07-05) + - [v2.9.0+7.04818ca - 2023-07-05](#v290704818ca---2023-07-05) + - [v2.9.0 - 2023-07-04](#v290---2023-07-04) + - [Reverts](#reverts) + - [Pull Requests](#pull-requests) + + diff --git a/CHANGELOG/CHANGELOG.md b/CHANGELOG/CHANGELOG.md index 67f28d959..39ed572db 100644 --- a/CHANGELOG/CHANGELOG.md +++ b/CHANGELOG/CHANGELOG.md @@ -2,6 +2,7 @@ All notable changes to this project will be documented in this file. ++ [https://github.com/OpenIMSDK/Open-IM-Server/releases](https://github.com/OpenIMSDK/Open-IM-Server/releases) ## command @@ -16,3 +17,13 @@ git-chglog --next-tag 2.0.0 -o CHANGELOG.md git commit -am "release 2.0.0" git tag 2.0.0 ``` + +## Release version logs + ++ [OpenIM CHANGELOG-V1.0](CHANGELOG-1.0.md) ++ [OpenIM CHANGELOG-V2.0](CHANGELOG-2.0.md) ++ [OpenIM CHANGELOG-V2.1](CHANGELOG-2.1.md) ++ [OpenIM CHANGELOG-V2.2](CHANGELOG-2.2.md) ++ [OpenIM CHANGELOG-V2.3](CHANGELOG-2.3.md) ++ [OpenIM CHANGELOG-V2.9](CHANGELOG-2.9.md) ++ [OpenIM CHANGELOG-V3.0](CHANGELOG-3.0.md) \ No newline at end of file From 42aa349846c418f5f410552ba3526f9852563a95 Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <86140903+cubxxw@users.noreply.github.com> Date: Tue, 11 Jul 2023 16:18:12 +0800 Subject: [PATCH 53/73] feat: add the changelog (#490) --- docker-compose.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index db3ba4bcc..4b87306f5 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -100,8 +100,8 @@ services: openim_server: - image: openim/openim_server:v3.0.1 - container_name: openim_server + image: openim/open_im_server:v3.0.1 + container_name: open_im_server volumes: - ./logs:/Open-IM-Server/logs - ./config/config.yaml:/Open-IM-Server/config/config.yaml From b5d1adb465c4109ee6760bad3acf3a3d5c39ef5b Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <86140903+cubxxw@users.noreply.github.com> Date: Tue, 11 Jul 2023 16:37:51 +0800 Subject: [PATCH 54/73] fix: docker file path (#492) Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 01923032c..76b2a4c14 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,7 +32,7 @@ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config","/Open-IM-Server/scripts #Copy scripts files and binary files to the blank image COPY --from=build /Open-IM-Server/scripts /Open-IM-Server/scripts -COPY --from=build /Open-IM-Server/bin /Open-IM-Server/bin +COPY --from=build /Open-IM-Server/bin /Open-IM-Server/_output/bin/platforms/linux/amd64/ WORKDIR /Open-IM-Server/scripts From 0350636960c37ad0f3e945cd8661fe82af7f56c8 Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <86140903+cubxxw@users.noreply.github.com> Date: Tue, 11 Jul 2023 21:13:06 +0800 Subject: [PATCH 55/73] fix: docker file path (#495) * fix: docker file path Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> * fix: docker file path Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> * fix: fix scripts Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --------- Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- docker-compose.yaml | 4 ++-- pkg/common/cmd/root.go | 1 + scripts/check_all.sh | 3 ++- scripts/docker_start_all.sh | 4 ++++ scripts/msg_gateway_start.sh | 2 +- scripts/msg_transfer_start.sh | 4 ++-- scripts/path_info.sh | 6 +++++- scripts/start_all.sh | 4 +++- scripts/start_rpc_service.sh | 7 +++++-- scripts/stop_all.sh | 3 ++- 10 files changed, 27 insertions(+), 11 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 4b87306f5..c78e4a63b 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -100,8 +100,8 @@ services: openim_server: - image: openim/open_im_server:v3.0.1 - container_name: open_im_server + image: ghcr.io/openimsdk/openim-server:v3.0.0 + container_name: openim-server volumes: - ./logs:/Open-IM-Server/logs - ./config/config.yaml:/Open-IM-Server/config/config.yaml diff --git a/pkg/common/cmd/root.go b/pkg/common/cmd/root.go index 4a5744033..b15d984a0 100644 --- a/pkg/common/cmd/root.go +++ b/pkg/common/cmd/root.go @@ -84,6 +84,7 @@ func (r *RootCmd) GetPrometheusPortFlag() int { func (r *RootCmd) getConfFromCmdAndInit(cmdLines *cobra.Command) error { configFolderPath, _ := cmdLines.Flags().GetString(constant.FlagConf) + fmt.Println("configFolderPath:", configFolderPath) return config.InitConfig(configFolderPath) } diff --git a/scripts/check_all.sh b/scripts/check_all.sh index c4f5dd490..84dabb1eb 100755 --- a/scripts/check_all.sh +++ b/scripts/check_all.sh @@ -57,10 +57,11 @@ for i in ${service_port_name[*]}; do done #Check launched service process -check=$(ps aux | grep -w ./${openim-msgtransfer} | grep -v grep | wc -l) +check=$(ps aux | grep -w ./${openim_msgtransfer} | grep -v grep | wc -l) if [ $check -eq ${msg_transfer_service_num} ]; then echo -e ${GREEN_PREFIX}"none port has been listening,belongs service is openImMsgTransfer"${COLOR_SUFFIX} else + echo $check ${msg_transfer_service_num} echo -e ${RED_PREFIX}"openImMsgTransfer service does not start normally, num err"${COLOR_SUFFIX} echo -e ${RED_PREFIX}"please check $OPENIM_ROOT/logs/openIM.log "${COLOR_SUFFIX} exit -1 diff --git a/scripts/docker_start_all.sh b/scripts/docker_start_all.sh index 98e5a131c..421900ef4 100755 --- a/scripts/docker_start_all.sh +++ b/scripts/docker_start_all.sh @@ -16,6 +16,10 @@ #fixme This scripts is the total startup scripts #fixme The full name of the shell scripts that needs to be started is placed in the need_to_start_server_shell array +#Include shell font styles and some basic information +SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. + #fixme Put the shell scripts name here need_to_start_server_shell=( start_rpc_service.sh diff --git a/scripts/msg_gateway_start.sh b/scripts/msg_gateway_start.sh index c66bd8460..f99ce9017 100755 --- a/scripts/msg_gateway_start.sh +++ b/scripts/msg_gateway_start.sh @@ -61,7 +61,7 @@ sleep 1 cd ${msg_gateway_binary_root} for ((i = 0; i < ${#ws_ports[@]}; i++)); do echo "==========================start msg_gateway server===========================">>$OPENIM_ROOT/logs/openIM.log - nohup ./${openim_msggateway} --port ${rpc_ports[$i]} --ws_port ${ws_ports[$i]} --prometheus_port ${prome_ports[$i]} >>$OPENIM_ROOT/logs/openIM.log 2>&1 & + nohup ./${openim_msggateway} --port ${rpc_ports[$i]} --ws_port ${ws_ports[$i]} --prometheus_port ${prome_ports[$i]} --config_folder_path ${configfile_path} >>$OPENIM_ROOT/logs/openIM.log 2>&1 & done #Check launched service process diff --git a/scripts/msg_transfer_start.sh b/scripts/msg_transfer_start.sh index e61492f34..e2034388b 100755 --- a/scripts/msg_transfer_start.sh +++ b/scripts/msg_transfer_start.sh @@ -52,9 +52,9 @@ sleep 1 cd ${msg_transfer_binary_root} for ((i = 0; i < ${msg_transfer_service_num}; i++)); do prome_port=${prome_ports[$i]} - cmd="nohup ./${openim_msgtransfer}" + cmd="nohup ./${openim_msgtransfer} --config_folder_path ${configfile_path}" if [ $prome_port != "" ]; then - cmd="$cmd --prometheus_port $prome_port" + cmd="$cmd --prometheus_port $prome_port --config_folder_path ${configfile_path}" fi echo "==========================start msg_transfer server===========================">>$OPENIM_ROOT/logs/openIM.log $cmd >>$OPENIM_ROOT/logs/openIM.log 2>&1 & diff --git a/scripts/path_info.sh b/scripts/path_info.sh index 5351a43db..595137566 100755 --- a/scripts/path_info.sh +++ b/scripts/path_info.sh @@ -65,8 +65,12 @@ cmd_utils_name="openim-cmdutils" cmd_utils_binary_root="$OPENIM_ROOT/$BIN_DIR" cmd_utils_source_root="$OPENIM_ROOT/cmd/openim-cmdutils/" +echo "debug========> config_path=$config_path" # Global configuration file default dir config_path="$OPENIM_ROOT/config/config.yaml" +configfile_path="$OPENIM_ROOT/config" + +echo "BUG: debug========> config_path=$config_path" # servicefile dir path service_source_root=( @@ -104,4 +108,4 @@ service_names=( "${msg_name}" "${push_name}" # "${sdk_server_name}" -) \ No newline at end of file +) diff --git a/scripts/start_all.sh b/scripts/start_all.sh index 4e7db6e3e..3554b7d14 100755 --- a/scripts/start_all.sh +++ b/scripts/start_all.sh @@ -50,6 +50,8 @@ echo -e "${BOLD_PREFIX}${CYAN_PREFIX}Server Start Time: ${time}${COLOR_SUFFIX}" # Print section separator echo -e "${PURPLE_PREFIX}==========================================================${COLOR_SUFFIX}" +cd $SCRIPTS_ROOT + # FIXME Put the shell script names here need_to_start_server_shell=( start_rpc_service.sh @@ -82,4 +84,4 @@ done echo -e "${PURPLE_PREFIX}==========================================================${COLOR_SUFFIX}" # Print completion message -echo -e "${GREEN_PREFIX}${BOLD_PREFIX}OpenIM Server has been started successfully!${COLOR_SUFFIX}" \ No newline at end of file +echo -e "${GREEN_PREFIX}${BOLD_PREFIX}OpenIM Server has been started successfully!${COLOR_SUFFIX}" diff --git a/scripts/start_rpc_service.sh b/scripts/start_rpc_service.sh index 132afdfe5..cafbb254e 100755 --- a/scripts/start_rpc_service.sh +++ b/scripts/start_rpc_service.sh @@ -95,21 +95,24 @@ for ((i = 0; i < ${#service_filename[*]}; i++)); do portList2=$(cat $config_path | grep ${service_prometheus_port_name[$i]} | awk -F '[:]' '{print $NF}') list_to_string $portList2 + echo -e "debug========>cmd=$cmd AAAAA" prome_ports=($ports_array) #Start related rpc services based on the number of ports for ((j = 0; j < ${#service_ports[*]}; j++)); do #Start the service in the background if [ -z "${prome_ports[$j]}" ]; then - cmd="./${service_filename[$i]} --port ${service_ports[$j]}" + cmd="./${service_filename[$i]} --port ${service_ports[$j]} --config_folder_path ${configfile_path}" else - cmd="./${service_filename[$i]} --port ${service_ports[$j]} --prometheus_port ${prome_ports[$j]}" + cmd="./${service_filename[$i]} --port ${service_ports[$j]} --prometheus_port ${prome_ports[$j]} --config_folder_path ${configfile_path}" fi if [ $i -eq 0 -o $i -eq 1 ]; then cmd="./${service_filename[$i]} --port ${service_ports[$j]}" fi echo $cmd + echo -e "debug========>cmd=$cmd" echo "=====================start ${service_filename[$i]}======================">>$OPENIM_ROOT/logs/openIM.log nohup $cmd >>$OPENIM_ROOT/logs/openIM.log 2>&1 & + echo -e "debug========>OpenIMROOT=$OPENIM_ROOT" sleep 1 pid="netstat -ntlp|grep $j |awk '{printf \$7}'|cut -d/ -f1" echo -e "${GREEN_PREFIX}${service_filename[$i]} start success,port number:${service_ports[$j]} pid:$(eval $pid)$COLOR_SUFFIX" diff --git a/scripts/stop_all.sh b/scripts/stop_all.sh index 4e3537ea7..f413d2a66 100755 --- a/scripts/stop_all.sh +++ b/scripts/stop_all.sh @@ -14,6 +14,7 @@ # limitations under the License. #Include shell font styles and some basic information +SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. #Include shell font styles and some basic information @@ -25,7 +26,7 @@ bin_dir="$BIN_DIR" logs_dir="$OPENIM_ROOT/logs" sdk_db_dir="$OPENIM_ROOT/sdk/db/" -cd "$OPENIM_ROOT/scripts/" +cd "$SCRIPTS_ROOT" for i in ${service_names[*]}; do #Check whether the service exists From f13ffc6e6c848c625ed55e9d0c506bad403b9859 Mon Sep 17 00:00:00 2001 From: kubbot <3293172751ysy@gmail.com> Date: Tue, 11 Jul 2023 22:10:43 +0800 Subject: [PATCH 56/73] fix: all file Signed-off-by: kubbot <3293172751ysy@gmail.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 76b2a4c14..5df672921 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,7 +32,7 @@ VOLUME ["/Open-IM-Server/logs","/Open-IM-Server/config","/Open-IM-Server/scripts #Copy scripts files and binary files to the blank image COPY --from=build /Open-IM-Server/scripts /Open-IM-Server/scripts -COPY --from=build /Open-IM-Server/bin /Open-IM-Server/_output/bin/platforms/linux/amd64/ +COPY --from=build /Open-IM-Server/_output/bin/platforms/linux/amd64 /Open-IM-Server/_output/bin/platforms/linux/amd64 WORKDIR /Open-IM-Server/scripts From 154658ee12cf789b98cb71fd572a72d47857de37 Mon Sep 17 00:00:00 2001 From: kubbot <3293172751ysy@gmail.com> Date: Tue, 11 Jul 2023 22:14:44 +0800 Subject: [PATCH 57/73] feat: add make file Signed-off-by: kubbot <3293172751ysy@gmail.com> --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 5fcc3b8c3..d1c733717 100644 --- a/Makefile +++ b/Makefile @@ -87,7 +87,7 @@ fmt: vet: @$(GO) vet ./... -lint: Check syntax and styling of go sources. ✨ +## lint: Check syntax and styling of go sources. ✨ .PHONY: lint lint: @$(MAKE) go.lint @@ -184,4 +184,4 @@ help: Makefile ## help-all: Show all help details info. ✨ .PHONY: help-all help-all: go.help copyright.help tools.help image.help dependencies.help gen.help release.help swagger.help help - $(call makeallhelp) \ No newline at end of file + $(call makeallhelp) From 531c8a9876af3cee82111f8546735ec6cbae81e5 Mon Sep 17 00:00:00 2001 From: pluto <83957921+plutoyty@users.noreply.github.com> Date: Wed, 12 Jul 2023 10:06:30 +0800 Subject: [PATCH 58/73] Add database retry (#493) --- pkg/common/db/relation/mysql_init.go | 33 ++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/pkg/common/db/relation/mysql_init.go b/pkg/common/db/relation/mysql_init.go index 67419c7bf..fe467b675 100644 --- a/pkg/common/db/relation/mysql_init.go +++ b/pkg/common/db/relation/mysql_init.go @@ -30,16 +30,18 @@ import ( "gorm.io/gorm/logger" ) +const ( + maxRetry = 100 +) + +//newMysqlGormDB Initialize the database connection func newMysqlGormDB() (*gorm.DB, error) { dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=true&loc=Local", config.Config.Mysql.Username, config.Config.Mysql.Password, config.Config.Mysql.Address[0], "mysql") - db, err := gorm.Open(mysql.Open(dsn), nil) + + db, err := connectToDatabase(dsn, maxRetry) if err != nil { - time.Sleep(time.Duration(30) * time.Second) - db, err = gorm.Open(mysql.Open(dsn), nil) - if err != nil { - panic(err.Error() + " open failed " + dsn) - } + panic(err.Error() + " Open failed " + dsn) } sqlDB, err := db.DB() if err != nil { @@ -82,7 +84,24 @@ func newMysqlGormDB() (*gorm.DB, error) { return db, nil } -// gorm mysql +//connectToDatabase Connection retry for mysql +func connectToDatabase(dsn string, maxRetry int) (*gorm.DB, error) { + var db *gorm.DB + var err error + for i := 0; i <= maxRetry; i++ { + db, err = gorm.Open(mysql.Open(dsn), nil) + if err == nil { + return db, nil + } + if mysqlErr, ok := err.(*mysqlDriver.MySQLError); ok && mysqlErr.Number == 1045 { + return nil, err + } + time.Sleep(time.Duration(1) * time.Second) + } + return nil, err +} + +// NewGormDB gorm mysql func NewGormDB() (*gorm.DB, error) { specialerror.AddReplace(gorm.ErrRecordNotFound, errs.ErrRecordNotFound) specialerror.AddErrHandler(replaceDuplicateKey) From dd7e2be74a07d836ef6c8c5e3b192b873db2777b Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <86140903+cubxxw@users.noreply.github.com> Date: Wed, 12 Jul 2023 12:28:18 +0800 Subject: [PATCH 59/73] feat: add logic to deploy images (#497) * feat: add logic to deploy images Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> * feat: add logic to deploy images Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> * fix: add ubuntu Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> * fix: sync quest Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> * feat: add copyright information Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> * feat: add more cicd design Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> * fix: fix sync Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> * feat: test file Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> * feat: test file Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> * feat: build multiarch Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> * feat: build multiarch Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> * feat: add start scripts Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --------- Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- .github/workflows/golangci-link.yml | 4 +- .github/workflows/openim-ci.yml | 148 ++++++++++++++---- .github/workflows/scripts-test.yml | 50 ++++++ .github/workflows/sync.yml | 4 - README.md | 14 +- assets/README.md | 32 ++++ assets/intive-slack.png | Bin 0 -> 21044 bytes assets/logo-gif/LICENSE | 1 + assets/logo-gif/openim-logo.gif | Bin 0 -> 430462 bytes assets/logo/LICENSE | 1 + assets/logo/openim-logo-blue.png | Bin 0 -> 32557 bytes assets/logo/openim-logo-cyan.png | Bin 0 -> 35907 bytes assets/logo/openim-logo-gradient.png | Bin 0 -> 38678 bytes assets/logo/openim-logo-green.png | Bin 0 -> 32597 bytes assets/logo/openim-logo-orange.png | Bin 0 -> 37334 bytes assets/logo/openim-logo-purple.png | Bin 0 -> 32931 bytes assets/logo/openim-logo-red.png | Bin 0 -> 31948 bytes assets/logo/openim-logo-yellow.png | Bin 0 -> 36659 bytes assets/logo/openim-logo.png | Bin 0 -> 38208 bytes go.mod | 1 - go.sum | 2 - install_im_server.sh | 36 +++-- .../msgtransfer/persistent_msg_handler.go | 14 ++ pkg/common/db/relation/mysql_init.go | 4 +- pkg/common/kafka/consumer_group.go | 14 ++ pkg/utils/strings.go | 14 ++ pkg/utils/time_format.go | 14 ++ scripts/build_all_service.sh | 8 +- scripts/path_info.sh | 14 ++ scripts/style_info.sh | 14 ++ 30 files changed, 323 insertions(+), 66 deletions(-) create mode 100644 .github/workflows/scripts-test.yml create mode 100644 assets/README.md create mode 100644 assets/intive-slack.png create mode 100644 assets/logo-gif/LICENSE create mode 100644 assets/logo-gif/openim-logo.gif create mode 100644 assets/logo/LICENSE create mode 100644 assets/logo/openim-logo-blue.png create mode 100644 assets/logo/openim-logo-cyan.png create mode 100644 assets/logo/openim-logo-gradient.png create mode 100644 assets/logo/openim-logo-green.png create mode 100644 assets/logo/openim-logo-orange.png create mode 100644 assets/logo/openim-logo-purple.png create mode 100644 assets/logo/openim-logo-red.png create mode 100644 assets/logo/openim-logo-yellow.png create mode 100644 assets/logo/openim-logo.png diff --git a/.github/workflows/golangci-link.yml b/.github/workflows/golangci-link.yml index 057dcff74..c5fdd7b1b 100644 --- a/.github/workflows/golangci-link.yml +++ b/.github/workflows/golangci-link.yml @@ -17,8 +17,8 @@ name: OpenKF golangci-lint on: push: branches: [main] - pull_request: - branches: [main] +# pull_request: +# branches: [main] jobs: golangci: name: lint diff --git a/.github/workflows/openim-ci.yml b/.github/workflows/openim-ci.yml index c8b95eb8e..5794b2ef3 100644 --- a/.github/workflows/openim-ci.yml +++ b/.github/workflows/openim-ci.yml @@ -1,26 +1,52 @@ +# Copyright © 2023 OpenIM open source community. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + name: OpenIM CI -on: -# main branch +on: push: branches: - - main + - main + paths-ignore: + - "docs/**" + - "README.md" + - "README_zh-CN.md" + - "CONTRIBUTING.md" pull_request: branches: - - main + - main + paths-ignore: + - "README.md" + - "README_zh-CN.md" + - "CONTRIBUTING.md" + - "docs/**" -jobs: +env: + GO_VERSION: "1.19" + GOLANGCI_VERSION: "v1.50.1" - openimci: +jobs: + openim: name: Test with go ${{ matrix.go_version }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} environment: - name: openimci + name: openim strategy: matrix: - go_version: ['1.18', '1.19', '1.20'] - os: [ubuntu-latest, macOS-latest] + go_version: ["1.18","1.19","1.20"] + os: [ubuntu-latest] steps: - name: Set up Go ${{ matrix.go_version }} @@ -32,41 +58,95 @@ jobs: - name: Check out code into the Go module directory uses: actions/checkout@v2 - - name: Run go modules Tidy + - name: Run go modules tidy run: | make tidy - - name: Generate all necessary files, such as error code files + - name: Run go format run: | - make gen + make format + echo "Run go format successfully" - - name: Check syntax and styling of go sources - run: | - make lint + # - name: Generate all necessary files, such as error code files + # run: | + # make generate - - name: Run unit test and get test coverage - run: | - make cover + # - name: Check syntax and styling of go sources + # run: | + # set -e + # make lint + + # - name: Run unit test and get test coverage + # run: | + # make cover - name: Build source code for host platform run: | - make build + make multiarch + echo "Build source code for host platform successfully" - - name: Collect Test Coverage File - uses: actions/upload-artifact@v1.0.0 - with: - name: main-output - path: _output/coverage.out + # - name: Collect Test Coverage File + # uses: actions/upload-artifact@v1.0.0 + # with: + # name: main-output + # path: _output/tmp/coverage.out - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + # - name: Set up Docker Buildx + # uses: docker/setup-buildx-action@v1 - - name: Login to DockerHub - uses: docker/login-action@v1 - with: - username: ${{ env.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} +# lint: +# runs-on: ubuntu-20.04 +# steps: +# - name: Checkout +# uses: actions/checkout@v3 +# with: +# fetch-depth: 0 + +# - name: Set up Go +# uses: actions/setup-go@v3 +# with: +# go-version: ${{ env.GO_VERSION }} + +# - name: golangci-lint +# uses: golangci/golangci-lint-action@v3 +# with: +# version: ${{ env.GOLANGCI_VERSION }} - - name: Build docker images for host arch and push images to registry - run: | - make push \ No newline at end of file +# docker-image-tests: +# runs-on: ubuntu-20.04 +# steps: +# - name: Checkout +# uses: actions/checkout@v3 +# with: +# fetch-depth: 0 + +# - name: Set up Go +# uses: actions/setup-go@v3 +# with: +# go-version: ${{ env.GO_VERSION }} + +# - name: Run tests +# run: make build + +# - name: Test docker image +# run: | +# docker build -t openim:ci-build . + +# goreleaser-test: +# runs-on: ubuntu-20.04 +# steps: +# - name: Checkout +# uses: actions/checkout@v3 +# with: +# fetch-depth: 0 + +# - name: Set up Go +# uses: actions/setup-go@v3 +# with: +# go-version: ${{ env.GO_VERSION }} + +# - name: Run GoReleaser +# uses: goreleaser/goreleaser-action@v4 +# with: +# version: latest +# args: release --clean --skip-publish --snapshot \ No newline at end of file diff --git a/.github/workflows/scripts-test.yml b/.github/workflows/scripts-test.yml new file mode 100644 index 000000000..5bcb2277c --- /dev/null +++ b/.github/workflows/scripts-test.yml @@ -0,0 +1,50 @@ +name: Execute Scripts + +on: + push: + branches: + - main + paths-ignore: + - "docs/**" + - "README.md" + - "README_zh-CN.md" + - "CONTRIBUTING.md" + pull_request: + branches: + - main + paths-ignore: + - "README.md" + - "README_zh-CN.md" + - "CONTRIBUTING.md" + - "docs/**" + +jobs: + execute-scripts: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Start Docker Compose + run: docker-compose up -d + + - name: Stop all services + run: | + chmod +x ./scripts/stop_all.sh + ./scripts/stop_all.sh + + - name: Build all services + run: | + chmod +x ./scripts/build_all_service.sh + ./scripts/build_all_service.sh + + - name: Start all services + run: | + chmod +x ./scripts/start_all.sh + ./scripts/start_all.sh + + - name: Check all services + run: | + chmod +x ./scripts/check_all.sh + ./scripts/check_all.sh diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index 871e400d5..d6e15bf23 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -8,9 +8,6 @@ on: push: branches: - main - pull_request: - branches: - - main workflow_dispatch: jobs: @@ -25,7 +22,6 @@ jobs: with: GH_INSTALLATION_TOKEN: "${{ secrets.BOT_GITHUB_TOKEN }}" CONFIG_PATH: .github/sync.yml - GH_PAT: "${{ secrets.BOT_GITHUB_TOKEN }}" ORIGINAL_MESSAGE: true SKIP_PR: true COMMIT_EACH_FILE: false diff --git a/README.md b/README.md index e8da2ab5e..b180f4438 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ -

- - Open IM Server
-
- ⭐️ Open source Instant Messaging Server ⭐️
-

+

+ + + +

+

+ ⭐️ Open source Instant Messaging Server ⭐️
+

diff --git a/assets/README.md b/assets/README.md new file mode 100644 index 000000000..d3e735dc0 --- /dev/null +++ b/assets/README.md @@ -0,0 +1,32 @@ +# `/assets` + +The `/assets` directory in the OpenIM repository contains various assets such as images, logos, and animated GIFs. These assets serve different purposes and contribute to the functionality and aesthetics of the OpenIM project. + +## Directory Structure: + +```bash +assets/ +├── README.md # Documentation for the assets directory +├── images # Directory holding images related to OpenIM +│ ├── architecture.png # Image depicting the architecture of OpenIM +│ └── mvc.png # Image illustrating the Model-View-Controller (MVC) pattern +├── intive-slack.png # Image displaying the Intive Slack logo +├── logo # Directory containing various logo variations for OpenIM +│ ├── openim-logo-black.png # OpenIM logo with a black background +│ ├── openim-logo-blue.png # OpenIM logo with a blue background +│ ├── openim-logo-green.png # OpenIM logo with a green background +│ ├── openim-logo-purple.png # OpenIM logo with a purple background +│ ├── openim-logo-white.png # OpenIM logo with a white background +│ ├── openim-logo-yellow.png # OpenIM logo with a yellow background +│ └── openim-logo.png # OpenIM logo with a transparent background +└── logo-gif # Directory containing animated GIF versions of the OpenIM logo + └── openim-log.gif # Animated OpenIM logo with a transparent background +``` + +## Copyright Notice: + +The OpenIM logo, including its variations and animated versions, displayed in this repository [OpenIM](https://github.com/OpenIMSDK/openim) under the `/assets/logo` and `/assets/logo-gif` directories, are protected by copyright laws. + +The logo design is credited to @Xx(席欣). + +Please respect the intellectual property rights and refrain from unauthorized use and distribution of these assets. \ No newline at end of file diff --git a/assets/intive-slack.png b/assets/intive-slack.png new file mode 100644 index 0000000000000000000000000000000000000000..3f27e0c6959298ec79a093ac36a00a58755767e9 GIT binary patch literal 21044 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 DC_6OD literal 0 HcmV?d00001 diff --git a/assets/logo-gif/LICENSE b/assets/logo-gif/LICENSE new file mode 100644 index 000000000..fe61855c2 --- /dev/null +++ b/assets/logo-gif/LICENSE @@ -0,0 +1 @@ +# The OpenIM logo files are licensed under a choice of either Apache-2.0 or CC-BY-4.0 (Creative Commons Attribution 4.0 International). \ No newline at end of file diff --git a/assets/logo-gif/openim-logo.gif b/assets/logo-gif/openim-logo.gif new file mode 100644 index 0000000000000000000000000000000000000000..7797896413b8fa290d3c51ad7ac145f34a44f2ed GIT binary patch literal 430462 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 z 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 z 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 *(L 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∾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%0A=;x!k&uD( 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 z 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

ToBM4ua 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 z

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 qz|dTCOh+2f4<<Xz3qyyI_%!Q0h@R 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ⅇZWuv<_4cT<)%~d2dUpy@o12Jrb-M%5?4tdj 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 literal 0 HcmV?d00001 diff --git a/assets/logo/openim-logo-yellow.png b/assets/logo/openim-logo-yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..7440e3c632af1130de6c7f40d82a541f58b84eaf GIT binary patch literal 36659 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%0AWqLXt%@lh6rQ5 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 literal 0 HcmV?d00001 diff --git a/assets/logo/openim-logo.png b/assets/logo/openim-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..cc87f0ac36b2977c7843a3758b98366e8175c049 GIT binary patch literal 38208 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 /dev/null then - docker-compose up -d ; + docker-compose up -d else - docker compose up -d ; + docker compose up -d fi +# Change directory to scripts folder again +cd scripts -cd scripts ; -./docker_check_service.sh +# Check docker services +./docker_check_service.sh \ No newline at end of file diff --git a/internal/msgtransfer/persistent_msg_handler.go b/internal/msgtransfer/persistent_msg_handler.go index eaf47dee9..9ee0f5393 100644 --- a/internal/msgtransfer/persistent_msg_handler.go +++ b/internal/msgtransfer/persistent_msg_handler.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msgtransfer import ( diff --git a/pkg/common/db/relation/mysql_init.go b/pkg/common/db/relation/mysql_init.go index fe467b675..d480ae6e8 100644 --- a/pkg/common/db/relation/mysql_init.go +++ b/pkg/common/db/relation/mysql_init.go @@ -34,7 +34,7 @@ const ( maxRetry = 100 ) -//newMysqlGormDB Initialize the database connection +// newMysqlGormDB Initialize the database connection func newMysqlGormDB() (*gorm.DB, error) { dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=true&loc=Local", config.Config.Mysql.Username, config.Config.Mysql.Password, config.Config.Mysql.Address[0], "mysql") @@ -84,7 +84,7 @@ func newMysqlGormDB() (*gorm.DB, error) { return db, nil } -//connectToDatabase Connection retry for mysql +// connectToDatabase Connection retry for mysql func connectToDatabase(dsn string, maxRetry int) (*gorm.DB, error) { var db *gorm.DB var err error diff --git a/pkg/common/kafka/consumer_group.go b/pkg/common/kafka/consumer_group.go index 2e0155f67..0f801e8b2 100644 --- a/pkg/common/kafka/consumer_group.go +++ b/pkg/common/kafka/consumer_group.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package kafka import ( diff --git a/pkg/utils/strings.go b/pkg/utils/strings.go index 106c6a112..641c43e9f 100644 --- a/pkg/utils/strings.go +++ b/pkg/utils/strings.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package utils import ( diff --git a/pkg/utils/time_format.go b/pkg/utils/time_format.go index af1be7f04..9278a58ef 100644 --- a/pkg/utils/time_format.go +++ b/pkg/utils/time_format.go @@ -1,3 +1,17 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package utils import ( diff --git a/scripts/build_all_service.sh b/scripts/build_all_service.sh index ebbc02dfa..5d8592eca 100755 --- a/scripts/build_all_service.sh +++ b/scripts/build_all_service.sh @@ -53,10 +53,12 @@ fi #Include shell font styles and some basic information OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +echo "PWD=================>$PWD" + #Include shell font styles and some basic information -source $OPENIM_ROOT/scripts/style_info.sh -source $OPENIM_ROOT/scripts/path_info.sh -source $OPENIM_ROOT/scripts/function.sh +source ./style_info.sh +source ./path_info.sh +source ./function.sh cd $OPENIM_ROOT diff --git a/scripts/path_info.sh b/scripts/path_info.sh index 595137566..bff87ed83 100755 --- a/scripts/path_info.sh +++ b/scripts/path_info.sh @@ -1,4 +1,18 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Determine the architecture and version architecture=$(uname -m) version=$(uname -s | tr '[:upper:]' '[:lower:]') diff --git a/scripts/style_info.sh b/scripts/style_info.sh index 631f8a5f1..3c747dd0c 100755 --- a/scripts/style_info.sh +++ b/scripts/style_info.sh @@ -1,4 +1,18 @@ #!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + COLOR_SUFFIX="\033[0m" # End all colors and special effects From 5755a43f1c65bb8ccc2a1aaf5eefea72e0c5a17d Mon Sep 17 00:00:00 2001 From: kubbot <3293172751ysy@gmail.com> Date: Wed, 12 Jul 2023 12:45:23 +0800 Subject: [PATCH 60/73] fix: scripts start Signed-off-by: kubbot <3293172751ysy@gmail.com> --- .github/workflows/scripts-test.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/scripts-test.yml b/.github/workflows/scripts-test.yml index 5bcb2277c..63ffa7d79 100644 --- a/.github/workflows/scripts-test.yml +++ b/.github/workflows/scripts-test.yml @@ -27,7 +27,9 @@ jobs: uses: actions/checkout@v2 - name: Start Docker Compose - run: docker-compose up -d + run: | + docker-compose stop + docker-compose up -d - name: Stop all services run: | @@ -48,3 +50,8 @@ jobs: run: | chmod +x ./scripts/check_all.sh ./scripts/check_all.sh + - name: Print openIM.log + run: | + cat -n logs/openIM.log + cat logs/openIM.log >> "$GITHUB_OUTPUT" + From 3ae5375d1a908e4230451d05e7507036e7742380 Mon Sep 17 00:00:00 2001 From: kubbot <3293172751ysy@gmail.com> Date: Wed, 12 Jul 2023 12:58:24 +0800 Subject: [PATCH 61/73] fix: scripts start Signed-off-by: kubbot <3293172751ysy@gmail.com> --- .github/workflows/scripts-test.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/scripts-test.yml b/.github/workflows/scripts-test.yml index 63ffa7d79..4ddd80c56 100644 --- a/.github/workflows/scripts-test.yml +++ b/.github/workflows/scripts-test.yml @@ -30,26 +30,31 @@ jobs: run: | docker-compose stop docker-compose up -d + cat logs/openIM.log >> "$GITHUB_OUTPUT" - name: Stop all services run: | chmod +x ./scripts/stop_all.sh ./scripts/stop_all.sh + cat logs/openIM.log >> "$GITHUB_OUTPUT" - name: Build all services run: | chmod +x ./scripts/build_all_service.sh ./scripts/build_all_service.sh + cat logs/openIM.log >> "$GITHUB_OUTPUT" - name: Start all services run: | chmod +x ./scripts/start_all.sh ./scripts/start_all.sh + cat logs/openIM.log >> "$GITHUB_OUTPUT" - name: Check all services run: | chmod +x ./scripts/check_all.sh ./scripts/check_all.sh + cat logs/openIM.log >> "$GITHUB_OUTPUT" - name: Print openIM.log run: | cat -n logs/openIM.log From e6499436495095c402ea6cda405a3697c8df5bf1 Mon Sep 17 00:00:00 2001 From: kubbot <3293172751ysy@gmail.com> Date: Wed, 12 Jul 2023 13:01:20 +0800 Subject: [PATCH 62/73] fix: scripts start Signed-off-by: kubbot <3293172751ysy@gmail.com> --- .github/workflows/scripts-test.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/scripts-test.yml b/.github/workflows/scripts-test.yml index 4ddd80c56..ff6afb9ed 100644 --- a/.github/workflows/scripts-test.yml +++ b/.github/workflows/scripts-test.yml @@ -30,13 +30,11 @@ jobs: run: | docker-compose stop docker-compose up -d - cat logs/openIM.log >> "$GITHUB_OUTPUT" - name: Stop all services run: | chmod +x ./scripts/stop_all.sh ./scripts/stop_all.sh - cat logs/openIM.log >> "$GITHUB_OUTPUT" - name: Build all services run: | From 3ff4103711d573d48929a68397fe9ee2577cd95f Mon Sep 17 00:00:00 2001 From: kubbot <3293172751ysy@gmail.com> Date: Wed, 12 Jul 2023 13:11:31 +0800 Subject: [PATCH 63/73] fix: scripts start Signed-off-by: kubbot <3293172751ysy@gmail.com> --- .github/workflows/scripts-test.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/scripts-test.yml b/.github/workflows/scripts-test.yml index ff6afb9ed..1358bcb37 100644 --- a/.github/workflows/scripts-test.yml +++ b/.github/workflows/scripts-test.yml @@ -40,19 +40,22 @@ jobs: run: | chmod +x ./scripts/build_all_service.sh ./scripts/build_all_service.sh - cat logs/openIM.log >> "$GITHUB_OUTPUT" + cat logs/openIM.log - name: Start all services run: | chmod +x ./scripts/start_all.sh ./scripts/start_all.sh - cat logs/openIM.log >> "$GITHUB_OUTPUT" + cat logs/openIM.log + continue-on-error: true - name: Check all services run: | chmod +x ./scripts/check_all.sh ./scripts/check_all.sh - cat logs/openIM.log >> "$GITHUB_OUTPUT" + cat logs/openIM.log + continue-on-error: true + - name: Print openIM.log run: | cat -n logs/openIM.log From e000236ecafdd60d20e642a2107557819de294a3 Mon Sep 17 00:00:00 2001 From: guangwu Date: Wed, 12 Jul 2023 13:19:44 +0800 Subject: [PATCH 64/73] chore: use fmt.Errorf Sprint (#469) --- pkg/common/db/unrelation/extend_msg.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/common/db/unrelation/extend_msg.go b/pkg/common/db/unrelation/extend_msg.go index 17e0b2e19..6f09247bc 100644 --- a/pkg/common/db/unrelation/extend_msg.go +++ b/pkg/common/db/unrelation/extend_msg.go @@ -152,7 +152,7 @@ func (e *ExtendMsgSetMongoDriver) InsertOrUpdateReactionExtendMsgSet( return utils.Wrap(err, "") } if set == nil { - return errors.New(fmt.Sprintf("conversationID %s has no set", conversationID)) + return fmt.Errorf("conversationID %s has no set", conversationID) } _, err = e.ExtendMsgSetCollection.UpdateOne( ctx, @@ -181,7 +181,7 @@ func (e *ExtendMsgSetMongoDriver) DeleteReactionExtendMsgSet( return utils.Wrap(err, "") } if set == nil { - return errors.New(fmt.Sprintf("conversationID %s has no set", conversationID)) + return fmt.Errorf("conversationID %s has no set", conversationID) } _, err = e.ExtendMsgSetCollection.UpdateOne( ctx, @@ -226,5 +226,5 @@ func (e *ExtendMsgSetMongoDriver) TakeExtendMsg( if v, ok := setList[0].ExtendMsgs[clientMsgID]; ok { return &v, nil } - return nil, errors.New(fmt.Sprintf("cant find client msg id: %s", clientMsgID)) + return nil, fmt.Errorf("cant find client msg id: %s", clientMsgID) } From 5467887e1ccceb90af8035dd5dc7739f4966e4f7 Mon Sep 17 00:00:00 2001 From: pluto <83957921+plutoyty@users.noreply.github.com> Date: Wed, 12 Jul 2023 15:23:25 +0800 Subject: [PATCH 65/73] For "add database retry" add test (#500) * Add database retry * For "add database retry" add test * For "add database retry" add test * For 'add database retry' add test Signed-off-by: plutoyty <2631223275@qq.com> * Remove .idea directory and add it to .gitignore --------- Signed-off-by: plutoyty <2631223275@qq.com> --- pkg/common/db/relation/mysql_init.go | 2 +- pkg/common/db/relation/mysql_init_test.go | 38 +++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 pkg/common/db/relation/mysql_init_test.go diff --git a/pkg/common/db/relation/mysql_init.go b/pkg/common/db/relation/mysql_init.go index d480ae6e8..4a6758cba 100644 --- a/pkg/common/db/relation/mysql_init.go +++ b/pkg/common/db/relation/mysql_init.go @@ -31,7 +31,7 @@ import ( ) const ( - maxRetry = 100 + maxRetry = 100 //number of retries ) // newMysqlGormDB Initialize the database connection diff --git a/pkg/common/db/relation/mysql_init_test.go b/pkg/common/db/relation/mysql_init_test.go new file mode 100644 index 000000000..a4f38f7bc --- /dev/null +++ b/pkg/common/db/relation/mysql_init_test.go @@ -0,0 +1,38 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package relation + +import ( + "fmt" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" + "testing" +) + +//TestNewGormDB Test the retry of sporadic errors and the direct exit of wrong password. +func TestNewGormDB(t *testing.T) { + err := config.InitConfig("config_folder_path") + if err != nil { + fmt.Println("config load error") + return + } + db, err := newMysqlGormDB() + if err != nil { + fmt.Println("password error") + return + } + if db != nil { + fmt.Println("success connect") + } +} From 454bf97e3fa2eccd46e373508b837455715c421e Mon Sep 17 00:00:00 2001 From: Gordon <46924906+FGadvancer@users.noreply.github.com> Date: Wed, 12 Jul 2023 15:31:24 +0800 Subject: [PATCH 66/73] merge v3dev into main (#504) * statistics user register * refactor: router change * minio init * UserRegisterCount * push use local conn * refactor: user pb update * remove online push close grpc conn * refactor: user pb update * refactor:pb file * msgs statistics * msgs statistics * revoke userID * refactor: errcode update * active user * active user * active user * refactor: errcode update * feat: conn update token * active user * active user * feat: conn update token * active user * feat: conn update token * feat: conn update token * feat: conn update token * add tx_oss cos * active user * active user * group create * group create * feat: group notification show to conversation * feat: group notification show to conversation * group active * user active * sendNotificationWithName * withname * privateChat * a2r call option * grpc with detail return error * change log error * chain unary interceptor * api nil slice map * fix sync has read * fix: text update * fix: update add model * set conversations update * set privateChat * fix: content update * remove unuse rpc * msgDestruct * cron use rpc mw * set IsMsgDestruct * msg destruct * msgDestruct * s3 minio, cos, oss support * feat: add implement of GetUsersOnlineStatus, #472 (#477) * s3 minio, cos, oss support * s3 route * remove extendMsg code * s3 route * remove unuse code * s3 pb * s3 pb * s3 pb * s3 presigned put * s3 presigned test * s3 presigned test * s3 presigned test * s3 presigned test * s3 presigned test * s3 presigned test * s3 presigned test * s3 presigned test * Update .gitignore (#482) * s3 debug log * s3 debug log * cron add log and fix cron * add log * cron * s3 config * fix kick user bug * s3 cos * add kick log * s3 cos test * s3 cos test * s3 cos test * kick user log * kickuserlog * s3 cos copy * s3 cos copy * s3 url * s3 url * s3 AccessURL * log * s3 InitiateMultipartUpload add ExpireTime --------- Co-authored-by: withchao <993506633@qq.com> Co-authored-by: wangchuxiao Co-authored-by: BanTanger <88583317+BanTanger@users.noreply.github.com> Co-authored-by: withchao <48119764+withchao@users.noreply.github.com> Co-authored-by: Alan <68671759+hanzhixiao@users.noreply.github.com> --- .gitignore | 2 + .idea/Open-IM-Server.iml | 9 + .idea/dataSources.xml | 14 + .idea/misc.xml | 6 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + cmd/openim-api/main.go | 2 +- cmd/openim-msgtransfer/main.go | 27 - config/config.yaml | 59 +- go.mod | 25 +- go.sum | 58 +- internal/api/conversation.go | 12 - internal/api/group.go | 4 + internal/api/msg.go | 34 +- internal/api/route.go | 27 +- internal/api/third.go | 84 +- internal/api/user.go | 115 +- internal/msggateway/client.go | 24 +- internal/msggateway/hub_server.go | 4 + internal/msggateway/n_ws_server.go | 2 +- internal/msgtransfer/init.go | 62 +- internal/msgtransfer/modify_msg_handler.go | 145 - internal/push/push_to_client.go | 118 +- internal/rpc/auth/auth.go | 8 +- internal/rpc/conversation/conversaion.go | 202 +- internal/rpc/group/db_map.go | 8 +- internal/rpc/group/group.go | 237 +- internal/rpc/group/statistics.go | 28 + internal/rpc/msg/extend_msg.go | 414 --- internal/rpc/msg/extend_msg_callback.go | 105 - internal/rpc/msg/revoke.go | 1 - internal/rpc/msg/send.go | 34 +- internal/rpc/msg/server.go | 30 +- internal/rpc/msg/statistics.go | 84 + internal/rpc/third/s3.go | 163 +- internal/rpc/third/third.go | 76 +- internal/rpc/third/tool.go | 63 + internal/rpc/user/statistics.go | 11 +- internal/rpc/user/user.go | 76 +- internal/tools/conversation.go | 38 + internal/tools/cron_task.go | 12 +- internal/tools/msg.go | 93 +- pkg/apiresp/format.go | 5 + pkg/apiresp/resp.go | 3 + pkg/apistruct/msg.go | 147 +- pkg/callbackstruct/message.go | 69 - pkg/common/cmd/cron_task.go | 2 +- pkg/common/cmd/root.go | 33 +- pkg/common/cmd/rpc.go | 24 +- pkg/common/config/config.go | 63 +- pkg/common/convert/conversation.go | 2 + pkg/common/db/cache/extend_msg_set.go | 101 - pkg/common/db/controller/conversation.go | 137 +- pkg/common/db/controller/extend_msg.go | 158 - pkg/common/db/controller/group.go | 14 + pkg/common/db/controller/msg.go | 620 +--- pkg/common/db/controller/s3.go | 76 + pkg/common/db/controller/storage.go | 567 --- pkg/common/db/controller/user.go | 6 +- pkg/common/db/obj/minio.go | 258 -- pkg/common/db/obj/obj.go | 110 - pkg/common/db/relation/conversation_model.go | 216 +- pkg/common/db/relation/group_model.go | 66 +- pkg/common/db/relation/object_hash_model.go | 66 - pkg/common/db/relation/object_info_model.go | 69 - pkg/common/db/relation/object_model.go | 36 + pkg/common/db/relation/object_put_model.go | 67 - pkg/common/db/relation/user_model.go | 14 +- pkg/common/db/s3/cont/consts.go | 9 + pkg/common/db/s3/cont/controller.go | 244 ++ pkg/common/db/s3/cont/error.go | 14 + pkg/common/db/s3/cont/id.go | 35 + pkg/common/db/s3/cont/structs.go | 15 + pkg/common/db/s3/cos/cos.go | 254 ++ pkg/common/db/s3/minio/minio.go | 250 ++ pkg/common/db/s3/oss/oss.go | 259 ++ pkg/common/db/s3/oss/sign.go | 81 + pkg/common/db/s3/oss/sort.go | 47 + pkg/common/db/s3/s3.go | 134 + pkg/common/db/table/relation/conversation.go | 71 +- pkg/common/db/table/relation/group.go | 4 + pkg/common/db/table/relation/object.go | 31 + pkg/common/db/table/relation/object_hash.go | 44 - pkg/common/db/table/relation/object_info.go | 43 - pkg/common/db/table/relation/object_put.go | 51 - pkg/common/db/table/relation/user.go | 4 +- .../db/table/unrelation/extend_msg_set.go | 125 - pkg/common/db/table/unrelation/msg.go | 14 +- pkg/common/db/unrelation/extend_msg.go | 230 -- pkg/common/db/unrelation/mongo.go | 4 - pkg/common/db/unrelation/msg.go | 683 +++- pkg/common/log/sql_logger.go | 60 +- pkg/common/mw/rpc_client_interceptor.go | 2 +- pkg/common/mw/rpc_server_interceptor.go | 6 +- pkg/common/tokenverify/jwt_token.go | 4 +- pkg/discoveryregistry/discovery_register.go | 3 +- pkg/discoveryregistry/zookeeper/discover.go | 74 +- pkg/discoveryregistry/zookeeper/register.go | 3 +- pkg/discoveryregistry/zookeeper/zk.go | 9 +- pkg/errs/code.go | 66 +- pkg/errs/predefine.go | 51 +- pkg/errs/relation.go | 1 - pkg/proto/conversation/conversation.pb.go | 1455 +++----- pkg/proto/conversation/conversation.proto | 33 +- pkg/proto/group/group.pb.go | 789 +++-- pkg/proto/group/group.proto | 13 + pkg/proto/msg/msg.pb.go | 3152 ++++++----------- pkg/proto/msg/msg.proto | 140 +- pkg/proto/msggateway/msggateway.pb.go | 470 ++- pkg/proto/msggateway/msggateway.proto | 12 + pkg/proto/sdkws/sdkws.pb.go | 832 +---- pkg/proto/sdkws/sdkws.proto | 42 - pkg/proto/third/third.pb.go | 1420 +++++--- pkg/proto/third/third.proto | 131 +- pkg/proto/user/user.pb.go | 152 +- pkg/proto/user/user.proto | 5 +- pkg/rpcclient/conversation.go | 105 +- pkg/rpcclient/msg.go | 102 +- pkg/rpcclient/notification/extend_msg.go | 162 - pkg/rpcclient/notification/group.go | 260 +- pkg/rpcclient/notification/msg.go | 36 + pkg/rpcclient/user.go | 8 +- pkg/utils/utils_v2.go | 12 + 123 files changed, 7402 insertions(+), 10313 deletions(-) create mode 100644 .idea/Open-IM-Server.iml create mode 100644 .idea/dataSources.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml delete mode 100644 cmd/openim-msgtransfer/main.go delete mode 100644 internal/msgtransfer/modify_msg_handler.go create mode 100644 internal/rpc/group/statistics.go delete mode 100644 internal/rpc/msg/extend_msg.go delete mode 100644 internal/rpc/msg/extend_msg_callback.go create mode 100644 internal/rpc/msg/statistics.go create mode 100644 internal/rpc/third/tool.go create mode 100644 internal/tools/conversation.go create mode 100644 pkg/apiresp/format.go delete mode 100644 pkg/common/db/cache/extend_msg_set.go delete mode 100644 pkg/common/db/controller/extend_msg.go create mode 100644 pkg/common/db/controller/s3.go delete mode 100644 pkg/common/db/controller/storage.go delete mode 100644 pkg/common/db/obj/minio.go delete mode 100644 pkg/common/db/obj/obj.go delete mode 100644 pkg/common/db/relation/object_hash_model.go delete mode 100644 pkg/common/db/relation/object_info_model.go create mode 100644 pkg/common/db/relation/object_model.go delete mode 100644 pkg/common/db/relation/object_put_model.go create mode 100644 pkg/common/db/s3/cont/consts.go create mode 100644 pkg/common/db/s3/cont/controller.go create mode 100644 pkg/common/db/s3/cont/error.go create mode 100644 pkg/common/db/s3/cont/id.go create mode 100644 pkg/common/db/s3/cont/structs.go create mode 100644 pkg/common/db/s3/cos/cos.go create mode 100644 pkg/common/db/s3/minio/minio.go create mode 100644 pkg/common/db/s3/oss/oss.go create mode 100644 pkg/common/db/s3/oss/sign.go create mode 100644 pkg/common/db/s3/oss/sort.go create mode 100644 pkg/common/db/s3/s3.go create mode 100644 pkg/common/db/table/relation/object.go delete mode 100644 pkg/common/db/table/relation/object_hash.go delete mode 100644 pkg/common/db/table/relation/object_info.go delete mode 100644 pkg/common/db/table/relation/object_put.go delete mode 100644 pkg/common/db/table/unrelation/extend_msg_set.go delete mode 100644 pkg/common/db/unrelation/extend_msg.go delete mode 100644 pkg/rpcclient/notification/extend_msg.go create mode 100644 pkg/rpcclient/notification/msg.go diff --git a/.gitignore b/.gitignore index 6e29bad2c..bac199fc2 100644 --- a/.gitignore +++ b/.gitignore @@ -162,6 +162,8 @@ go.work # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # User-specific stuff +.idea/ +.idea .idea/**/workspace.xml .idea/**/tasks.xml .idea/**/usage.statistics.xml diff --git a/.idea/Open-IM-Server.iml b/.idea/Open-IM-Server.iml new file mode 100644 index 000000000..5e764c4f0 --- /dev/null +++ b/.idea/Open-IM-Server.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 000000000..589964a46 --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,14 @@ + + + + + sqlite.xerial + true + org.sqlite.JDBC + jdbc:sqlite:C:\Users\Administrator\Desktop\Open-IM-Server\docker-compose_cfg\grafana.db + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 000000000..28a804d89 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 000000000..d9805dbb6 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 000000000..35eb1ddfb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/cmd/openim-api/main.go b/cmd/openim-api/main.go index aac83a805..b80460990 100644 --- a/cmd/openim-api/main.go +++ b/cmd/openim-api/main.go @@ -73,7 +73,7 @@ func run(port int) error { if err != nil { return err } - if client.CreateRpcRootNodes(config.GetServiceNames()); err != nil { + if err := client.CreateRpcRootNodes(config.GetServiceNames()); err != nil { return err } fmt.Println("api init discov client success") diff --git a/cmd/openim-msgtransfer/main.go b/cmd/openim-msgtransfer/main.go deleted file mode 100644 index aef347793..000000000 --- a/cmd/openim-msgtransfer/main.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/cmd" -) - -func main() { - msgTransferCmd := cmd.NewMsgTransferCmd() - msgTransferCmd.AddPrometheusPortFlag() - if err := msgTransferCmd.Exec(); err != nil { - panic(err.Error()) - } -} diff --git a/config/config.yaml b/config/config.yaml index ea6f8772d..07d46957e 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -56,14 +56,11 @@ kafka: topic: "offlineMsgToMongoMysql" #不建议修改 msgToPush: topic: "msgToPush" #不建议修改 - msgToModify: - topic: "msgToModify" #不建议修改 consumerGroupID: #消费者组,不建议修改 msgToRedis: redis # msgToMongo: mongo # msgToMySql: mysql # msgToPush: push # - msgToModify: modify # rpc: @@ -76,41 +73,26 @@ api: listenIP: #默认为0.0.0.0 object: - enable: minio #使用minio - apiURL: http://127.0.0.1:10002/third/object + enable: "minio" #使用minio + apiURL: "http://127.0.0.1:10002/object/" minio: - tempBucket: "openim" #不建议修改 - dataBucket: "openim" #不建议修改 - location: us-east-1 #不建议修改 - endpoint: http://127.0.0.1:10005 #minio对外服务的ip和端口,app要能访问此ip和端口 - accessKeyID: root #ID - secretAccessKey: openIM123 #秘钥 - isDistributedMod: false #是否分布式多硬盘部署,如果是多硬盘部署,需要修改为true - tencent: #tencent cos - appID: - region: - bucket: - secretID: - secretKey: - ali: #ali oss - regionID: - accessKeyID: - accessKeySecret: - stsEndpoint: - ossEndpoint: - bucket: - finalHost: - stsDurationSeconds: - OssRoleArn: - aws: - accessKeyID: - accessKeySecret: - region: - bucket: - finalHost: - roleArn: - externalId: - roleSessionName: + bucket: "openim" #不建议修改 + endpoint: "http://127.0.0.1:10005" #minio对外服务的ip和端口,app要能访问此ip和端口 + accessKeyID: "root" #ID + secretAccessKey: "openIM123" #秘钥 + sessionToken: "" #token + cos: #tencent cos + bucketURL: "https://temp-1252357374.cos.ap-chengdu.myqcloud.com" + secretID: "" + secretKey: "" + sessionToken: "" + oss: #ali oss + endpoint: "https://oss-cn-chengdu.aliyuncs.com" + bucket: "demo-9999999" + bucketURL: "https://demo-9999999.oss-cn-chengdu.aliyuncs.com" + accessKeyID: "" + accessKeySecret: "" + sessionToken: "" rpcPort: #rpc服务端口,不建议修改,端口由脚本读取后传入程序,如启动多个程序,只需要填入多个端口,用逗号隔开,如 [10110, 10111] openImUserPort: [ 10110 ] @@ -182,7 +164,8 @@ groupMessageHasReadReceiptEnable: true #群聊已读是否开 singleMessageHasReadReceiptEnable: true #单聊已读是否开启 retainChatRecords: 365 #mongo保存离线消息时间(天) -chatRecordsClearTime: "0 2 * * 3" #每周三凌晨2点清理mongo中的过期(超过retainChatRecords时间)消息 +chatRecordsClearTime: "0 2 * * 3" #每周三凌晨2点清理mongo中的过期(超过retainChatRecords时间)消息,这个删除是为了清理满足上个配置retainChatRecords的过期消息,不会发送通知,仅仅作为清理磁盘使用 +msgDestructTime: "0 2 * * *" #消息自动删除时间,每天凌晨2点删除过期消息,这个删除是为了删除保留时间超过超过会话字段msg_destruct_time(秒)的消息。 secret: tuoyun #秘钥,获取token时校验 diff --git a/go.mod b/go.mod index 0a8c1fd52..8a7fd7153 100644 --- a/go.mod +++ b/go.mod @@ -17,13 +17,13 @@ require ( github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/jinzhu/copier v0.3.5 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible - github.com/minio/minio-go/v7 v7.0.22 - github.com/mitchellh/mapstructure v1.4.2 + github.com/minio/minio-go/v7 v7.0.59 + github.com/mitchellh/mapstructure v1.5.0 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.13.0 github.com/robfig/cron/v3 v3.0.1 - github.com/sirupsen/logrus v1.9.0 // indirect + github.com/sirupsen/logrus v1.9.2 // indirect github.com/stretchr/testify v1.8.3 github.com/tencentyun/qcloud-cos-sts-sdk v0.0.0-20210325043845-84a0811633ca go.mongodb.org/mongo-driver v1.8.3 @@ -39,10 +39,12 @@ require ( require github.com/google/uuid v1.3.0 require ( + github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible github.com/go-redis/redis v6.15.9+incompatible github.com/go-sql-driver/mysql v1.6.0 github.com/go-zookeeper/zk v1.0.3 github.com/redis/go-redis/v9 v9.0.5 + github.com/tencentyun/cos-go-sdk-v5 v0.7.41 ) require ( @@ -57,9 +59,10 @@ require ( github.com/bytedance/sonic v1.9.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/clbanning/mxj v1.8.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/dustin/go-humanize v1.0.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/eapache/go-resiliency v1.2.0 // indirect github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect github.com/eapache/queue v1.1.0 // indirect @@ -70,6 +73,7 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.3 // indirect github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-querystring v1.1.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect github.com/googleapis/gax-go/v2 v2.7.1 // indirect github.com/hashicorp/go-uuid v1.0.2 // indirect @@ -82,17 +86,16 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.13.6 // indirect - github.com/klauspost/cpuid v1.3.1 // indirect + github.com/klauspost/compress v1.16.5 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/lithammer/shortuuid v3.0.0+incompatible // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect - github.com/minio/md5-simd v1.1.0 // indirect - github.com/minio/sha256-simd v0.1.1 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/minio/md5-simd v1.1.2 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mozillazg/go-httpheader v0.4.0 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/gomega v1.18.1 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect @@ -102,7 +105,7 @@ require ( github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect - github.com/rs/xid v1.2.1 // indirect + github.com/rs/xid v1.5.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect @@ -136,7 +139,7 @@ require ( go.uber.org/zap v1.24.0 golang.org/x/crypto v0.10.0 // indirect google.golang.org/genproto v0.0.0-20230525234025-438c736192d0 // indirect - gopkg.in/ini.v1 v1.66.2 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect ) replace github.com/Shopify/sarama => github.com/Shopify/sarama v1.29.0 diff --git a/go.sum b/go.sum index 443d6cb9d..e95d2e81e 100644 --- a/go.sum +++ b/go.sum @@ -51,6 +51,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OpenIMSDK/open_utils v1.0.8 h1:IopxWgJwEF5ZAPsRuiZZOfcxNOQOCt/p8VDENcHN9r4= github.com/OpenIMSDK/open_utils v1.0.8/go.mod h1:FLoaQblWUVKQgqt2LrNzfSZLT6D3DICBn1kcOMDLUOI= +github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= github.com/Shopify/sarama v1.29.0 h1:ARid8o8oieau9XrHI55f/L3EoRAhm9px6sonbD7yuUE= github.com/Shopify/sarama v1.29.0/go.mod h1:2QpgD79wpdAESqNQMxNc0KYMkycd4slxGdV3TWSVqrU= github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= @@ -60,6 +61,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible h1:KpbJFXwhVeuxNtBJ74MCGbIoaBok2uZvkD7QXp2+Wis= +github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -83,6 +86,8 @@ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583j github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= +github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -94,8 +99,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dtm-labs/rockscache v0.1.1 h1:6S1vgaHvGqrLd8Ka4hRTKeKPV7v+tT0MSkTIX81LRyA= github.com/dtm-labs/rockscache v0.1.1/go.mod h1:c76WX0kyIibmQ2ACxUXvDvaLykoPakivMqIxt+UzE7A= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q= github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= @@ -202,6 +207,9 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -216,6 +224,7 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -274,11 +283,10 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= -github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= +github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= +github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= @@ -306,16 +314,15 @@ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APP github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4= -github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= -github.com/minio/minio-go/v7 v7.0.22 h1:iXhsiRyYh1ozm/+jN2qGgEIahYjEkvcpuu6NcdpSxcA= -github.com/minio/minio-go/v7 v7.0.22/go.mod h1:ei5JjmxwHaMrgsMrn4U/+Nmg+d8MKS1U2DAn1ou4+Do= -github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= -github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo= -github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +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.59 h1:lxIXwsTIcQkYoEG25rUJbzpmSB/oWeVDmxFo/uWUUsw= +github.com/minio/minio-go/v7 v7.0.59/go.mod h1:NUDy4A4oXPq1l2yK6LTSvCEzAMeIcoz9lcj5dbzSrRE= +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/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -324,6 +331,9 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60= +github.com/mozillazg/go-httpheader v0.4.0 h1:aBn6aRXtFzyDLZ4VIRLsZbbJloagQfMnCiYgOq6hK4w= +github.com/mozillazg/go-httpheader v0.4.0/go.mod h1:PuT8h0pw6efvp8ZeUec1Rs7dwjK08bt6gKSReGMqtdA= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= @@ -384,14 +394,14 @@ github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDO 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/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= +github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -412,6 +422,10 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.194/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.194/go.mod h1:yrBKWhChnDqNz1xuXdSbWXG56XawEq0G5j1lg4VwBD4= +github.com/tencentyun/cos-go-sdk-v5 v0.7.41 h1:iU0Li/Np78H4SBna0ECQoF3mpgi6ImLXU+doGzPFXGc= +github.com/tencentyun/cos-go-sdk-v5 v0.7.41/go.mod h1:4dCEtLHGh8QPxHEkgq+nFaky7yZxQuYwgSJM87icDaw= github.com/tencentyun/qcloud-cos-sts-sdk v0.0.0-20210325043845-84a0811633ca h1:G/aIr3WiUesWHL2YGYgEqjM5tCAJ43Ml+0C18wDkWWs= github.com/tencentyun/qcloud-cos-sts-sdk v0.0.0-20210325043845-84a0811633ca/go.mod h1:b18KQa4IxHbxeseW1GcZox53d7J0z39VNONTxvvlkXw= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= @@ -782,8 +796,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= -gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/api/conversation.go b/internal/api/conversation.go index 7f1732fec..6058049a1 100644 --- a/internal/api/conversation.go +++ b/internal/api/conversation.go @@ -41,18 +41,6 @@ func (o *ConversationApi) GetConversations(c *gin.Context) { a2r.Call(conversation.ConversationClient.GetConversations, o.Client, c) } -func (o *ConversationApi) BatchSetConversations(c *gin.Context) { - a2r.Call(conversation.ConversationClient.BatchSetConversations, o.Client, c) -} - -func (o *ConversationApi) SetRecvMsgOpt(c *gin.Context) { - a2r.Call(conversation.ConversationClient.SetRecvMsgOpt, o.Client, c) -} - -func (o *ConversationApi) ModifyConversationField(c *gin.Context) { - a2r.Call(conversation.ConversationClient.ModifyConversationField, o.Client, c) -} - func (o *ConversationApi) SetConversations(c *gin.Context) { a2r.Call(conversation.ConversationClient.SetConversations, o.Client, c) } diff --git a/internal/api/group.go b/internal/api/group.go index 432645caa..2e293f72a 100644 --- a/internal/api/group.go +++ b/internal/api/group.go @@ -128,3 +128,7 @@ func (o *GroupApi) GetJoinedSuperGroupList(c *gin.Context) { func (o *GroupApi) GetSuperGroupsInfo(c *gin.Context) { a2r.Call(group.GroupClient.GetSuperGroupsInfo, o.Client, c) } + +func (o *GroupApi) GroupCreateCount(c *gin.Context) { + a2r.Call(group.GroupClient.GroupCreateCount, o.Client, c) +} diff --git a/internal/api/msg.go b/internal/api/msg.go index a4da4ea7e..7e9101681 100644 --- a/internal/api/msg.go +++ b/internal/api/msg.go @@ -25,6 +25,7 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/apistruct" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/tokenverify" "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" @@ -162,33 +163,16 @@ func (m *MessageApi) DeleteMsgPhysical(c *gin.Context) { a2r.Call(msg.MsgClient.DeleteMsgPhysical, m.Client, c) } -func (m *MessageApi) SetMessageReactionExtensions(c *gin.Context) { - a2r.Call(msg.MsgClient.SetMessageReactionExtensions, m.Client, c) -} - -func (m *MessageApi) GetMessageListReactionExtensions(c *gin.Context) { - a2r.Call(msg.MsgClient.GetMessagesReactionExtensions, m.Client, c) -} - -func (m *MessageApi) AddMessageReactionExtensions(c *gin.Context) { - a2r.Call(msg.MsgClient.AddMessageReactionExtensions, m.Client, c) -} - -func (m *MessageApi) DeleteMessageReactionExtensions(c *gin.Context) { - a2r.Call(msg.MsgClient.DeleteMessageReactionExtensions, m.Client, c) -} - func (m *MessageApi) SendMessage(c *gin.Context) { params := apistruct.ManagementSendMsgReq{} if err := c.BindJSON(¶ms); err != nil { apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap()) return } - // todo - //if !tokenverify.IsAppManagerUid(c) { - // apiresp.GinError(c, errs.ErrNoPermission.Wrap("only app manager can send message")) - // return - //} + if !tokenverify.IsAppManagerUid(c) { + apiresp.GinError(c, errs.ErrNoPermission.Wrap("only app manager can send message")) + return + } var data interface{} switch params.ContentType { @@ -253,3 +237,11 @@ func (m *MessageApi) CheckMsgIsSendSuccess(c *gin.Context) { func (m *MessageApi) GetUsersOnlineStatus(c *gin.Context) { a2r.Call(msg.MsgClient.GetSendMsgStatus, m.Client, c) } + +func (m *MessageApi) GetActiveUser(c *gin.Context) { + a2r.Call(msg.MsgClient.GetActiveUser, m.Client, c) +} + +func (m *MessageApi) GetActiveGroup(c *gin.Context) { + a2r.Call(msg.MsgClient.GetActiveGroup, m.Client, c) +} diff --git a/internal/api/route.go b/internal/api/route.go index b014f66ab..b37e5a3f7 100644 --- a/internal/api/route.go +++ b/internal/api/route.go @@ -41,6 +41,7 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive log.ZInfo(context.Background(), "load config", "config", config.Config) r.Use(gin.Recovery(), mw.CorsHandler(), mw.GinParseOperationID()) u := NewUserApi(discov) + m := NewMessageApi(discov) if config.Config.Prometheus.Enable { prome.NewApiRequestCounter() prome.NewApiRequestFailedCounter() @@ -59,6 +60,7 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive userRouterGroup.POST("/account_check", ParseToken, u.AccountCheck) userRouterGroup.POST("/get_users", ParseToken, u.GetUsers) userRouterGroup.POST("/get_users_online_status", ParseToken, u.GetUsersOnlineStatus) + userRouterGroup.POST("/get_users_online_token_detail", ParseToken, u.GetUsersOnlineTokenDetail) } //friend routing group friendRouterGroup := r.Group("/friend", ParseToken) @@ -111,7 +113,6 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive authRouterGroup := r.Group("/auth") { a := NewAuthApi(discov) - authRouterGroup.POST("/user_register", u.UserRegister) authRouterGroup.POST("/user_token", a.UserToken) authRouterGroup.POST("/parse_token", a.ParseToken) authRouterGroup.POST("/force_logout", ParseToken, a.ForceLogout) @@ -123,17 +124,19 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive thirdGroup.POST("/fcm_update_token", t.FcmUpdateToken) thirdGroup.POST("/set_app_badge", t.SetAppBadge) - thirdGroup.POST("/apply_put", t.ApplyPut) - thirdGroup.POST("/get_put", t.GetPut) - thirdGroup.POST("/confirm_put", t.ConfirmPut) - thirdGroup.POST("/get_hash", t.GetHash) - thirdGroup.POST("/object", t.GetURL) - thirdGroup.GET("/object", t.GetURL) + objectGroup := r.Group("/object", ParseToken) + + objectGroup.POST("/part_limit", t.PartLimit) + objectGroup.POST("/part_size", t.PartSize) + objectGroup.POST("/initiate_multipart_upload", t.InitiateMultipartUpload) + objectGroup.POST("/auth_sign", t.AuthSign) + objectGroup.POST("/complete_multipart_upload", t.CompleteMultipartUpload) + objectGroup.POST("/access_url", t.AccessURL) + objectGroup.GET("/*name", t.ObjectRedirect) } //Message msgGroup := r.Group("/msg", ParseToken) { - m := NewMessageApi(discov) msgGroup.POST("/newest_seq", m.GetSeq) msgGroup.POST("/send_msg", m.SendMessage) msgGroup.POST("/pull_msg_by_seq", m.PullMsgBySeqs) @@ -159,15 +162,15 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive conversationGroup.POST("/get_all_conversations", c.GetAllConversations) conversationGroup.POST("/get_conversation", c.GetConversation) conversationGroup.POST("/get_conversations", c.GetConversations) - conversationGroup.POST("/batch_set_conversation", c.BatchSetConversations) - conversationGroup.POST("/set_recv_msg_opt", c.SetRecvMsgOpt) - conversationGroup.POST("/modify_conversation_field", c.ModifyConversationField) conversationGroup.POST("/set_conversations", c.SetConversations) } statisticsGroup := r.Group("/statistics", ParseToken) { - statisticsGroup.POST("/user_register", u.UserRegisterCount) + statisticsGroup.POST("/user/register", u.UserRegisterCount) + statisticsGroup.POST("/user/active", m.GetActiveUser) + statisticsGroup.POST("/group/create", g.GroupCreateCount) + statisticsGroup.POST("/group/active", m.GetActiveGroup) } return r } diff --git a/internal/api/third.go b/internal/api/third.go index 79be58a46..ad9475511 100644 --- a/internal/api/third.go +++ b/internal/api/third.go @@ -1,33 +1,16 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package api import ( - "math/rand" - "net/http" - "strconv" - - "github.com/gin-gonic/gin" - "github.com/OpenIMSDK/Open-IM-Server/pkg/a2r" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/third" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" + "github.com/gin-gonic/gin" + "math/rand" + "net/http" + "strconv" ) type ThirdApi rpcclient.Third @@ -36,54 +19,55 @@ func NewThirdApi(discov discoveryregistry.SvcDiscoveryRegistry) ThirdApi { return ThirdApi(*rpcclient.NewThird(discov)) } -func (o *ThirdApi) ApplyPut(c *gin.Context) { - a2r.Call(third.ThirdClient.ApplyPut, o.Client, c) +func (o *ThirdApi) FcmUpdateToken(c *gin.Context) { + a2r.Call(third.ThirdClient.FcmUpdateToken, o.Client, c) +} + +func (o *ThirdApi) SetAppBadge(c *gin.Context) { + a2r.Call(third.ThirdClient.SetAppBadge, o.Client, c) } -func (o *ThirdApi) GetPut(c *gin.Context) { - a2r.Call(third.ThirdClient.GetPut, o.Client, c) +// #################### s3 #################### + +func (o *ThirdApi) PartLimit(c *gin.Context) { + a2r.Call(third.ThirdClient.PartLimit, o.Client, c) } -func (o *ThirdApi) ConfirmPut(c *gin.Context) { - a2r.Call(third.ThirdClient.ConfirmPut, o.Client, c) +func (o *ThirdApi) PartSize(c *gin.Context) { + a2r.Call(third.ThirdClient.PartSize, o.Client, c) } -func (o *ThirdApi) GetHash(c *gin.Context) { - a2r.Call(third.ThirdClient.GetHashInfo, o.Client, c) +func (o *ThirdApi) InitiateMultipartUpload(c *gin.Context) { + a2r.Call(third.ThirdClient.InitiateMultipartUpload, o.Client, c) } -func (o *ThirdApi) FcmUpdateToken(c *gin.Context) { - a2r.Call(third.ThirdClient.FcmUpdateToken, o.Client, c) +func (o *ThirdApi) AuthSign(c *gin.Context) { + a2r.Call(third.ThirdClient.AuthSign, o.Client, c) } -func (o *ThirdApi) SetAppBadge(c *gin.Context) { - a2r.Call(third.ThirdClient.SetAppBadge, o.Client, c) +func (o *ThirdApi) CompleteMultipartUpload(c *gin.Context) { + a2r.Call(third.ThirdClient.CompleteMultipartUpload, o.Client, c) } -func (o *ThirdApi) GetURL(c *gin.Context) { - if c.Request.Method == http.MethodPost { - a2r.Call(third.ThirdClient.GetUrl, o.Client, c) - return - } - name := c.Query("name") +func (o *ThirdApi) AccessURL(c *gin.Context) { + a2r.Call(third.ThirdClient.AccessURL, o.Client, c) +} + +func (o *ThirdApi) ObjectRedirect(c *gin.Context) { + name := c.Param("name") if name == "" { c.String(http.StatusBadRequest, "name is empty") return } + if name[0] == '/' { + name = name[1:] + } operationID := c.Query("operationID") if operationID == "" { - operationID = "auto_" + strconv.Itoa(rand.Int()) - } - expires, _ := strconv.ParseInt(c.Query("expires"), 10, 64) - if expires <= 0 { - expires = 3600 * 1000 + operationID = strconv.Itoa(rand.Int()) } - attachment, _ := strconv.ParseBool(c.Query("attachment")) - c.Set(constant.OperationID, operationID) - resp, err := o.Client.GetUrl( - mcontext.SetOperationID(c, operationID), - &third.GetUrlReq{Name: name, Expires: expires, Attachment: attachment}, - ) + ctx := mcontext.SetOperationID(c, operationID) + resp, err := o.Client.AccessURL(ctx, &third.AccessURLReq{Name: name}) if err != nil { if errs.ErrArgs.Is(err) { c.String(http.StatusBadRequest, err.Error()) diff --git a/internal/api/user.go b/internal/api/user.go index e99e17b00..4e3fb7f7f 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -19,10 +19,12 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/a2r" "github.com/OpenIMSDK/Open-IM-Server/pkg/apiresp" - "github.com/OpenIMSDK/Open-IM-Server/pkg/apistruct" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/tokenverify" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" + "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msggateway" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/user" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" ) @@ -62,17 +64,118 @@ func (u *UserApi) GetUsers(c *gin.Context) { } func (u *UserApi) GetUsersOnlineStatus(c *gin.Context) { - params := apistruct.ManagementSendMsgReq{} - if err := c.BindJSON(¶ms); err != nil { + var req msggateway.GetUsersOnlineStatusReq + if err := c.BindJSON(&req); err != nil { apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap()) return } - if !tokenverify.IsAppManagerUid(c) { - apiresp.GinError(c, errs.ErrNoPermission.Wrap("only app manager can send message")) + conns, err := u.Discov.GetConns(c, config.Config.RpcRegisterName.OpenImMessageGatewayName) + if err != nil { + apiresp.GinError(c, err) return } + + var wsResult []*msggateway.GetUsersOnlineStatusResp_SuccessResult + var respResult []*msggateway.GetUsersOnlineStatusResp_SuccessResult + flag := false + + //Online push message + for _, v := range conns { + msgClient := msggateway.NewMsgGatewayClient(v) + reply, err := msgClient.GetUsersOnlineStatus(c, &req) + if err != nil { + log.ZWarn(c, "GetUsersOnlineStatus rpc err", err) + continue + } else { + wsResult = append(wsResult, reply.SuccessResult...) + } + } + // 遍历 api 请求体中的 userIDs + for _, v1 := range req.UserIDs { + flag = false + res := new(msggateway.GetUsersOnlineStatusResp_SuccessResult) + // 遍历从各个网关中获取的在线结果 + for _, v2 := range wsResult { + // 如果匹配上说明在线,反之 + if v2.UserID == v1 { + flag = true + res.UserID = v1 + res.Status = constant.OnlineStatus + res.DetailPlatformStatus = append(res.DetailPlatformStatus, v2.DetailPlatformStatus...) + break + } + } + if !flag { + res.UserID = v1 + res.Status = constant.OnlineStatus + } + respResult = append(respResult, res) + } + apiresp.GinSuccess(c, respResult) } func (u *UserApi) UserRegisterCount(c *gin.Context) { a2r.Call(user.UserClient.UserRegisterCount, u.Client, c) } + +func (u *UserApi) GetUsersOnlineTokenDetail(c *gin.Context) { + var wsResult []*msggateway.GetUsersOnlineStatusResp_SuccessResult + var respResult []*msggateway.SingleDetail + flag := false + var req msggateway.GetUsersOnlineStatusReq + if err := c.BindJSON(&req); err != nil { + apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap()) + return + } + conns, err := u.Discov.GetConns(c, config.Config.RpcRegisterName.OpenImMessageGatewayName) + if err != nil { + apiresp.GinError(c, err) + return + } + //Online push message + for _, v := range conns { + msgClient := msggateway.NewMsgGatewayClient(v) + reply, err := msgClient.GetUsersOnlineStatus(c, &req) + if err != nil { + log.ZWarn(c, "GetUsersOnlineStatus rpc err", err) + continue + } else { + wsResult = append(wsResult, reply.SuccessResult...) + } + } + + for _, v1 := range req.UserIDs { + m := make(map[string][]string, 10) + flag = false + temp := new(msggateway.SingleDetail) + for _, v2 := range wsResult { + if v2.UserID == v1 { + flag = true + temp.UserID = v1 + temp.Status = constant.OnlineStatus + for _, status := range v2.DetailPlatformStatus { + if v, ok := m[status.Platform]; ok { + m[status.Platform] = append(v, status.Token) + } else { + m[status.Platform] = []string{status.Token} + } + } + } + + } + for p, tokens := range m { + t := new(msggateway.SinglePlatformToken) + t.Platform = p + t.Token = tokens + t.Total = int32(len(tokens)) + temp.SinglePlatformToken = append(temp.SinglePlatformToken, t) + } + + if flag { + respResult = append(respResult, temp) + } + } + + apiresp.GinSuccess(c, respResult) + +} diff --git a/internal/msggateway/client.go b/internal/msggateway/client.go index 439a98fee..ec3e08d7c 100644 --- a/internal/msggateway/client.go +++ b/internal/msggateway/client.go @@ -21,14 +21,13 @@ import ( "runtime/debug" "sync" - "google.golang.org/protobuf/proto" - "github.com/OpenIMSDK/Open-IM-Server/pkg/apiresp" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" + "google.golang.org/protobuf/proto" ) var ErrConnClosed = errors.New("conn has closed") @@ -68,6 +67,7 @@ type Client struct { longConnServer LongConnServer closed bool closedErr error + token string } func newClient(ctx *UserConnContext, conn LongConn, isCompress bool) *Client { @@ -80,13 +80,7 @@ func newClient(ctx *UserConnContext, conn LongConn, isCompress bool) *Client { ctx: ctx, } } - -func (c *Client) ResetClient( - ctx *UserConnContext, - conn LongConn, - isBackground, isCompress bool, - longConnServer LongConnServer, -) { +func (c *Client) ResetClient(ctx *UserConnContext, conn LongConn, isBackground, isCompress bool, longConnServer LongConnServer, token string) { c.w = new(sync.Mutex) c.conn = conn c.PlatformID = utils.StringToInt(ctx.GetPlatformID()) @@ -98,6 +92,7 @@ func (c *Client) ResetClient( c.IsBackground = false c.closed = false c.closedErr = nil + c.token = token } func (c *Client) pongHandler(_ string) error { c.conn.SetReadDeadline(pongWait) @@ -166,9 +161,7 @@ func (c *Client) handleMessage(message []byte) error { if binaryReq.SendID != c.UserID { return utils.Wrap(errors.New("exception conn userID not same to req userID"), binaryReq.String()) } - ctx := mcontext.WithMustInfoCtx( - []string{binaryReq.OperationID, binaryReq.SendID, constant.PlatformIDToName(c.PlatformID), c.ctx.GetConnID()}, - ) + ctx := mcontext.WithMustInfoCtx([]string{binaryReq.OperationID, binaryReq.SendID, constant.PlatformIDToName(c.PlatformID), c.ctx.GetConnID()}) log.ZDebug(ctx, "gateway req message", "req", binaryReq.String()) var messageErr error var resp []byte @@ -186,12 +179,7 @@ func (c *Client) handleMessage(message []byte) error { case WsSetBackgroundStatus: resp, messageErr = c.setAppBackgroundStatus(ctx, binaryReq) default: - return fmt.Errorf( - "ReqIdentifier failed,sendID:%s,msgIncr:%s,reqIdentifier:%d", - binaryReq.SendID, - binaryReq.MsgIncr, - binaryReq.ReqIdentifier, - ) + return fmt.Errorf("ReqIdentifier failed,sendID:%s,msgIncr:%s,reqIdentifier:%d", binaryReq.SendID, binaryReq.MsgIncr, binaryReq.ReqIdentifier) } c.replyMessage(ctx, &binaryReq, messageErr, resp) return nil diff --git a/internal/msggateway/hub_server.go b/internal/msggateway/hub_server.go index a7d571e20..ea7b9fd8d 100644 --- a/internal/msggateway/hub_server.go +++ b/internal/msggateway/hub_server.go @@ -101,6 +101,7 @@ func (s *Server) GetUsersOnlineStatus( ps.Platform = constant.PlatformIDToName(client.PlatformID) ps.Status = constant.OnlineStatus ps.ConnID = client.ctx.GetConnID() + ps.Token = client.token ps.IsBackground = client.IsBackground temp.Status = constant.OnlineStatus temp.DetailPlatformStatus = append(temp.DetailPlatformStatus, ps) @@ -179,11 +180,14 @@ func (s *Server) KickUserOffline( for _, v := range req.KickUserIDList { if clients, _, ok := s.LongConnServer.GetUserPlatformCons(v, int(req.PlatformID)); ok { for _, client := range clients { + log.ZDebug(ctx, "kick user offline", "userID", v, "platformID", req.PlatformID, "client", client) err := client.KickOnlineMessage() if err != nil { return nil, err } } + } else { + log.ZWarn(ctx, "conn not exist", nil, "userID", v, "platformID", req.PlatformID) } } return &msggateway.KickUserOfflineResp{}, nil diff --git a/internal/msggateway/n_ws_server.go b/internal/msggateway/n_ws_server.go index 53469fca8..c204dc115 100644 --- a/internal/msggateway/n_ws_server.go +++ b/internal/msggateway/n_ws_server.go @@ -381,7 +381,7 @@ func (ws *WsServer) wsHandler(w http.ResponseWriter, r *http.Request) { } } client := ws.clientPool.Get().(*Client) - client.ResetClient(connContext, wsLongConn, connContext.GetBackground(), compression, ws) + client.ResetClient(connContext, wsLongConn, connContext.GetBackground(), compression, ws, token) ws.registerChan <- client go client.readMessage() } diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go index 946048f0c..1f166d743 100644 --- a/internal/msgtransfer/init.go +++ b/internal/msgtransfer/init.go @@ -1,17 +1,3 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package msgtransfer import ( @@ -19,28 +5,26 @@ import ( "sync" "time" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/controller" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/relation" relationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/tx" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/unrelation" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mw" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/prome" openKeeper "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry/zookeeper" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" ) type MsgTransfer struct { persistentCH *PersistentConsumerHandler // 聊天记录持久化到mysql的消费者 订阅的topic: ws2ms_chat historyCH *OnlineHistoryRedisConsumerHandler // 这个消费者聚合消息, 订阅的topic:ws2ms_chat, 修改通知发往msg_to_modify topic, 消息存入redis后Incr Redis, 再发消息到ms2pschat topic推送, 发消息到msg_to_mongo topic持久化 historyMongoCH *OnlineHistoryMongoConsumerHandler // mongoDB批量插入, 成功后删除redis中消息,以及处理删除通知消息删除的 订阅的topic: msg_to_mongo - modifyCH *ModifyMsgConsumerHandler // 负责消费修改消息通知的consumer, 订阅的topic: msg_to_modify + // modifyCH *ModifyMsgConsumerHandler // 负责消费修改消息通知的consumer, 订阅的topic: msg_to_modify } func StartTransfer(prometheusPort int) error { @@ -62,18 +46,9 @@ func StartTransfer(prometheusPort int) error { if err := mongo.CreateMsgIndex(); err != nil { return err } - client, err := openKeeper.NewClient( - config.Config.Zookeeper.ZkAddr, - config.Config.Zookeeper.Schema, - openKeeper.WithFreq( - time.Hour, - ), - openKeeper.WithRoundRobin(), - openKeeper.WithUserNameAndPassword(config.Config.Zookeeper.Username, - config.Config.Zookeeper.Password), - openKeeper.WithTimeout(10), - openKeeper.WithLogger(log.NewZkLogger()), - ) + client, err := openKeeper.NewClient(config.Config.Zookeeper.ZkAddr, config.Config.Zookeeper.Schema, + openKeeper.WithFreq(time.Hour), openKeeper.WithRoundRobin(), openKeeper.WithUserNameAndPassword(config.Config.Zookeeper.Username, + config.Config.Zookeeper.Password), openKeeper.WithTimeout(10), openKeeper.WithLogger(log.NewZkLogger())) if err != nil { return err } @@ -83,35 +58,20 @@ func StartTransfer(prometheusPort int) error { client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials())) msgModel := cache.NewMsgCacheModel(rdb) msgDocModel := unrelation.NewMsgMongoDriver(mongo.GetDatabase()) - extendMsgModel := unrelation.NewExtendMsgSetMongoDriver(mongo.GetDatabase()) - extendMsgCache := cache.NewExtendMsgSetCacheRedis(rdb, extendMsgModel, cache.GetDefaultOpt()) chatLogDatabase := controller.NewChatLogDatabase(relation.NewChatLogGorm(db)) - extendMsgDatabase := controller.NewExtendMsgDatabase(extendMsgModel, extendMsgCache, tx.NewMongo(mongo.GetClient())) msgDatabase := controller.NewCommonMsgDatabase(msgDocModel, msgModel) conversationRpcClient := rpcclient.NewConversationRpcClient(client) groupRpcClient := rpcclient.NewGroupRpcClient(client) - msgTransfer := NewMsgTransfer( - chatLogDatabase, - extendMsgDatabase, - msgDatabase, - &conversationRpcClient, - &groupRpcClient, - ) + msgTransfer := NewMsgTransfer(chatLogDatabase, msgDatabase, &conversationRpcClient, &groupRpcClient) msgTransfer.initPrometheus() return msgTransfer.Start(prometheusPort) } func NewMsgTransfer(chatLogDatabase controller.ChatLogDatabase, - extendMsgDatabase controller.ExtendMsgDatabase, msgDatabase controller.CommonMsgDatabase, + msgDatabase controller.CommonMsgDatabase, conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient) *MsgTransfer { - return &MsgTransfer{ - persistentCH: NewPersistentConsumerHandler(chatLogDatabase), - historyCH: NewOnlineHistoryRedisConsumerHandler(msgDatabase, conversationRpcClient, groupRpcClient), - historyMongoCH: NewOnlineHistoryMongoConsumerHandler( - msgDatabase, - ), - modifyCH: NewModifyMsgConsumerHandler(extendMsgDatabase), - } + return &MsgTransfer{persistentCH: NewPersistentConsumerHandler(chatLogDatabase), historyCH: NewOnlineHistoryRedisConsumerHandler(msgDatabase, conversationRpcClient, groupRpcClient), + historyMongoCH: NewOnlineHistoryMongoConsumerHandler(msgDatabase)} } func (m *MsgTransfer) initPrometheus() { @@ -136,7 +96,7 @@ func (m *MsgTransfer) Start(prometheusPort int) error { } go m.historyCH.historyConsumerGroup.RegisterHandleAndConsumer(m.historyCH) go m.historyMongoCH.historyConsumerGroup.RegisterHandleAndConsumer(m.historyMongoCH) - go m.modifyCH.modifyMsgConsumerGroup.RegisterHandleAndConsumer(m.modifyCH) + // go m.modifyCH.modifyMsgConsumerGroup.RegisterHandleAndConsumer(m.modifyCH) err := prome.StartPrometheusSrv(prometheusPort) if err != nil { return err diff --git a/internal/msgtransfer/modify_msg_handler.go b/internal/msgtransfer/modify_msg_handler.go deleted file mode 100644 index 38a2f5fce..000000000 --- a/internal/msgtransfer/modify_msg_handler.go +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package msgtransfer - -import ( - "context" - "encoding/json" - - "github.com/Shopify/sarama" - - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/controller" - unRelationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" - kfk "github.com/OpenIMSDK/Open-IM-Server/pkg/common/kafka" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" - pbMsg "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" - "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" - "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" - - "google.golang.org/protobuf/proto" -) - -type ModifyMsgConsumerHandler struct { - modifyMsgConsumerGroup *kfk.MConsumerGroup - - extendMsgDatabase controller.ExtendMsgDatabase - extendSetMsgModel unRelationTb.ExtendMsgSetModel -} - -func NewModifyMsgConsumerHandler(database controller.ExtendMsgDatabase) *ModifyMsgConsumerHandler { - return &ModifyMsgConsumerHandler{ - modifyMsgConsumerGroup: kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{KafkaVersion: sarama.V2_0_0_0, - OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false}, []string{config.Config.Kafka.MsgToModify.Topic}, - config.Config.Kafka.Addr, config.Config.Kafka.ConsumerGroupID.MsgToModify), - extendMsgDatabase: database, - } -} - -func (ModifyMsgConsumerHandler) Setup(_ sarama.ConsumerGroupSession) error { return nil } -func (ModifyMsgConsumerHandler) Cleanup(_ sarama.ConsumerGroupSession) error { return nil } -func (mmc *ModifyMsgConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession, - claim sarama.ConsumerGroupClaim) error { - for msg := range claim.Messages() { - ctx := mmc.modifyMsgConsumerGroup.GetContextFromMsg(msg) - log.ZDebug( - ctx, - "kafka get info to mysql", - "ModifyMsgConsumerHandler", - msg.Topic, - "msgPartition", - msg.Partition, - "msg", - string(msg.Value), - "key", - string(msg.Key), - ) - if len(msg.Value) != 0 { - mmc.ModifyMsg(ctx, msg, string(msg.Key), sess) - } else { - log.ZError(ctx, "msg get from kafka but is nil", nil, "key", msg.Key) - } - sess.MarkMessage(msg, "") - } - return nil -} - -func (mmc *ModifyMsgConsumerHandler) ModifyMsg( - ctx context.Context, - cMsg *sarama.ConsumerMessage, - msgKey string, - _ sarama.ConsumerGroupSession, -) { - msgFromMQ := pbMsg.MsgDataToModifyByMQ{} - operationID := mcontext.GetOperationID(ctx) - err := proto.Unmarshal(cMsg.Value, &msgFromMQ) - if err != nil { - log.ZError(ctx, "msg_transfer Unmarshal msg err", err, "msg", string(cMsg.Value)) - return - } - log.ZDebug(ctx, "proto.Unmarshal MsgDataToMQ", "msgs", msgFromMQ.String()) - for _, msg := range msgFromMQ.Messages { - isReactionFromCache := utils.GetSwitchFromOptions(msg.Options, constant.IsReactionFromCache) - if !isReactionFromCache { - continue - } - ctx = mcontext.SetOperationID(ctx, operationID) - if msg.ContentType == constant.ReactionMessageModifier { - notification := &sdkws.ReactionMessageModifierNotification{} - if err := json.Unmarshal(msg.Content, notification); err != nil { - continue - } - if notification.IsExternalExtensions { - continue - } - if !notification.IsReact { - // first time to modify - var reactionExtensionList = make(map[string]unRelationTb.KeyValueModel) - extendMsg := unRelationTb.ExtendMsgModel{ - ReactionExtensionList: reactionExtensionList, - ClientMsgID: notification.ClientMsgID, - MsgFirstModifyTime: notification.MsgFirstModifyTime, - } - for _, v := range notification.SuccessReactionExtensions { - reactionExtensionList[v.TypeKey] = unRelationTb.KeyValueModel{ - TypeKey: v.TypeKey, - Value: v.Value, - LatestUpdateTime: v.LatestUpdateTime, - } - } - - if err := mmc.extendMsgDatabase.InsertExtendMsg(ctx, notification.ConversationID, notification.SessionType, &extendMsg); err != nil { - // log.ZError(ctx, "MsgFirstModify InsertExtendMsg failed", notification.ConversationID, - // notification.SessionType, extendMsg, err.Error()) - continue - } - } else { - if err := mmc.extendMsgDatabase.InsertOrUpdateReactionExtendMsgSet(ctx, notification.ConversationID, notification.SessionType, notification.ClientMsgID, notification.MsgFirstModifyTime, mmc.extendSetMsgModel.Pb2Model(notification.SuccessReactionExtensions)); err != nil { - // log.NewError(operationID, "InsertOrUpdateReactionExtendMsgSet failed") - } - } - } else if msg.ContentType == constant.ReactionMessageDeleter { - notification := &sdkws.ReactionMessageDeleteNotification{} - if err := json.Unmarshal(msg.Content, notification); err != nil { - continue - } - if err := mmc.extendMsgDatabase.DeleteReactionExtendMsgSet(ctx, notification.ConversationID, notification.SessionType, notification.ClientMsgID, notification.MsgFirstModifyTime, mmc.extendSetMsgModel.Pb2Model(notification.SuccessReactionExtensions)); err != nil { - // log.NewError(operationID, "InsertOrUpdateReactionExtendMsgSet failed") - } - } - } -} diff --git a/internal/push/push_to_client.go b/internal/push/push_to_client.go index 7cc5b1ea4..607d862e1 100644 --- a/internal/push/push_to_client.go +++ b/internal/push/push_to_client.go @@ -1,17 +1,3 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package push import ( @@ -52,16 +38,9 @@ type Pusher struct { var errNoOfflinePusher = errors.New("no offlinePusher is configured") -func NewPusher( - discov discoveryregistry.SvcDiscoveryRegistry, - offlinePusher offlinepush.OfflinePusher, - database controller.PushDatabase, - groupLocalCache *localcache.GroupLocalCache, - conversationLocalCache *localcache.ConversationLocalCache, - conversationRpcClient *rpcclient.ConversationRpcClient, - groupRpcClient *rpcclient.GroupRpcClient, - msgRpcClient *rpcclient.MessageRpcClient, -) *Pusher { +func NewPusher(discov discoveryregistry.SvcDiscoveryRegistry, offlinePusher offlinepush.OfflinePusher, database controller.PushDatabase, + groupLocalCache *localcache.GroupLocalCache, conversationLocalCache *localcache.ConversationLocalCache, + conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient, msgRpcClient *rpcclient.MessageRpcClient) *Pusher { return &Pusher{ discov: discov, database: database, @@ -108,18 +87,7 @@ func (p *Pusher) Push2User(ctx context.Context, userIDs []string, msg *sdkws.Msg return err } isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush) - log.ZDebug( - ctx, - "push_result", - "ws push result", - wsResults, - "sendData", - msg, - "isOfflinePush", - isOfflinePush, - "push_to_userID", - userIDs, - ) + log.ZDebug(ctx, "push_result", "ws push result", wsResults, "sendData", msg, "isOfflinePush", isOfflinePush, "push_to_userID", userIDs) p.successCount++ for _, userID := range userIDs { if isOfflinePush && userID != msg.SendID { @@ -170,15 +138,7 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws } defer func(groupID string, userIDs []string) { if err := p.DeleteMemberAndSetConversationSeq(ctx, groupID, userIDs); err != nil { - log.ZError( - ctx, - "MemberQuitNotification DeleteMemberAndSetConversationSeq", - err, - "groupID", - groupID, - "userIDs", - userIDs, - ) + log.ZError(ctx, "MemberQuitNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", userIDs) } }(groupID, []string{tips.QuitUser.UserID}) pushToUserIDs = append(pushToUserIDs, tips.QuitUser.UserID) @@ -187,21 +147,10 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws if p.UnmarshalNotificationElem(msg.Content, &tips) != nil { return err } - kickedUsers := utils.Slice( - tips.KickedUserList, - func(e *sdkws.GroupMemberFullInfo) string { return e.UserID }, - ) + kickedUsers := utils.Slice(tips.KickedUserList, func(e *sdkws.GroupMemberFullInfo) string { return e.UserID }) defer func(groupID string, userIDs []string) { if err := p.DeleteMemberAndSetConversationSeq(ctx, groupID, userIDs); err != nil { - log.ZError( - ctx, - "MemberKickedNotification DeleteMemberAndSetConversationSeq", - err, - "groupID", - groupID, - "userIDs", - userIDs, - ) + log.ZError(ctx, "MemberKickedNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", userIDs) } }(groupID, kickedUsers) pushToUserIDs = append(pushToUserIDs, kickedUsers...) @@ -211,16 +160,7 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws if p.UnmarshalNotificationElem(msg.Content, &tips) != nil { return err } - log.ZInfo( - ctx, - "GroupDismissedNotificationInfo****", - "groupID", - groupID, - "num", - len(pushToUserIDs), - "list", - pushToUserIDs, - ) + log.ZInfo(ctx, "GroupDismissedNotificationInfo****", "groupID", groupID, "num", len(pushToUserIDs), "list", pushToUserIDs) if len(config.Config.Manager.UserID) > 0 { ctx = mcontext.WithOpUserIDContext(ctx, config.Config.Manager.UserID[0]) } @@ -284,23 +224,9 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg) return err } - _, err := p.GetConnsAndOnlinePush( - ctx, - msg, - utils.IntersectString(needOfflinePushUserIDs, WebAndPcBackgroundUserIDs), - ) + _, err := p.GetConnsAndOnlinePush(ctx, msg, utils.IntersectString(needOfflinePushUserIDs, WebAndPcBackgroundUserIDs)) if err != nil { - log.ZError( - ctx, - "offlinePushMsg failed", - err, - "groupID", - groupID, - "msg", - msg, - "userIDs", - utils.IntersectString(needOfflinePushUserIDs, WebAndPcBackgroundUserIDs), - ) + log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg, "userIDs", utils.IntersectString(needOfflinePushUserIDs, WebAndPcBackgroundUserIDs)) return err } } @@ -308,11 +234,7 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws return nil } -func (p *Pusher) GetConnsAndOnlinePush( - ctx context.Context, - msg *sdkws.MsgData, - pushToUserIDs []string, -) (wsResults []*msggateway.SingleMsgToUserResults, err error) { +func (p *Pusher) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) { conns, err := p.discov.GetConns(ctx, config.Config.RpcRegisterName.OpenImMessageGatewayName) log.ZDebug(ctx, "get gateway conn", "conn length", len(conns)) if err != nil { @@ -321,11 +243,7 @@ func (p *Pusher) GetConnsAndOnlinePush( //Online push message for _, v := range conns { msgClient := msggateway.NewMsgGatewayClient(v) - reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg( - ctx, - &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: pushToUserIDs}, - ) - p.discov.CloseConn(v) + reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: pushToUserIDs}) if err != nil { continue } @@ -338,12 +256,7 @@ func (p *Pusher) GetConnsAndOnlinePush( return wsResults, nil } -func (p *Pusher) offlinePushMsg( - ctx context.Context, - conversationID string, - msg *sdkws.MsgData, - offlinePushUserIDs []string, -) error { +func (p *Pusher) offlinePushMsg(ctx context.Context, conversationID string, msg *sdkws.MsgData, offlinePushUserIDs []string) error { title, content, opts, err := p.getOfflinePushInfos(conversationID, msg) if err != nil { return err @@ -377,10 +290,7 @@ func (p *Pusher) GetOfflinePushOpts(msg *sdkws.MsgData) (opts *offlinepush.Opts, return opts, nil } -func (p *Pusher) getOfflinePushInfos( - conversationID string, - msg *sdkws.MsgData, -) (title, content string, opts *offlinepush.Opts, err error) { +func (p *Pusher) getOfflinePushInfos(conversationID string, msg *sdkws.MsgData) (title, content string, opts *offlinepush.Opts, err error) { if p.offlinePusher == nil { err = errNoOfflinePusher return diff --git a/internal/rpc/auth/auth.go b/internal/rpc/auth/auth.go index 9376a5ccc..48e21fbce 100644 --- a/internal/rpc/auth/auth.go +++ b/internal/rpc/auth/auth.go @@ -60,7 +60,7 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e func (s *authServer) UserToken(ctx context.Context, req *pbAuth.UserTokenReq) (*pbAuth.UserTokenResp, error) { resp := pbAuth.UserTokenResp{} if req.Secret != config.Config.Secret { - return nil, errs.ErrIdentity.Wrap("secret invalid") + return nil, errs.ErrNoPermission.Wrap("secret invalid") } if _, err := s.userRpcClient.GetUserInfo(ctx, req.UserID); err != nil { return nil, err @@ -115,14 +115,13 @@ func (s *authServer) ParseToken( } func (s *authServer) ForceLogout(ctx context.Context, req *pbAuth.ForceLogoutReq) (*pbAuth.ForceLogoutResp, error) { - resp := pbAuth.ForceLogoutResp{} if err := tokenverify.CheckAdmin(ctx); err != nil { return nil, err } if err := s.forceKickOff(ctx, req.UserID, req.PlatformID, mcontext.GetOperationID(ctx)); err != nil { return nil, err } - return &resp, nil + return &pbAuth.ForceLogoutResp{}, nil } func (s *authServer) forceKickOff(ctx context.Context, userID string, platformID int32, operationID string) error { @@ -134,8 +133,7 @@ func (s *authServer) forceKickOff(ctx context.Context, userID string, platformID client := msggateway.NewMsgGatewayClient(v) kickReq := &msggateway.KickUserOfflineReq{KickUserIDList: []string{userID}, PlatformID: platformID} _, err := client.KickUserOffline(ctx, kickReq) - s.RegisterCenter.CloseConn(v) return utils.Wrap(err, "") } - return errs.ErrInternalServer.Wrap() + return nil } diff --git a/internal/rpc/conversation/conversaion.go b/internal/rpc/conversation/conversaion.go index 640c09991..1f45d0224 100644 --- a/internal/rpc/conversation/conversaion.go +++ b/internal/rpc/conversation/conversaion.go @@ -1,24 +1,8 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package conversation import ( "context" - "google.golang.org/grpc" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/convert" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" @@ -33,6 +17,7 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient/notification" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" + "google.golang.org/grpc" ) type conversationServer struct { @@ -59,19 +44,12 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e pbConversation.RegisterConversationServer(server, &conversationServer{ conversationNotificationSender: notification.NewConversationNotificationSender(&msgRpcClient), groupRpcClient: &groupRpcClient, - conversationDatabase: controller.NewConversationDatabase( - conversationDB, - cache.NewConversationRedis(rdb, cache.GetDefaultOpt(), conversationDB), - tx.NewGorm(db), - ), + conversationDatabase: controller.NewConversationDatabase(conversationDB, cache.NewConversationRedis(rdb, cache.GetDefaultOpt(), conversationDB), tx.NewGorm(db)), }) return nil } -func (c *conversationServer) GetConversation( - ctx context.Context, - req *pbConversation.GetConversationReq, -) (*pbConversation.GetConversationResp, error) { +func (c *conversationServer) GetConversation(ctx context.Context, req *pbConversation.GetConversationReq) (*pbConversation.GetConversationResp, error) { conversations, err := c.conversationDatabase.FindConversations(ctx, req.OwnerUserID, []string{req.ConversationID}) if err != nil { return nil, err @@ -84,10 +62,7 @@ func (c *conversationServer) GetConversation( return resp, nil } -func (c *conversationServer) GetAllConversations( - ctx context.Context, - req *pbConversation.GetAllConversationsReq, -) (*pbConversation.GetAllConversationsResp, error) { +func (c *conversationServer) GetAllConversations(ctx context.Context, req *pbConversation.GetAllConversationsReq) (*pbConversation.GetAllConversationsResp, error) { conversations, err := c.conversationDatabase.GetUserAllConversation(ctx, req.OwnerUserID) if err != nil { return nil, err @@ -97,10 +72,7 @@ func (c *conversationServer) GetAllConversations( return resp, nil } -func (c *conversationServer) GetConversations( - ctx context.Context, - req *pbConversation.GetConversationsReq, -) (*pbConversation.GetConversationsResp, error) { +func (c *conversationServer) GetConversations(ctx context.Context, req *pbConversation.GetConversationsReq) (*pbConversation.GetConversationsResp, error) { conversations, err := c.conversationDatabase.FindConversations(ctx, req.OwnerUserID, req.ConversationIDs) if err != nil { return nil, err @@ -110,32 +82,12 @@ func (c *conversationServer) GetConversations( return resp, nil } -func (c *conversationServer) BatchSetConversations( - ctx context.Context, - req *pbConversation.BatchSetConversationsReq, -) (*pbConversation.BatchSetConversationsResp, error) { - conversations := convert.ConversationsPb2DB(req.Conversations) - err := c.conversationDatabase.SetUserConversations(ctx, req.OwnerUserID, conversations) - if err != nil { - return nil, err - } - _ = c.conversationNotificationSender.ConversationChangeNotification(ctx, req.OwnerUserID) - return &pbConversation.BatchSetConversationsResp{}, nil -} - -func (c *conversationServer) SetConversation( - ctx context.Context, - req *pbConversation.SetConversationReq, -) (*pbConversation.SetConversationResp, error) { +func (c *conversationServer) SetConversation(ctx context.Context, req *pbConversation.SetConversationReq) (*pbConversation.SetConversationResp, error) { var conversation tableRelation.ConversationModel if err := utils.CopyStructFields(&conversation, req.Conversation); err != nil { return nil, err } - err := c.conversationDatabase.SetUserConversations( - ctx, - req.Conversation.OwnerUserID, - []*tableRelation.ConversationModel{&conversation}, - ) + err := c.conversationDatabase.SetUserConversations(ctx, req.Conversation.OwnerUserID, []*tableRelation.ConversationModel{&conversation}) if err != nil { return nil, err } @@ -144,79 +96,7 @@ func (c *conversationServer) SetConversation( return resp, nil } -func (c *conversationServer) SetRecvMsgOpt( - ctx context.Context, - req *pbConversation.SetRecvMsgOptReq, -) (*pbConversation.SetRecvMsgOptResp, error) { - if err := c.conversationDatabase.SetUsersConversationFiledTx(ctx, []string{req.OwnerUserID}, &tableRelation.ConversationModel{OwnerUserID: req.OwnerUserID, ConversationID: req.ConversationID, RecvMsgOpt: req.RecvMsgOpt}, map[string]interface{}{"recv_msg_opt": req.RecvMsgOpt}); err != nil { - return nil, err - } - _ = c.conversationNotificationSender.ConversationChangeNotification(ctx, req.OwnerUserID) - return &pbConversation.SetRecvMsgOptResp{}, nil -} - -// deprecated -func (c *conversationServer) ModifyConversationField( - ctx context.Context, - req *pbConversation.ModifyConversationFieldReq, -) (*pbConversation.ModifyConversationFieldResp, error) { - resp := &pbConversation.ModifyConversationFieldResp{} - var err error - if req.Conversation.ConversationType == constant.GroupChatType { - groupInfo, err := c.groupRpcClient.GetGroupInfo(ctx, req.Conversation.GroupID) - if err != nil { - return nil, err - } - if groupInfo.Status == constant.GroupStatusDismissed && req.FieldType != constant.FieldUnread { - return nil, err - } - } - conversation := convert.ConversationPb2DB(req.Conversation) - if req.FieldType == constant.FieldIsPrivateChat { - err := c.conversationDatabase.SyncPeerUserPrivateConversationTx( - ctx, - []*tableRelation.ConversationModel{conversation}, - ) - if err != nil { - return nil, err - } - c.conversationNotificationSender.ConversationSetPrivateNotification( - ctx, - req.Conversation.OwnerUserID, - req.Conversation.UserID, - req.Conversation.IsPrivateChat, - ) - return resp, nil - } - filedMap := make(map[string]interface{}) - switch req.FieldType { - case constant.FieldRecvMsgOpt: - filedMap["recv_msg_opt"] = req.Conversation.RecvMsgOpt - case constant.FieldGroupAtType: - filedMap["group_at_type"] = req.Conversation.GroupAtType - case constant.FieldIsPinned: - filedMap["is_pinned"] = req.Conversation.IsPinned - case constant.FieldEx: - filedMap["ex"] = req.Conversation.Ex - case constant.FieldAttachedInfo: - filedMap["attached_info"] = req.Conversation.AttachedInfo - case constant.FieldBurnDuration: - filedMap["burn_duration"] = req.Conversation.BurnDuration - } - err = c.conversationDatabase.SetUsersConversationFiledTx(ctx, req.UserIDList, conversation, filedMap) - if err != nil { - return nil, err - } - for _, v := range req.UserIDList { - c.conversationNotificationSender.ConversationChangeNotification(ctx, v) - } - return resp, nil -} - -func (c *conversationServer) SetConversations( - ctx context.Context, - req *pbConversation.SetConversationsReq, -) (*pbConversation.SetConversationsResp, error) { +func (c *conversationServer) SetConversations(ctx context.Context, req *pbConversation.SetConversationsReq) (*pbConversation.SetConversationsResp, error) { if req.Conversation == nil { return nil, errs.ErrArgs.Wrap("conversation must not be nil") } @@ -228,6 +108,12 @@ func (c *conversationServer) SetConversations( if groupInfo.Status == constant.GroupStatusDismissed { return nil, err } + // for _, userID := range req.UserIDs { + // if _, err := c.groupRpcClient.GetGroupMemberCache(ctx, req.Conversation.GroupID, userID); err != nil { + // log.ZError(ctx, "user not in group", err, "userID", userID, "groupID", req.Conversation.GroupID) + // return nil, err + // } + // } } var conversation tableRelation.ConversationModel conversation.ConversationID = req.Conversation.ConversationID @@ -250,24 +136,25 @@ func (c *conversationServer) SetConversations( if req.Conversation.GroupAtType != nil { m["group_at_type"] = req.Conversation.GroupAtType.Value } - if req.Conversation.IsPrivateChat != nil { + if req.Conversation.MsgDestructTime != nil { + m["msg_destruct_time"] = req.Conversation.MsgDestructTime.Value + } + if req.Conversation.IsMsgDestruct != nil { + m["is_msg_destruct"] = req.Conversation.IsMsgDestruct.Value + } + if req.Conversation.IsPrivateChat != nil && req.Conversation.ConversationType != constant.SuperGroupChatType { var conversations []*tableRelation.ConversationModel for _, ownerUserID := range req.UserIDs { conversation2 := conversation - conversation.OwnerUserID = ownerUserID - conversation.IsPrivateChat = req.Conversation.IsPrivateChat.Value + conversation2.OwnerUserID = ownerUserID + conversation2.IsPrivateChat = req.Conversation.IsPrivateChat.Value conversations = append(conversations, &conversation2) } if err := c.conversationDatabase.SyncPeerUserPrivateConversationTx(ctx, conversations); err != nil { return nil, err } - for _, ownerUserID := range req.UserIDs { - c.conversationNotificationSender.ConversationSetPrivateNotification( - ctx, - ownerUserID, - req.Conversation.UserID, - req.Conversation.IsPrivateChat.Value, - ) + for _, userID := range req.UserIDs { + c.conversationNotificationSender.ConversationSetPrivateNotification(ctx, userID, req.Conversation.UserID, req.Conversation.IsPrivateChat.Value) } } if req.Conversation.BurnDuration != nil { @@ -284,10 +171,7 @@ func (c *conversationServer) SetConversations( } // 获取超级大群开启免打扰的用户ID -func (c *conversationServer) GetRecvMsgNotNotifyUserIDs( - ctx context.Context, - req *pbConversation.GetRecvMsgNotNotifyUserIDsReq, -) (*pbConversation.GetRecvMsgNotNotifyUserIDsResp, error) { +func (c *conversationServer) GetRecvMsgNotNotifyUserIDs(ctx context.Context, req *pbConversation.GetRecvMsgNotNotifyUserIDsReq) (*pbConversation.GetRecvMsgNotNotifyUserIDsResp, error) { userIDs, err := c.conversationDatabase.FindRecvMsgNotNotifyUserIDs(ctx, req.GroupID) if err != nil { return nil, err @@ -296,10 +180,7 @@ func (c *conversationServer) GetRecvMsgNotNotifyUserIDs( } // create conversation without notification for msg redis transfer -func (c *conversationServer) CreateSingleChatConversations( - ctx context.Context, - req *pbConversation.CreateSingleChatConversationsReq, -) (*pbConversation.CreateSingleChatConversationsResp, error) { +func (c *conversationServer) CreateSingleChatConversations(ctx context.Context, req *pbConversation.CreateSingleChatConversationsReq) (*pbConversation.CreateSingleChatConversationsResp, error) { var conversation tableRelation.ConversationModel conversation.ConversationID = utils.GetConversationIDBySessionType(constant.SingleChatType, req.RecvID, req.SendID) conversation.ConversationType = constant.SingleChatType @@ -320,10 +201,7 @@ func (c *conversationServer) CreateSingleChatConversations( return &pbConversation.CreateSingleChatConversationsResp{}, nil } -func (c *conversationServer) CreateGroupChatConversations( - ctx context.Context, - req *pbConversation.CreateGroupChatConversationsReq, -) (*pbConversation.CreateGroupChatConversationsResp, error) { +func (c *conversationServer) CreateGroupChatConversations(ctx context.Context, req *pbConversation.CreateGroupChatConversationsReq) (*pbConversation.CreateGroupChatConversationsResp, error) { err := c.conversationDatabase.CreateGroupChatConversation(ctx, req.GroupID, req.UserIDs) if err != nil { return nil, err @@ -331,10 +209,7 @@ func (c *conversationServer) CreateGroupChatConversations( return &pbConversation.CreateGroupChatConversationsResp{}, nil } -func (c *conversationServer) SetConversationMaxSeq( - ctx context.Context, - req *pbConversation.SetConversationMaxSeqReq, -) (*pbConversation.SetConversationMaxSeqResp, error) { +func (c *conversationServer) SetConversationMaxSeq(ctx context.Context, req *pbConversation.SetConversationMaxSeqReq) (*pbConversation.SetConversationMaxSeqResp, error) { if err := c.conversationDatabase.UpdateUsersConversationFiled(ctx, req.OwnerUserID, req.ConversationID, map[string]interface{}{"max_seq": req.MaxSeq}); err != nil { return nil, err @@ -342,10 +217,7 @@ func (c *conversationServer) SetConversationMaxSeq( return &pbConversation.SetConversationMaxSeqResp{}, nil } -func (c *conversationServer) GetConversationIDs( - ctx context.Context, - req *pbConversation.GetConversationIDsReq, -) (*pbConversation.GetConversationIDsResp, error) { +func (c *conversationServer) GetConversationIDs(ctx context.Context, req *pbConversation.GetConversationIDsReq) (*pbConversation.GetConversationIDsResp, error) { conversationIDs, err := c.conversationDatabase.GetConversationIDs(ctx, req.UserID) if err != nil { return nil, err @@ -353,10 +225,7 @@ func (c *conversationServer) GetConversationIDs( return &pbConversation.GetConversationIDsResp{ConversationIDs: conversationIDs}, nil } -func (c *conversationServer) GetUserConversationIDsHash( - ctx context.Context, - req *pbConversation.GetUserConversationIDsHashReq, -) (*pbConversation.GetUserConversationIDsHashResp, error) { +func (c *conversationServer) GetUserConversationIDsHash(ctx context.Context, req *pbConversation.GetUserConversationIDsHashReq) (*pbConversation.GetUserConversationIDsHashResp, error) { hash, err := c.conversationDatabase.GetUserConversationIDsHash(ctx, req.OwnerUserID) if err != nil { return nil, err @@ -364,15 +233,10 @@ func (c *conversationServer) GetUserConversationIDsHash( return &pbConversation.GetUserConversationIDsHashResp{Hash: hash}, nil } -func (c *conversationServer) GetConversationsByConversationID( - ctx context.Context, - req *pbConversation.GetConversationsByConversationIDReq, -) (*pbConversation.GetConversationsByConversationIDResp, error) { +func (c *conversationServer) GetConversationsByConversationID(ctx context.Context, req *pbConversation.GetConversationsByConversationIDReq) (*pbConversation.GetConversationsByConversationIDResp, error) { conversations, err := c.conversationDatabase.GetConversationsByConversationID(ctx, req.ConversationIDs) if err != nil { return nil, err } - return &pbConversation.GetConversationsByConversationIDResp{ - Conversations: convert.ConversationsDB2Pb(conversations), - }, nil + return &pbConversation.GetConversationsByConversationIDResp{Conversations: convert.ConversationsDB2Pb(conversations)}, nil } diff --git a/internal/rpc/group/db_map.go b/internal/rpc/group/db_map.go index 2ab1a75ea..84c0e74a4 100644 --- a/internal/rpc/group/db_map.go +++ b/internal/rpc/group/db_map.go @@ -15,19 +15,23 @@ package group import ( + "context" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" "time" pbGroup "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/group" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" ) -func UpdateGroupInfoMap(group *sdkws.GroupInfoForSet) map[string]any { +func UpdateGroupInfoMap(ctx context.Context, group *sdkws.GroupInfoForSet) map[string]any { m := make(map[string]any) if group.GroupName != "" { m["name"] = group.GroupName } if group.Notification != "" { - m["Notification"] = group.Notification + m["notification"] = group.Notification + m["notification_update_time"] = time.Now() + m["notification_user_id"] = mcontext.GetOpUserID(ctx) } if group.Introduction != "" { m["introduction"] = group.Introduction diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index 8daf45454..ec5d0bdb5 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -1,22 +1,10 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package group import ( "context" "fmt" + pbConversation "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/conversation" + "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/wrapperspb" "math/big" "math/rand" "strconv" @@ -29,8 +17,6 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mw/specialerror" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" - "google.golang.org/grpc" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/controller" @@ -45,6 +31,7 @@ import ( pbGroup "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/group" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" + "google.golang.org/grpc" ) func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { @@ -70,17 +57,13 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e pbGroup.RegisterGroupServer(server, &groupServer{ GroupDatabase: database, User: userRpcClient, - Notification: notification.NewGroupNotificationSender( - database, - &msgRpcClient, - func(ctx context.Context, userIDs []string) ([]notification.CommonUser, error) { - users, err := userRpcClient.GetUsersInfo(ctx, userIDs) - if err != nil { - return nil, err - } - return utils.Slice(users, func(e *sdkws.UserInfo) notification.CommonUser { return e }), nil - }, - ), + Notification: notification.NewGroupNotificationSender(database, &msgRpcClient, &userRpcClient, func(ctx context.Context, userIDs []string) ([]notification.CommonUser, error) { + users, err := userRpcClient.GetUsersInfo(ctx, userIDs) + if err != nil { + return nil, err + } + return utils.Slice(users, func(e *sdkws.UserInfo) notification.CommonUser { return e }), nil + }), conversationRpcClient: conversationRpcClient, msgRpcClient: msgRpcClient, }) @@ -137,16 +120,7 @@ func (s *groupServer) GenGroupID(ctx context.Context, groupID *string) error { } } for i := 0; i < 10; i++ { - id := utils.Md5( - strings.Join( - []string{ - mcontext.GetOperationID(ctx), - strconv.FormatInt(time.Now().UnixNano(), 10), - strconv.Itoa(rand.Int()), - }, - ",;,", - ), - ) + id := utils.Md5(strings.Join([]string{mcontext.GetOperationID(ctx), strconv.FormatInt(time.Now().UnixNano(), 10), strconv.Itoa(rand.Int())}, ",;,")) bi := big.NewInt(0) bi.SetString(id[0:8], 16) id = bi.String() @@ -260,10 +234,7 @@ func (s *groupServer) CreateGroup(ctx context.Context, req *pbGroup.CreateGroupR return resp, nil } -func (s *groupServer) GetJoinedGroupList( - ctx context.Context, - req *pbGroup.GetJoinedGroupListReq, -) (*pbGroup.GetJoinedGroupListResp, error) { +func (s *groupServer) GetJoinedGroupList(ctx context.Context, req *pbGroup.GetJoinedGroupListReq) (*pbGroup.GetJoinedGroupListResp, error) { resp := &pbGroup.GetJoinedGroupListResp{} if err := tokenverify.CheckAccessV3(ctx, req.FromUserID); err != nil { return nil, err @@ -273,8 +244,7 @@ func (s *groupServer) GetJoinedGroupList( pageNumber = req.Pagination.PageNumber showNumber = req.Pagination.ShowNumber } - // total, members, err := s.GroupDatabase.PageGroupMember(ctx, nil, []string{req.FromUserID}, nil, pageNumber, - // showNumber) + //total, members, err := s.GroupDatabase.PageGroupMember(ctx, nil, []string{req.FromUserID}, nil, pageNumber, showNumber) total, members, err := s.GroupDatabase.PageGetJoinGroup(ctx, req.FromUserID, pageNumber, showNumber) if err != nil { return nil, err @@ -313,10 +283,7 @@ func (s *groupServer) GetJoinedGroupList( return resp, nil } -func (s *groupServer) InviteUserToGroup( - ctx context.Context, - req *pbGroup.InviteUserToGroupReq, -) (*pbGroup.InviteUserToGroupResp, error) { +func (s *groupServer) InviteUserToGroup(ctx context.Context, req *pbGroup.InviteUserToGroupReq) (*pbGroup.InviteUserToGroupResp, error) { resp := &pbGroup.InviteUserToGroupResp{} if len(req.InvitedUserIDs) == 0 { return nil, errs.ErrArgs.Wrap("user empty") @@ -417,10 +384,7 @@ func (s *groupServer) InviteUserToGroup( return resp, nil } -func (s *groupServer) GetGroupAllMember( - ctx context.Context, - req *pbGroup.GetGroupAllMemberReq, -) (*pbGroup.GetGroupAllMemberResp, error) { +func (s *groupServer) GetGroupAllMember(ctx context.Context, req *pbGroup.GetGroupAllMemberReq) (*pbGroup.GetGroupAllMemberResp, error) { resp := &pbGroup.GetGroupAllMemberResp{} group, err := s.GroupDatabase.TakeGroup(ctx, req.GroupID) if err != nil { @@ -448,10 +412,7 @@ func (s *groupServer) GetGroupAllMember( return resp, nil } -func (s *groupServer) GetGroupMemberList( - ctx context.Context, - req *pbGroup.GetGroupMemberListReq, -) (*pbGroup.GetGroupMemberListResp, error) { +func (s *groupServer) GetGroupMemberList(ctx context.Context, req *pbGroup.GetGroupMemberListReq) (*pbGroup.GetGroupMemberListResp, error) { resp := &pbGroup.GetGroupMemberListResp{} total, members, err := s.PageGetGroupMember(ctx, req.GroupID, req.Pagination.PageNumber, req.Pagination.ShowNumber) log.ZDebug(ctx, "GetGroupMemberList", "total", total, "members", members, "length", len(members)) @@ -464,10 +425,7 @@ func (s *groupServer) GetGroupMemberList( return resp, nil } -func (s *groupServer) KickGroupMember( - ctx context.Context, - req *pbGroup.KickGroupMemberReq, -) (*pbGroup.KickGroupMemberResp, error) { +func (s *groupServer) KickGroupMember(ctx context.Context, req *pbGroup.KickGroupMemberReq) (*pbGroup.KickGroupMemberResp, error) { resp := &pbGroup.KickGroupMemberResp{} group, err := s.GroupDatabase.TakeGroup(ctx, req.GroupID) if err != nil { @@ -575,10 +533,7 @@ func (s *groupServer) KickGroupMember( return resp, nil } -func (s *groupServer) GetGroupMembersInfo( - ctx context.Context, - req *pbGroup.GetGroupMembersInfoReq, -) (*pbGroup.GetGroupMembersInfoResp, error) { +func (s *groupServer) GetGroupMembersInfo(ctx context.Context, req *pbGroup.GetGroupMembersInfoReq) (*pbGroup.GetGroupMembersInfoResp, error) { resp := &pbGroup.GetGroupMembersInfoResp{} if len(req.UserIDs) == 0 { return nil, errs.ErrArgs.Wrap("userIDs empty") @@ -605,10 +560,7 @@ func (s *groupServer) GetGroupMembersInfo( return resp, nil } -func (s *groupServer) GetGroupApplicationList( - ctx context.Context, - req *pbGroup.GetGroupApplicationListReq, -) (*pbGroup.GetGroupApplicationListResp, error) { +func (s *groupServer) GetGroupApplicationList(ctx context.Context, req *pbGroup.GetGroupApplicationListReq) (*pbGroup.GetGroupApplicationListResp, error) { pageNumber, showNumber := utils.GetPage(req.Pagination) groupIDs, err := s.GroupDatabase.FindUserManagedGroupID(ctx, req.FromUserID) @@ -659,19 +611,12 @@ func (s *groupServer) GetGroupApplicationList( return e.GroupID }) resp.GroupRequests = utils.Slice(groupRequests, func(e *relationTb.GroupRequestModel) *sdkws.GroupRequest { - return convert.Db2PbGroupRequest( - e, - userMap[e.UserID], - convert.Db2PbGroupInfo(groupMap[e.GroupID], ownerMap[e.GroupID].UserID, groupMemberNumMap[e.GroupID]), - ) + return convert.Db2PbGroupRequest(e, userMap[e.UserID], convert.Db2PbGroupInfo(groupMap[e.GroupID], ownerMap[e.GroupID].UserID, groupMemberNumMap[e.GroupID])) }) return resp, nil } -func (s *groupServer) GetGroupsInfo( - ctx context.Context, - req *pbGroup.GetGroupsInfoReq, -) (*pbGroup.GetGroupsInfoResp, error) { +func (s *groupServer) GetGroupsInfo(ctx context.Context, req *pbGroup.GetGroupsInfoReq) (*pbGroup.GetGroupsInfoResp, error) { resp := &pbGroup.GetGroupsInfoResp{} if len(req.GroupIDs) == 0 { return nil, errs.ErrArgs.Wrap("groupID is empty") @@ -701,10 +646,7 @@ func (s *groupServer) GetGroupsInfo( return resp, nil } -func (s *groupServer) GroupApplicationResponse( - ctx context.Context, - req *pbGroup.GroupApplicationResponseReq, -) (*pbGroup.GroupApplicationResponseResp, error) { +func (s *groupServer) GroupApplicationResponse(ctx context.Context, req *pbGroup.GroupApplicationResponseReq) (*pbGroup.GroupApplicationResponseResp, error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") if !utils.Contain(req.HandleResult, constant.GroupResponseAgree, constant.GroupResponseRefuse) { return nil, errs.ErrArgs.Wrap("HandleResult unknown") @@ -776,10 +718,7 @@ func (s *groupServer) GroupApplicationResponse( return &pbGroup.GroupApplicationResponseResp{}, nil } -func (s *groupServer) JoinGroup( - ctx context.Context, - req *pbGroup.JoinGroupReq, -) (resp *pbGroup.JoinGroupResp, err error) { +func (s *groupServer) JoinGroup(ctx context.Context, req *pbGroup.JoinGroupReq) (resp *pbGroup.JoinGroupResp, err error) { defer log.ZInfo(ctx, "JoinGroup.Return") user, err := s.User.GetUserInfo(ctx, req.InviterUserID) if err != nil { @@ -879,10 +818,7 @@ func (s *groupServer) deleteMemberAndSetConversationSeq(ctx context.Context, gro return s.conversationRpcClient.SetConversationMaxSeq(ctx, userIDs, conevrsationID, maxSeq) } -func (s *groupServer) SetGroupInfo( - ctx context.Context, - req *pbGroup.SetGroupInfoReq, -) (*pbGroup.SetGroupInfoResp, error) { +func (s *groupServer) SetGroupInfo(ctx context.Context, req *pbGroup.SetGroupInfoReq) (*pbGroup.SetGroupInfoResp, error) { var opMember *relationTb.GroupMemberModel if !tokenverify.IsAppManagerUid(ctx) { var err error @@ -910,7 +846,7 @@ func (s *groupServer) SetGroupInfo( if err != nil { return nil, err } - data := UpdateGroupInfoMap(req.GroupInfoForSet) + data := UpdateGroupInfoMap(ctx, req.GroupInfoForSet) if len(data) == 0 { return resp, nil } @@ -931,11 +867,25 @@ func (s *groupServer) SetGroupInfo( } var num int if req.GroupInfoForSet.Notification != "" { + go func() { + nctx := mcontext.NewCtx("@@@" + mcontext.GetOperationID(ctx)) + conversation := &pbConversation.ConversationReq{ + ConversationID: utils.GetConversationIDBySessionType(constant.SuperGroupChatType, req.GroupInfoForSet.GroupID), + ConversationType: constant.SuperGroupChatType, + GroupID: req.GroupInfoForSet.GroupID, + } + resp, err := s.GetGroupMemberUserIDs(nctx, &pbGroup.GetGroupMemberUserIDsReq{GroupID: req.GroupInfoForSet.GroupID}) + if err != nil { + log.ZWarn(ctx, "GetGroupMemberIDs", err) + return + } + conversation.GroupAtType = &wrapperspb.Int32Value{Value: constant.GroupNotification} + if err := s.conversationRpcClient.SetConversations(nctx, resp.UserIDs, conversation); err != nil { + log.ZWarn(ctx, "SetConversations", err, resp.UserIDs, conversation) + } + }() num++ - s.Notification.GroupInfoSetAnnouncementNotification( - ctx, - &sdkws.GroupInfoSetAnnouncementTips{Group: tips.Group, OpUser: tips.OpUser}, - ) + s.Notification.GroupInfoSetAnnouncementNotification(ctx, &sdkws.GroupInfoSetAnnouncementTips{Group: tips.Group, OpUser: tips.OpUser}) } switch len(data) - num { @@ -952,10 +902,7 @@ func (s *groupServer) SetGroupInfo( return resp, nil } -func (s *groupServer) TransferGroupOwner( - ctx context.Context, - req *pbGroup.TransferGroupOwnerReq, -) (*pbGroup.TransferGroupOwnerResp, error) { +func (s *groupServer) TransferGroupOwner(ctx context.Context, req *pbGroup.TransferGroupOwnerReq) (*pbGroup.TransferGroupOwnerResp, error) { resp := &pbGroup.TransferGroupOwnerResp{} group, err := s.GroupDatabase.TakeGroup(ctx, req.GroupID) if err != nil { @@ -1034,20 +981,9 @@ func (s *groupServer) GetGroups(ctx context.Context, req *pbGroup.GetGroupsReq) return resp, nil } -func (s *groupServer) GetGroupMembersCMS( - ctx context.Context, - req *pbGroup.GetGroupMembersCMSReq, -) (*pbGroup.GetGroupMembersCMSResp, error) { +func (s *groupServer) GetGroupMembersCMS(ctx context.Context, req *pbGroup.GetGroupMembersCMSReq) (*pbGroup.GetGroupMembersCMSResp, error) { resp := &pbGroup.GetGroupMembersCMSResp{} - total, members, err := s.GroupDatabase.SearchGroupMember( - ctx, - req.UserName, - []string{req.GroupID}, - nil, - nil, - req.Pagination.PageNumber, - req.Pagination.ShowNumber, - ) + total, members, err := s.GroupDatabase.SearchGroupMember(ctx, req.UserName, []string{req.GroupID}, nil, nil, req.Pagination.PageNumber, req.Pagination.ShowNumber) if err != nil { return nil, err } @@ -1067,10 +1003,7 @@ func (s *groupServer) GetGroupMembersCMS( return resp, nil } -func (s *groupServer) GetUserReqApplicationList( - ctx context.Context, - req *pbGroup.GetUserReqApplicationListReq, -) (*pbGroup.GetUserReqApplicationListResp, error) { +func (s *groupServer) GetUserReqApplicationList(ctx context.Context, req *pbGroup.GetUserReqApplicationListReq) (*pbGroup.GetUserReqApplicationListResp, error) { resp := &pbGroup.GetUserReqApplicationListResp{} user, err := s.User.GetPublicUserInfo(ctx, req.UserID) if err != nil { @@ -1117,19 +1050,12 @@ func (s *groupServer) GetUserReqApplicationList( return nil, err } resp.GroupRequests = utils.Slice(requests, func(e *relationTb.GroupRequestModel) *sdkws.GroupRequest { - return convert.Db2PbGroupRequest( - e, - user, - convert.Db2PbGroupInfo(groupMap[e.GroupID], ownerMap[e.GroupID].UserID, uint32(groupMemberNum[e.GroupID])), - ) + return convert.Db2PbGroupRequest(e, user, convert.Db2PbGroupInfo(groupMap[e.GroupID], ownerMap[e.GroupID].UserID, uint32(groupMemberNum[e.GroupID]))) }) return resp, nil } -func (s *groupServer) DismissGroup( - ctx context.Context, - req *pbGroup.DismissGroupReq, -) (*pbGroup.DismissGroupResp, error) { +func (s *groupServer) DismissGroup(ctx context.Context, req *pbGroup.DismissGroupReq) (*pbGroup.DismissGroupResp, error) { defer log.ZInfo(ctx, "DismissGroup.return") resp := &pbGroup.DismissGroupResp{} owner, err := s.TakeGroupOwner(ctx, req.GroupID) @@ -1178,10 +1104,7 @@ func (s *groupServer) DismissGroup( return resp, nil } -func (s *groupServer) MuteGroupMember( - ctx context.Context, - req *pbGroup.MuteGroupMemberReq, -) (*pbGroup.MuteGroupMemberResp, error) { +func (s *groupServer) MuteGroupMember(ctx context.Context, req *pbGroup.MuteGroupMemberReq) (*pbGroup.MuteGroupMemberResp, error) { resp := &pbGroup.MuteGroupMemberResp{} //if err := tokenverify.CheckAccessV3(ctx, req.UserID); err != nil { // return nil, err @@ -1216,10 +1139,7 @@ func (s *groupServer) MuteGroupMember( return resp, nil } -func (s *groupServer) CancelMuteGroupMember( - ctx context.Context, - req *pbGroup.CancelMuteGroupMemberReq, -) (*pbGroup.CancelMuteGroupMemberResp, error) { +func (s *groupServer) CancelMuteGroupMember(ctx context.Context, req *pbGroup.CancelMuteGroupMemberReq) (*pbGroup.CancelMuteGroupMemberResp, error) { resp := &pbGroup.CancelMuteGroupMemberResp{} //member, err := s.GroupDatabase.TakeGroupMember(ctx, req.GroupID, req.UserID) //if err != nil { @@ -1231,8 +1151,7 @@ func (s *groupServer) CancelMuteGroupMember( // return nil, err // } // if opMember.RoleLevel <= member.RoleLevel { - // return nil, errs.ErrNoPermission.Wrap(fmt.Sprintf("self RoleLevel %d target %d", opMember.RoleLevel, - // member.RoleLevel)) + // return nil, errs.ErrNoPermission.Wrap(fmt.Sprintf("self RoleLevel %d target %d", opMember.RoleLevel, member.RoleLevel)) // } //} //if err := tokenverify.CheckAccessV3(ctx, req.UserID); err != nil { @@ -1280,10 +1199,7 @@ func (s *groupServer) MuteGroup(ctx context.Context, req *pbGroup.MuteGroupReq) return resp, nil } -func (s *groupServer) CancelMuteGroup( - ctx context.Context, - req *pbGroup.CancelMuteGroupReq, -) (*pbGroup.CancelMuteGroupResp, error) { +func (s *groupServer) CancelMuteGroup(ctx context.Context, req *pbGroup.CancelMuteGroupReq) (*pbGroup.CancelMuteGroupResp, error) { resp := &pbGroup.CancelMuteGroupResp{} if err := s.CheckGroupAdmin(ctx, req.GroupID); err != nil { return nil, err @@ -1295,10 +1211,7 @@ func (s *groupServer) CancelMuteGroup( return resp, nil } -func (s *groupServer) SetGroupMemberInfo( - ctx context.Context, - req *pbGroup.SetGroupMemberInfoReq, -) (*pbGroup.SetGroupMemberInfoResp, error) { +func (s *groupServer) SetGroupMemberInfo(ctx context.Context, req *pbGroup.SetGroupMemberInfoReq) (*pbGroup.SetGroupMemberInfoResp, error) { resp := &pbGroup.SetGroupMemberInfoResp{} if len(req.Members) == 0 { return nil, errs.ErrArgs.Wrap("members empty") @@ -1325,11 +1238,9 @@ func (s *groupServer) SetGroupMemberInfo( delete(duplicateMap, [...]string{member.GroupID, member.UserID}) } if len(duplicateMap) > 0 { - return nil, errs.ErrArgs.Wrap( - "user not found" + strings.Join(utils.Slice(utils.Keys(duplicateMap), func(e [2]string) string { - return fmt.Sprintf("[group: %s user: %s]", e[0], e[1]) - }), ","), - ) + return nil, errs.ErrArgs.Wrap("user not found" + strings.Join(utils.Slice(utils.Keys(duplicateMap), func(e [2]string) string { + return fmt.Sprintf("[group: %s user: %s]", e[0], e[1]) + }), ",")) } memberMap := utils.SliceToMap(members, func(e *relationTb.GroupMemberModel) [2]string { return [...]string{e.GroupID, e.UserID} @@ -1359,9 +1270,7 @@ func (s *groupServer) SetGroupMemberInfo( } dbMember, ok := memberMap[[...]string{member.GroupID, member.UserID}] if !ok { - return nil, errs.ErrRecordNotFound.Wrap( - fmt.Sprintf("user %s not in group %s", member.UserID, member.GroupID), - ) + return nil, errs.ErrRecordNotFound.Wrap(fmt.Sprintf("user %s not in group %s", member.UserID, member.GroupID)) } //if opMember.RoleLevel == constant.GroupOwner { // continue @@ -1423,25 +1332,14 @@ func (s *groupServer) SetGroupMemberInfo( if member.Nickname != nil || member.FaceURL != nil || member.Ex != nil { log.ZDebug(ctx, "setGroupMemberInfo notification", "member", member.UserID) if err := s.Notification.GroupMemberInfoSetNotification(ctx, member.GroupID, member.UserID); err != nil { - log.ZError( - ctx, - "setGroupMemberInfo notification failed", - err, - "member", - member.UserID, - "groupID", - member.GroupID, - ) + log.ZError(ctx, "setGroupMemberInfo notification failed", err, "member", member.UserID, "groupID", member.GroupID) } } } return resp, nil } -func (s *groupServer) GetGroupAbstractInfo( - ctx context.Context, - req *pbGroup.GetGroupAbstractInfoReq, -) (*pbGroup.GetGroupAbstractInfoResp, error) { +func (s *groupServer) GetGroupAbstractInfo(ctx context.Context, req *pbGroup.GetGroupAbstractInfoReq) (*pbGroup.GetGroupAbstractInfoResp, error) { resp := &pbGroup.GetGroupAbstractInfoResp{} if len(req.GroupIDs) == 0 { return nil, errs.ErrArgs.Wrap("groupIDs empty") @@ -1472,10 +1370,7 @@ func (s *groupServer) GetGroupAbstractInfo( return resp, nil } -func (s *groupServer) GetUserInGroupMembers( - ctx context.Context, - req *pbGroup.GetUserInGroupMembersReq, -) (*pbGroup.GetUserInGroupMembersResp, error) { +func (s *groupServer) GetUserInGroupMembers(ctx context.Context, req *pbGroup.GetUserInGroupMembersReq) (*pbGroup.GetUserInGroupMembersResp, error) { resp := &pbGroup.GetUserInGroupMembersResp{} if len(req.GroupIDs) == 0 { return nil, errs.ErrArgs.Wrap("groupIDs empty") @@ -1499,10 +1394,7 @@ func (s *groupServer) GetUserInGroupMembers( return resp, nil } -func (s *groupServer) GetGroupMemberUserIDs( - ctx context.Context, - req *pbGroup.GetGroupMemberUserIDsReq, -) (resp *pbGroup.GetGroupMemberUserIDsResp, err error) { +func (s *groupServer) GetGroupMemberUserIDs(ctx context.Context, req *pbGroup.GetGroupMemberUserIDsReq) (resp *pbGroup.GetGroupMemberUserIDsResp, err error) { resp = &pbGroup.GetGroupMemberUserIDsResp{} resp.UserIDs, err = s.GroupDatabase.FindGroupMemberUserID(ctx, req.GroupID) if err != nil { @@ -1511,10 +1403,7 @@ func (s *groupServer) GetGroupMemberUserIDs( return resp, nil } -func (s *groupServer) GetGroupMemberRoleLevel( - ctx context.Context, - req *pbGroup.GetGroupMemberRoleLevelReq, -) (*pbGroup.GetGroupMemberRoleLevelResp, error) { +func (s *groupServer) GetGroupMemberRoleLevel(ctx context.Context, req *pbGroup.GetGroupMemberRoleLevelReq) (*pbGroup.GetGroupMemberRoleLevelResp, error) { resp := &pbGroup.GetGroupMemberRoleLevelResp{} if len(req.RoleLevels) == 0 { return nil, errs.ErrArgs.Wrap("RoleLevels empty") diff --git a/internal/rpc/group/statistics.go b/internal/rpc/group/statistics.go new file mode 100644 index 000000000..c6664b4df --- /dev/null +++ b/internal/rpc/group/statistics.go @@ -0,0 +1,28 @@ +package group + +import ( + "context" + "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" + "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/group" + "time" +) + +func (s *groupServer) GroupCreateCount(ctx context.Context, req *group.GroupCreateCountReq) (*group.GroupCreateCountResp, error) { + if req.Start > req.End { + return nil, errs.ErrArgs.Wrap("start > end") + } + total, err := s.GroupDatabase.CountTotal(ctx, nil) + if err != nil { + return nil, err + } + start := time.UnixMilli(req.Start) + before, err := s.GroupDatabase.CountTotal(ctx, &start) + if err != nil { + return nil, err + } + count, err := s.GroupDatabase.CountRangeEverydayTotal(ctx, start, time.UnixMilli(req.End)) + if err != nil { + return nil, err + } + return &group.GroupCreateCountResp{Total: total, Before: before, Count: count}, nil +} diff --git a/internal/rpc/msg/extend_msg.go b/internal/rpc/msg/extend_msg.go deleted file mode 100644 index 6f82d980f..000000000 --- a/internal/rpc/msg/extend_msg.go +++ /dev/null @@ -1,414 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package msg - -import ( - "context" - - "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" - "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" -) - -func (m *msgServer) SetMessageReactionExtensions( - ctx context.Context, - req *msg.SetMessageReactionExtensionsReq, -) (resp *msg.SetMessageReactionExtensionsResp, err error) { - //resp = &msg.SetMessageReactionExtensionsResp{} - ////resp.ClientMsgID = req.ClientMsgID - ////resp.MsgFirstModifyTime = req.MsgFirstModifyTime - // - //if err := CallbackSetMessageReactionExtensions(ctx, req); err != nil { - // return nil, err - //} - ////if ExternalExtension - //if req.IsExternalExtensions { - // resp.MsgFirstModifyTime = req.MsgFirstModifyTime - // notification.ExtendMessageUpdatedNotification(req.OperationID, req.OpUserID, req.conversationID, - // req.SessionType, req, &resp, !req.IsReact, false) - // return resp, nil - //} - //isExists, err := m.MsgDatabase.JudgeMessageReactionExist(ctx, req.ClientMsgID, req.SessionType) - //if err != nil { - // return nil, err - //} - // - //if !isExists { - // if !req.IsReact { - // resp.MsgFirstModifyTime = utils.GetCurrentTimestampByMill() - // for k, v := range req.ReactionExtensions { - // err := m.MessageLocker.LockMessageTypeKey(ctx, req.ClientMsgID, k) - // if err != nil { - // return nil, err - // } - // v.LatestUpdateTime = utils.GetCurrentTimestampByMill() - // if err := m.MsgDatabase.SetMessageTypeKeyValue(ctx, req.ClientMsgID, req.SessionType, k, - // utils.StructToJsonString(v)); err != nil { - // return nil, err - // } - // } - // resp.IsReact = true - // _, err := m.MsgDatabase.SetMessageReactionExpire(ctx, req.ClientMsgID, req.SessionType, - // time.Duration(24*3)*time.Hour) - // if err != nil { - // return nil, err - // } - // } else { - // err := m.MessageLocker.LockGlobalMessage(ctx, req.ClientMsgID) - // if err != nil { - // return nil, err - // } - // mongoValue, err := m.MsgDatabase.GetExtendMsg(ctx, req.conversationID, req.SessionType, req.ClientMsgID, - // req.MsgFirstModifyTime) - // if err != nil { - // return nil, err - // } - // setValue := make(map[string]*sdkws.KeyValue) - // for k, v := range req.ReactionExtensions { - // - // temp := new(sdkws.KeyValue) - // if vv, ok := mongoValue.ReactionExtensions[k]; ok { - // utils.CopyStructFields(temp, &vv) - // if v.LatestUpdateTime != vv.LatestUpdateTime { - // setKeyResultInfo(&resp, 300, "message have update", req.ClientMsgID, k, temp) - // continue - // } - // } - // temp.TypeKey = k - // temp.Value = v.Value - // temp.LatestUpdateTime = utils.GetCurrentTimestampByMill() - // setValue[k] = temp - // } - // err = db.DB.InsertOrUpdateReactionExtendMsgSet(req.conversationID, req.SessionType, req.ClientMsgID, - // req.MsgFirstModifyTime, setValue) - // if err != nil { - // for _, value := range setValue { - // temp := new(msg.KeyValueResp) - // temp.KeyValue = value - // temp.ErrMsg = err.Error() - // temp.ErrCode = 100 - // resp.Result = append(resp.Result, temp) - // } - // } else { - // for _, value := range setValue { - // temp := new(msg.KeyValueResp) - // temp.KeyValue = value - // resp.Result = append(resp.Result, temp) - // } - // } - // lockErr := m.dMessageLocker.UnLockGlobalMessage(req.ClientMsgID) - // if lockErr != nil { - // log.Error(req.OperationID, "UnLockGlobalMessage err:", lockErr.Error()) - // } - // } - // - //} else { - // log.Debug(req.OperationID, "redis handle secondly", req.String()) - // - // for k, v := range req.Pb2Model { - // err := m.dMessageLocker.LockMessageTypeKey(req.ClientMsgID, k) - // if err != nil { - // setKeyResultInfo(&resp, 100, err.Error(), req.ClientMsgID, k, v) - // continue - // } - // redisValue, err := db.DB.GetMessageTypeKeyValue(req.ClientMsgID, req.SessionType, k) - // if err != nil && err != go_redis.Nil { - // setKeyResultInfo(&resp, 200, err.Error(), req.ClientMsgID, k, v) - // continue - // } - // temp := new(sdkws.KeyValue) - // utils.JsonStringToStruct(redisValue, temp) - // if v.LatestUpdateTime != temp.LatestUpdateTime { - // setKeyResultInfo(&resp, 300, "message have update", req.ClientMsgID, k, temp) - // continue - // } else { - // v.LatestUpdateTime = utils.GetCurrentTimestampByMill() - // newerr := db.DB.SetMessageTypeKeyValue(req.ClientMsgID, req.SessionType, k, utils.StructToJsonString(v)) - // if newerr != nil { - // setKeyResultInfo(&resp, 201, newerr.Error(), req.ClientMsgID, k, temp) - // continue - // } - // setKeyResultInfo(&resp, 0, "", req.ClientMsgID, k, v) - // } - // - // } - //} - //if !isExists { - // if !req.IsReact { - // notification.ExtendMessageUpdatedNotification(req.OperationID, req.OpUserID, req.conversationID, - // req.SessionType, req, &resp, true, true) - // } else { - // notification.ExtendMessageUpdatedNotification(req.OperationID, req.OpUserID, req.conversationID, - // req.SessionType, req, &resp, false, false) - // } - //} else { - // notification.ExtendMessageUpdatedNotification(req.OperationID, req.OpUserID, req.conversationID, - // req.SessionType, req, &resp, false, true) - //} - //log.Debug(req.OperationID, utils.GetSelfFuncName(), "m return is:", resp.String()) - return resp, nil - -} - -func (m *msgServer) setKeyResultInfo( - ctx context.Context, - r *msg.SetMessageReactionExtensionsResp, - errCode int32, - errMsg, clientMsgID, typeKey string, - keyValue *sdkws.KeyValue, -) { - temp := new(msg.KeyValueResp) - temp.KeyValue = keyValue - temp.ErrCode = errCode - temp.ErrMsg = errMsg - r.Result = append(r.Result, temp) - _ = m.MessageLocker.UnLockMessageTypeKey(ctx, clientMsgID, typeKey) -} - -func (m *msgServer) setDeleteKeyResultInfo( - ctx context.Context, - r *msg.DeleteMessagesReactionExtensionsResp, - errCode int32, - errMsg, clientMsgID, typeKey string, - keyValue *sdkws.KeyValue, -) { - temp := new(msg.KeyValueResp) - temp.KeyValue = keyValue - temp.ErrCode = errCode - temp.ErrMsg = errMsg - r.Result = append(r.Result, temp) - _ = m.MessageLocker.UnLockMessageTypeKey(ctx, clientMsgID, typeKey) -} - -func (m *msgServer) GetMessagesReactionExtensions( - ctx context.Context, - req *msg.GetMessagesReactionExtensionsReq, -) (resp *msg.GetMessagesReactionExtensionsResp, err error) { - //log.Debug(req.OperationID, utils.GetSelfFuncName(), "m args is:", req.String()) - //var rResp msg.GetMessageListReactionExtensionsResp - //for _, messageValue := range req.MessageReactionKeyList { - // var oneMessage msg.SingleMessageExtensionResult - // oneMessage.ClientMsgID = messageValue.ClientMsgID - // - // isExists, err := db.DB.JudgeMessageReactionExist(messageValue.ClientMsgID, req.SessionType) - // if err != nil { - // rResp.ErrCode = 100 - // rResp.ErrMsg = err.Error() - // return &rResp, nil - // } - // if isExists { - // redisValue, err := db.DB.GetOneMessageAllReactionList(messageValue.ClientMsgID, req.SessionType) - // if err != nil { - // oneMessage.ErrCode = 100 - // oneMessage.ErrMsg = err.Error() - // rResp.SingleMessageResult = append(rResp.SingleMessageResult, &oneMessage) - // continue - // } - // keyMap := make(map[string]*sdkws.KeyValue) - // - // for k, v := range redisValue { - // temp := new(sdkws.KeyValue) - // utils.JsonStringToStruct(v, temp) - // keyMap[k] = temp - // } - // oneMessage.Pb2Model = keyMap - // - // } else { - // mongoValue, err := db.DB.GetExtendMsg(req.conversationID, req.SessionType, messageValue.ClientMsgID, - // messageValue.MsgFirstModifyTime) - // if err != nil { - // oneMessage.ErrCode = 100 - // oneMessage.ErrMsg = err.Error() - // rResp.SingleMessageResult = append(rResp.SingleMessageResult, &oneMessage) - // continue - // } - // keyMap := make(map[string]*sdkws.KeyValue) - // - // for k, v := range mongoValue.Pb2Model { - // temp := new(sdkws.KeyValue) - // temp.TypeKey = v.TypeKey - // temp.Value = v.Value - // temp.LatestUpdateTime = v.LatestUpdateTime - // keyMap[k] = temp - // } - // oneMessage.Pb2Model = keyMap - // } - // rResp.SingleMessageResult = append(rResp.SingleMessageResult, &oneMessage) - //} - //log.Debug(req.OperationID, utils.GetSelfFuncName(), "m return is:", rResp.String()) - return resp, nil - -} - -func (m *msgServer) AddMessageReactionExtensions( - ctx context.Context, - req *msg.ModifyMessageReactionExtensionsReq, -) (resp *msg.ModifyMessageReactionExtensionsResp, err error) { - return -} - -func (m *msgServer) DeleteMessageReactionExtensions( - ctx context.Context, - req *msg.DeleteMessagesReactionExtensionsReq, -) (resp *msg.DeleteMessagesReactionExtensionsResp, err error) { - //log.Debug(req.OperationID, utils.GetSelfFuncName(), "m args is:", req.String()) - //var rResp msg.DeleteMessagesReactionExtensionsResp - //callbackResp := notification.callbackDeleteMessageReactionExtensions(req) - //if callbackResp.ActionCode != constant.ActionAllow || callbackResp.ErrCode != 0 { - // rResp.ErrCode = int32(callbackResp.ErrCode) - // rResp.ErrMsg = callbackResp.ErrMsg - // for _, value := range req.Pb2Model { - // temp := new(msg.KeyValueResp) - // temp.KeyValue = value - // temp.ErrMsg = callbackResp.ErrMsg - // temp.ErrCode = 100 - // rResp.Result = append(rResp.Result, temp) - // } - // return &rResp, nil - //} - ////if ExternalExtension - //if req.IsExternalExtensions { - // rResp.Result = callbackResp.ResultReactionExtensionList - // notification.ExtendMessageDeleteNotification(req.OperationID, req.OpUserID, req.conversationID, req.SessionType, - // req, &rResp, false, false) - // return &rResp, nil - // - //} - //for _, v := range callbackResp.ResultReactionExtensions { - // if v.ErrCode != 0 { - // func(req *[]*sdkws.KeyValue, typeKey string) { - // for i := 0; i < len(*req); i++ { - // if (*req)[i].TypeKey == typeKey { - // *req = append((*req)[:i], (*req)[i+1:]...) - // } - // } - // }(&req.Pb2Model, v.KeyValue.TypeKey) - // rResp.Result = append(rResp.Result, v) - // } - //} - //isExists, err := db.DB.JudgeMessageReactionExist(req.ClientMsgID, req.SessionType) - //if err != nil { - // rResp.ErrCode = 100 - // rResp.ErrMsg = err.Error() - // for _, value := range req.Pb2Model { - // temp := new(msg.KeyValueResp) - // temp.KeyValue = value - // temp.ErrMsg = err.Error() - // temp.ErrCode = 100 - // rResp.Result = append(rResp.Result, temp) - // } - // return &rResp, nil - //} - // - //if isExists { - // log.Debug(req.OperationID, "redis handle this delete", req.String()) - // for _, v := range req.Pb2Model { - // err := m.dMessageLocker.LockMessageTypeKey(req.ClientMsgID, v.TypeKey) - // if err != nil { - // setDeleteKeyResultInfo(&rResp, 100, err.Error(), req.ClientMsgID, v.TypeKey, v) - // continue - // } - // - // redisValue, err := db.DB.GetMessageTypeKeyValue(req.ClientMsgID, req.SessionType, v.TypeKey) - // if err != nil && err != go_redis.Nil { - // setDeleteKeyResultInfo(&rResp, 200, err.Error(), req.ClientMsgID, v.TypeKey, v) - // continue - // } - // temp := new(sdkws.KeyValue) - // utils.JsonStringToStruct(redisValue, temp) - // if v.LatestUpdateTime != temp.LatestUpdateTime { - // setDeleteKeyResultInfo(&rResp, 300, "message have update", req.ClientMsgID, v.TypeKey, temp) - // continue - // } else { - // newErr := db.DB.DeleteOneMessageKey(req.ClientMsgID, req.SessionType, v.TypeKey) - // if newErr != nil { - // setDeleteKeyResultInfo(&rResp, 201, newErr.Error(), req.ClientMsgID, v.TypeKey, temp) - // continue - // } - // setDeleteKeyResultInfo(&rResp, 0, "", req.ClientMsgID, v.TypeKey, v) - // } - // } - //} else { - // err := m.dMessageLocker.LockGlobalMessage(req.ClientMsgID) - // if err != nil { - // rResp.ErrCode = 100 - // rResp.ErrMsg = err.Error() - // for _, value := range req.Pb2Model { - // temp := new(msg.KeyValueResp) - // temp.KeyValue = value - // temp.ErrMsg = err.Error() - // temp.ErrCode = 100 - // rResp.Result = append(rResp.Result, temp) - // } - // return &rResp, nil - // } - // mongoValue, err := db.DB.GetExtendMsg(req.conversationID, req.SessionType, req.ClientMsgID, - // req.MsgFirstModifyTime) - // if err != nil { - // rResp.ErrCode = 200 - // rResp.ErrMsg = err.Error() - // for _, value := range req.Pb2Model { - // temp := new(msg.KeyValueResp) - // temp.KeyValue = value - // temp.ErrMsg = err.Error() - // temp.ErrCode = 100 - // rResp.Result = append(rResp.Result, temp) - // } - // return &rResp, nil - // } - // setValue := make(map[string]*sdkws.KeyValue) - // for _, v := range req.Pb2Model { - // - // temp := new(sdkws.KeyValue) - // if vv, ok := mongoValue.Pb2Model[v.TypeKey]; ok { - // utils.CopyStructFields(temp, &vv) - // if v.LatestUpdateTime != vv.LatestUpdateTime { - // setDeleteKeyResultInfo(&rResp, 300, "message have update", req.ClientMsgID, v.TypeKey, temp) - // continue - // } - // } else { - // setDeleteKeyResultInfo(&rResp, 400, "key not in", req.ClientMsgID, v.TypeKey, v) - // continue - // } - // temp.TypeKey = v.TypeKey - // setValue[v.TypeKey] = temp - // } - // err = db.DB.DeleteReactionExtendMsgSet(req.conversationID, req.SessionType, req.ClientMsgID, - // req.MsgFirstModifyTime, setValue) - // if err != nil { - // for _, value := range setValue { - // temp := new(msg.KeyValueResp) - // temp.KeyValue = value - // temp.ErrMsg = err.Error() - // temp.ErrCode = 100 - // rResp.Result = append(rResp.Result, temp) - // } - // } else { - // for _, value := range setValue { - // temp := new(msg.KeyValueResp) - // temp.KeyValue = value - // rResp.Result = append(rResp.Result, temp) - // } - // } - // lockErr := m.dMessageLocker.UnLockGlobalMessage(req.ClientMsgID) - // if lockErr != nil { - // log.Error(req.OperationID, "UnLockGlobalMessage err:", lockErr.Error()) - // } - // - //} - // notification.ExtendMessageDeleteNotification(req.OperationID, req.OpUserID, req.conversationID, req.SessionType, - // req, &rResp, false, isExists) - //log.Debug(req.OperationID, utils.GetSelfFuncName(), "m return is:", rResp.String()) - return resp, nil -} diff --git a/internal/rpc/msg/extend_msg_callback.go b/internal/rpc/msg/extend_msg_callback.go deleted file mode 100644 index d510afd0c..000000000 --- a/internal/rpc/msg/extend_msg_callback.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package msg - -import ( - "context" - - cbapi "github.com/OpenIMSDK/Open-IM-Server/pkg/callbackstruct" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/http" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" - "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" -) - -func callbackSetMessageReactionExtensions(ctx context.Context, setReq *msg.SetMessageReactionExtensionsReq) error { - if !config.Config.Callback.CallbackAfterSendGroupMsg.Enable { - return nil - } - req := &cbapi.CallbackBeforeSetMessageReactionExtReq{ - OperationID: mcontext.GetOperationID(ctx), - CallbackCommand: constant.CallbackBeforeSetMessageReactionExtensionCommand, - ConversationID: setReq.ConversationID, - OpUserID: mcontext.GetOpUserID(ctx), - SessionType: setReq.SessionType, - ReactionExtensionList: setReq.ReactionExtensions, - ClientMsgID: setReq.ClientMsgID, - IsReact: setReq.IsReact, - IsExternalExtensions: setReq.IsExternalExtensions, - MsgFirstModifyTime: setReq.MsgFirstModifyTime, - } - resp := &cbapi.CallbackBeforeSetMessageReactionExtResp{} - if err := http.CallBackPostReturn(ctx, cbURL(), req, resp, config.Config.Callback.CallbackAfterSendGroupMsg); err != nil { - return err - } - setReq.MsgFirstModifyTime = resp.MsgFirstModifyTime - return nil -} - -func callbackDeleteMessageReactionExtensions( - ctx context.Context, - setReq *msg.DeleteMessagesReactionExtensionsReq, -) error { - if !config.Config.Callback.CallbackAfterSendGroupMsg.Enable { - return nil - } - req := &cbapi.CallbackDeleteMessageReactionExtReq{ - OperationID: setReq.OperationID, - CallbackCommand: constant.CallbackBeforeDeleteMessageReactionExtensionsCommand, - ConversationID: setReq.ConversationID, - OpUserID: setReq.OpUserID, - SessionType: setReq.SessionType, - ReactionExtensionList: setReq.ReactionExtensions, - ClientMsgID: setReq.ClientMsgID, - IsExternalExtensions: setReq.IsExternalExtensions, - MsgFirstModifyTime: setReq.MsgFirstModifyTime, - } - resp := &cbapi.CallbackDeleteMessageReactionExtResp{} - return http.CallBackPostReturn(ctx, cbURL(), req, resp, config.Config.Callback.CallbackAfterSendGroupMsg) -} - -func callbackGetMessageListReactionExtensions(ctx context.Context, getReq *msg.GetMessagesReactionExtensionsReq) error { - if !config.Config.Callback.CallbackAfterSendGroupMsg.Enable { - return nil - } - req := &cbapi.CallbackGetMessageListReactionExtReq{ - OperationID: mcontext.GetOperationID(ctx), - CallbackCommand: constant.CallbackGetMessageListReactionExtensionsCommand, - ConversationID: getReq.ConversationID, - OpUserID: mcontext.GetOperationID(ctx), - SessionType: getReq.SessionType, - TypeKeyList: getReq.TypeKeys, - } - resp := &cbapi.CallbackGetMessageListReactionExtResp{} - return http.CallBackPostReturn(ctx, cbURL(), req, resp, config.Config.Callback.CallbackAfterSendGroupMsg) -} - -func callbackAddMessageReactionExtensions(ctx context.Context, setReq *msg.ModifyMessageReactionExtensionsReq) error { - req := &cbapi.CallbackAddMessageReactionExtReq{ - OperationID: mcontext.GetOperationID(ctx), - CallbackCommand: constant.CallbackAddMessageListReactionExtensionsCommand, - ConversationID: setReq.ConversationID, - OpUserID: mcontext.GetOperationID(ctx), - SessionType: setReq.SessionType, - ReactionExtensionList: setReq.ReactionExtensions, - ClientMsgID: setReq.ClientMsgID, - IsReact: setReq.IsReact, - IsExternalExtensions: setReq.IsExternalExtensions, - MsgFirstModifyTime: setReq.MsgFirstModifyTime, - } - resp := &cbapi.CallbackAddMessageReactionExtResp{} - return http.CallBackPostReturn(ctx, cbURL(), req, resp, config.Config.Callback.CallbackAfterSendGroupMsg) -} diff --git a/internal/rpc/msg/revoke.go b/internal/rpc/msg/revoke.go index c4b64aa46..b7664e2c6 100644 --- a/internal/rpc/msg/revoke.go +++ b/internal/rpc/msg/revoke.go @@ -99,7 +99,6 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg. } now := time.Now().UnixMilli() err = m.MsgDatabase.RevokeMsg(ctx, req.ConversationID, req.Seq, &unRelationTb.RevokeModel{ - ID: uuid.New().String(), Role: role, UserID: req.UserID, Nickname: user.Nickname, diff --git a/internal/rpc/msg/send.go b/internal/rpc/msg/send.go index 2c27b869d..671450eee 100644 --- a/internal/rpc/msg/send.go +++ b/internal/rpc/msg/send.go @@ -31,20 +31,24 @@ import ( func (m *msgServer) SendMsg(ctx context.Context, req *pbMsg.SendMsgReq) (resp *pbMsg.SendMsgResp, error error) { resp = &pbMsg.SendMsgResp{} - flag := isMessageHasReadEnabled(req.MsgData) - if !flag { - return nil, errs.ErrMessageHasReadDisable.Wrap() - } - m.encapsulateMsgData(req.MsgData) - switch req.MsgData.SessionType { - case constant.SingleChatType: - return m.sendMsgSingleChat(ctx, req) - case constant.NotificationChatType: - return m.sendMsgNotification(ctx, req) - case constant.SuperGroupChatType: - return m.sendMsgSuperGroupChat(ctx, req) - default: - return nil, errs.ErrArgs.Wrap("unknown sessionType") + if req.MsgData != nil { + flag := isMessageHasReadEnabled(req.MsgData) + if !flag { + return nil, errs.ErrMessageHasReadDisable.Wrap() + } + m.encapsulateMsgData(req.MsgData) + switch req.MsgData.SessionType { + case constant.SingleChatType: + return m.sendMsgSingleChat(ctx, req) + case constant.NotificationChatType: + return m.sendMsgNotification(ctx, req) + case constant.SuperGroupChatType: + return m.sendMsgSuperGroupChat(ctx, req) + default: + return nil, errs.ErrArgs.Wrap("unknown sessionType") + } + } else { + return nil, errs.ErrArgs.Wrap("msgData is nil") } } @@ -160,7 +164,7 @@ func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbMsg.SendMsgReq } if !isSend { promePkg.Inc(promePkg.SingleChatMsgProcessFailedCounter) - return nil, errs.ErrUserNotRecvMsg + return nil, nil } else { if err = callbackBeforeSendSingleMsg(ctx, req); err != nil { return nil, err diff --git a/internal/rpc/msg/server.go b/internal/rpc/msg/server.go index 4702eb5f9..909707703 100644 --- a/internal/rpc/msg/server.go +++ b/internal/rpc/msg/server.go @@ -1,42 +1,25 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package msg import ( "context" - "google.golang.org/grpc" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/controller" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/localcache" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/tx" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/unrelation" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/prome" "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/conversation" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" + "google.golang.org/grpc" ) type MessageInterceptorChain []MessageInterceptorFunc type msgServer struct { RegisterCenter discoveryregistry.SvcDiscoveryRegistry MsgDatabase controller.CommonMsgDatabase - ExtendMsgDatabase controller.ExtendMsgDatabase Group *rpcclient.GroupRpcClient User *rpcclient.UserRpcClient Conversation *rpcclient.ConversationRpcClient @@ -77,13 +60,6 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e } cacheModel := cache.NewMsgCacheModel(rdb) msgDocModel := unrelation.NewMsgMongoDriver(mongo.GetDatabase()) - extendMsgModel := unrelation.NewExtendMsgSetMongoDriver(mongo.GetDatabase()) - extendMsgCacheModel := cache.NewExtendMsgSetCacheRedis(rdb, extendMsgModel, cache.GetDefaultOpt()) - extendMsgDatabase := controller.NewExtendMsgDatabase( - extendMsgModel, - extendMsgCacheModel, - tx.NewMongo(mongo.GetClient()), - ) msgDatabase := controller.NewCommonMsgDatabase(msgDocModel, cacheModel) conversationClient := rpcclient.NewConversationRpcClient(client) userRpcClient := rpcclient.NewUserRpcClient(client) @@ -94,7 +70,6 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e User: &userRpcClient, Group: &groupRpcClient, MsgDatabase: msgDatabase, - ExtendMsgDatabase: extendMsgDatabase, RegisterCenter: client, GroupLocalCache: localcache.NewGroupLocalCache(&groupRpcClient), ConversationLocalCache: localcache.NewConversationLocalCache(&conversationClient), @@ -125,8 +100,7 @@ func (m *msgServer) initPrometheus() { } func (m *msgServer) conversationAndGetRecvID(conversation *conversation.Conversation, userID string) (recvID string) { - if conversation.ConversationType == constant.SingleChatType || - conversation.ConversationType == constant.NotificationChatType { + if conversation.ConversationType == constant.SingleChatType || conversation.ConversationType == constant.NotificationChatType { if userID == conversation.OwnerUserID { recvID = conversation.UserID } else { diff --git a/internal/rpc/msg/statistics.go b/internal/rpc/msg/statistics.go new file mode 100644 index 000000000..872ec8f18 --- /dev/null +++ b/internal/rpc/msg/statistics.go @@ -0,0 +1,84 @@ +package msg + +import ( + "context" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" + "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" + "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" + "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" + "time" +) + +func (m *msgServer) GetActiveUser(ctx context.Context, req *msg.GetActiveUserReq) (*msg.GetActiveUserResp, error) { + msgCount, userCount, users, dateCount, err := m.MsgDatabase.RangeUserSendCount(ctx, time.UnixMilli(req.Start), time.UnixMilli(req.End), req.Group, req.Ase, req.Pagination.PageNumber, req.Pagination.ShowNumber) + if err != nil { + return nil, err + } + var pbUsers []*msg.ActiveUser + if len(users) > 0 { + userIDs := utils.Slice(users, func(e *unrelation.UserCount) string { return e.UserID }) + userMap, err := m.User.GetUsersInfoMap(ctx, userIDs) + if err != nil { + return nil, err + } + pbUsers = make([]*msg.ActiveUser, 0, len(users)) + for _, user := range users { + pbUser := userMap[user.UserID] + if pbUser == nil { + pbUser = &sdkws.UserInfo{ + UserID: user.UserID, + Nickname: user.UserID, + } + } + pbUsers = append(pbUsers, &msg.ActiveUser{ + User: pbUser, + Count: user.Count, + }) + } + } + return &msg.GetActiveUserResp{ + MsgCount: msgCount, + UserCount: userCount, + DateCount: dateCount, + Users: pbUsers, + }, nil +} + +func (m *msgServer) GetActiveGroup(ctx context.Context, req *msg.GetActiveGroupReq) (*msg.GetActiveGroupResp, error) { + msgCount, groupCount, groups, dateCount, err := m.MsgDatabase.RangeGroupSendCount(ctx, time.UnixMilli(req.Start), time.UnixMilli(req.End), req.Ase, req.Pagination.PageNumber, req.Pagination.ShowNumber) + if err != nil { + return nil, err + } + var pbGroups []*msg.ActiveGroup + if len(groups) > 0 { + groupIDs := utils.Slice(groups, func(e *unrelation.GroupCount) string { return e.GroupID }) + resp, err := m.Group.GetGroupInfos(ctx, groupIDs, false) + if err != nil { + return nil, err + } + groupMap := make(map[string]*sdkws.GroupInfo, len(groups)) + for i, group := range groups { + groupMap[group.GroupID] = resp[i] + } + pbGroups = make([]*msg.ActiveGroup, 0, len(groups)) + for _, group := range groups { + pbGroup := groupMap[group.GroupID] + if pbGroup == nil { + pbGroup = &sdkws.GroupInfo{ + GroupID: group.GroupID, + GroupName: group.GroupID, + } + } + pbGroups = append(pbGroups, &msg.ActiveGroup{ + Group: pbGroup, + Count: group.Count, + }) + } + } + return &msg.GetActiveGroupResp{ + MsgCount: msgCount, + GroupCount: groupCount, + DateCount: dateCount, + Groups: pbGroups, + }, nil +} diff --git a/internal/rpc/third/s3.go b/internal/rpc/third/s3.go index 4ff9a3ce8..4cbbd3d7d 100644 --- a/internal/rpc/third/s3.go +++ b/internal/rpc/third/s3.go @@ -1,52 +1,151 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package third import ( "context" - "time" - - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/tokenverify" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3/cont" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" + "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/third" + "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" + "time" ) -func (t *thirdServer) ApplyPut(ctx context.Context, req *third.ApplyPutReq) (*third.ApplyPutResp, error) { - return t.s3dataBase.ApplyPut(ctx, req) +func (t *thirdServer) PartLimit(ctx context.Context, req *third.PartLimitReq) (*third.PartLimitResp, error) { + limit := t.s3dataBase.PartLimit() + return &third.PartLimitResp{ + MinPartSize: limit.MinPartSize, + MaxPartSize: limit.MaxPartSize, + MaxNumSize: int32(limit.MaxNumSize), + }, nil } -func (t *thirdServer) GetPut(ctx context.Context, req *third.GetPutReq) (*third.GetPutResp, error) { - return t.s3dataBase.GetPut(ctx, req) +func (t *thirdServer) PartSize(ctx context.Context, req *third.PartSizeReq) (*third.PartSizeResp, error) { + size, err := t.s3dataBase.PartSize(ctx, req.Size) + if err != nil { + return nil, err + } + return &third.PartSizeResp{Size: size}, nil } -func (t *thirdServer) ConfirmPut(ctx context.Context, req *third.ConfirmPutReq) (*third.ConfirmPutResp, error) { - return t.s3dataBase.ConfirmPut(ctx, req) +func (t *thirdServer) InitiateMultipartUpload(ctx context.Context, req *third.InitiateMultipartUploadReq) (*third.InitiateMultipartUploadResp, error) { + defer log.ZDebug(ctx, "return") + if err := checkUploadName(ctx, req.Name); err != nil { + return nil, err + } + expireTime := time.Now().Add(t.defaultExpire) + result, err := t.s3dataBase.InitiateMultipartUpload(ctx, req.Hash, req.Size, t.defaultExpire, int(req.MaxParts)) + if err != nil { + if haErr, ok := errs.Unwrap(err).(*cont.HashAlreadyExistsError); ok { + obj := &relation.ObjectModel{ + Name: req.Name, + UserID: mcontext.GetOpUserID(ctx), + Hash: req.Hash, + Key: haErr.Object.Key, + Size: haErr.Object.Size, + ContentType: req.ContentType, + Cause: req.Cause, + CreateTime: time.Now(), + } + if err := t.s3dataBase.SetObject(ctx, obj); err != nil { + return nil, err + } + return &third.InitiateMultipartUploadResp{ + Url: t.apiAddress(obj.Name), + }, nil + } + return nil, err + } + var sign *third.AuthSignParts + if result.Sign != nil && len(result.Sign.Parts) > 0 { + sign = &third.AuthSignParts{ + Url: result.Sign.URL, + Query: toPbMapArray(result.Sign.Query), + Header: toPbMapArray(result.Sign.Header), + Parts: make([]*third.SignPart, len(result.Sign.Parts)), + } + for i, part := range result.Sign.Parts { + sign.Parts[i] = &third.SignPart{ + PartNumber: int32(part.PartNumber), + Url: part.URL, + Query: toPbMapArray(part.Query), + Header: toPbMapArray(part.Header), + } + } + } + return &third.InitiateMultipartUploadResp{ + Upload: &third.UploadInfo{ + UploadID: result.UploadID, + PartSize: result.PartSize, + Sign: sign, + ExpireTime: expireTime.UnixMilli(), + }, + }, nil } -func (t *thirdServer) GetUrl(ctx context.Context, req *third.GetUrlReq) (*third.GetUrlResp, error) { - if req.Expires <= 0 { - if err := tokenverify.CheckAdmin(ctx); err != nil { - return nil, err +func (t *thirdServer) AuthSign(ctx context.Context, req *third.AuthSignReq) (*third.AuthSignResp, error) { + defer log.ZDebug(ctx, "return") + partNumbers := utils.Slice(req.PartNumbers, func(partNumber int32) int { return int(partNumber) }) + result, err := t.s3dataBase.AuthSign(ctx, req.UploadID, partNumbers) + if err != nil { + return nil, err + } + resp := &third.AuthSignResp{ + Url: result.URL, + Query: toPbMapArray(result.Query), + Header: toPbMapArray(result.Header), + Parts: make([]*third.SignPart, len(result.Parts)), + } + for i, part := range result.Parts { + resp.Parts[i] = &third.SignPart{ + PartNumber: int32(part.PartNumber), + Url: part.URL, + Query: toPbMapArray(part.Query), + Header: toPbMapArray(part.Header), } } - return t.s3dataBase.GetUrl(ctx, req) + return resp, nil +} + +func (t *thirdServer) CompleteMultipartUpload(ctx context.Context, req *third.CompleteMultipartUploadReq) (*third.CompleteMultipartUploadResp, error) { + defer log.ZDebug(ctx, "return") + if err := checkUploadName(ctx, req.Name); err != nil { + return nil, err + } + result, err := t.s3dataBase.CompleteMultipartUpload(ctx, req.UploadID, req.Parts) + if err != nil { + return nil, err + } + obj := &relation.ObjectModel{ + Name: req.Name, + UserID: mcontext.GetOpUserID(ctx), + Hash: result.Hash, + Key: result.Key, + Size: result.Size, + ContentType: req.ContentType, + Cause: req.Cause, + CreateTime: time.Now(), + } + if err := t.s3dataBase.SetObject(ctx, obj); err != nil { + return nil, err + } + return &third.CompleteMultipartUploadResp{ + Url: t.apiAddress(obj.Name), + }, nil } -func (t *thirdServer) GetHashInfo(ctx context.Context, req *third.GetHashInfoReq) (*third.GetHashInfoResp, error) { - return t.s3dataBase.GetHashInfo(ctx, req) +func (t *thirdServer) AccessURL(ctx context.Context, req *third.AccessURLReq) (*third.AccessURLResp, error) { + expireTime, rawURL, err := t.s3dataBase.AccessURL(ctx, req.Name, t.defaultExpire) + if err != nil { + return nil, err + } + return &third.AccessURLResp{ + Url: rawURL, + ExpireTime: expireTime.UnixMilli(), + }, nil } -func (t *thirdServer) CleanObject(ctx context.Context, now time.Time) { - t.s3dataBase.CleanExpirationObject(ctx, now) +func (t *thirdServer) apiAddress(name string) string { + return t.apiURL + name } diff --git a/internal/rpc/third/third.go b/internal/rpc/third/third.go index ebf2c60d1..7c487b1d2 100644 --- a/internal/rpc/third/third.go +++ b/internal/rpc/third/third.go @@ -1,80 +1,83 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package third import ( "context" + "fmt" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3/cos" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3/minio" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3/oss" "net/url" - - "google.golang.org/grpc" + "time" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/controller" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/obj" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/relation" relationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/third" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" + "google.golang.org/grpc" ) func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { - u, err := url.Parse(config.Config.Object.ApiURL) - if err != nil { + apiURL := config.Config.Object.ApiURL + if apiURL == "" { + return fmt.Errorf("api url is empty") + } + if _, err := url.Parse(config.Config.Object.ApiURL); err != nil { return err } + if apiURL[len(apiURL)-1] != '/' { + apiURL += "/" + } rdb, err := cache.NewRedis() if err != nil { return err } - o, err := obj.NewMinioInterface() + db, err := relation.NewGormDB() if err != nil { return err } - db, err := relation.NewGormDB() - if err != nil { + if err := db.AutoMigrate(&relationTb.ObjectModel{}); err != nil { return err } - if err := db.AutoMigrate(&relationTb.ObjectHashModel{}, &relationTb.ObjectInfoModel{}, &relationTb.ObjectPutModel{}); err != nil { + // 根据配置文件策略选择 oss 方式 + enable := config.Config.Object.Enable + var o s3.Interface + switch config.Config.Object.Enable { + case "minio": + o, err = minio.NewMinio() + case "cos": + o, err = cos.NewCos() + case "oss": + o, err = oss.NewOSS() + default: + err = fmt.Errorf("invalid object enable: %s", enable) + } + if err != nil { return err } third.RegisterThirdServer(server, &thirdServer{ + apiURL: apiURL, thirdDatabase: controller.NewThirdDatabase(cache.NewMsgCacheModel(rdb)), userRpcClient: rpcclient.NewUserRpcClient(client), - s3dataBase: controller.NewS3Database( - o, - relation.NewObjectHash(db), - relation.NewObjectInfo(db), - relation.NewObjectPut(db), - u, - ), + s3dataBase: controller.NewS3Database(o, relation.NewObjectInfo(db)), + defaultExpire: time.Hour * 24 * 7, }) return nil } type thirdServer struct { + apiURL string thirdDatabase controller.ThirdDatabase s3dataBase controller.S3Database userRpcClient rpcclient.UserRpcClient + defaultExpire time.Duration } -func (t *thirdServer) FcmUpdateToken( - ctx context.Context, - req *third.FcmUpdateTokenReq, -) (resp *third.FcmUpdateTokenResp, err error) { +func (t *thirdServer) FcmUpdateToken(ctx context.Context, req *third.FcmUpdateTokenReq) (resp *third.FcmUpdateTokenResp, err error) { err = t.thirdDatabase.FcmUpdateToken(ctx, req.Account, int(req.PlatformID), req.FcmToken, req.ExpireTime) if err != nil { return nil, err @@ -82,10 +85,7 @@ func (t *thirdServer) FcmUpdateToken( return &third.FcmUpdateTokenResp{}, nil } -func (t *thirdServer) SetAppBadge( - ctx context.Context, - req *third.SetAppBadgeReq, -) (resp *third.SetAppBadgeResp, err error) { +func (t *thirdServer) SetAppBadge(ctx context.Context, req *third.SetAppBadgeReq) (resp *third.SetAppBadgeResp, err error) { err = t.thirdDatabase.SetAppBadge(ctx, req.UserID, int(req.AppUnreadCount)) if err != nil { return nil, err diff --git a/internal/rpc/third/tool.go b/internal/rpc/third/tool.go new file mode 100644 index 000000000..84017ae1f --- /dev/null +++ b/internal/rpc/third/tool.go @@ -0,0 +1,63 @@ +package third + +import ( + "context" + "errors" + "fmt" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/tokenverify" + "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" + "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/third" + "strings" + "unicode/utf8" +) + +func toPbMapArray(m map[string][]string) []*third.KeyValues { + res := make([]*third.KeyValues, 0, len(m)) + for key := range m { + res = append(res, &third.KeyValues{ + Key: key, + Values: m[key], + }) + } + return res +} + +func checkUploadName(ctx context.Context, name string) error { + if name == "" { + return errs.ErrArgs.Wrap("name is empty") + } + if name[0] == '/' { + return errs.ErrArgs.Wrap("name cannot start with `/`") + } + if err := checkValidObjectName(name); err != nil { + return errs.ErrArgs.Wrap(err.Error()) + } + opUserID := mcontext.GetOpUserID(ctx) + if opUserID == "" { + return errs.ErrNoPermission.Wrap("opUserID is empty") + } + if !tokenverify.IsManagerUserID(opUserID) { + if !strings.HasPrefix(name, opUserID+"/") { + return errs.ErrNoPermission.Wrap(fmt.Sprintf("name must start with `%s/`", opUserID)) + } + } + return nil +} + +func checkValidObjectNamePrefix(objectName string) error { + if len(objectName) > 1024 { + return errors.New("object name cannot be longer than 1024 characters") + } + if !utf8.ValidString(objectName) { + return errors.New("object name with non UTF-8 strings are not supported") + } + return nil +} + +func checkValidObjectName(objectName string) error { + if strings.TrimSpace(objectName) == "" { + return errors.New("object name cannot be empty") + } + return checkValidObjectNamePrefix(objectName) +} diff --git a/internal/rpc/user/statistics.go b/internal/rpc/user/statistics.go index 7da40bb1e..dc11b8a90 100644 --- a/internal/rpc/user/statistics.go +++ b/internal/rpc/user/statistics.go @@ -29,13 +29,18 @@ func (s *userServer) UserRegisterCount( if req.Start > req.End { return nil, errs.ErrArgs.Wrap("start > end") } - total, err := s.CountTotal(ctx) + total, err := s.CountTotal(ctx, nil) if err != nil { return nil, err } - count, err := s.CountRangeEverydayTotal(ctx, time.UnixMilli(req.Start), time.UnixMilli(req.End)) + start := time.UnixMilli(req.Start) + before, err := s.CountTotal(ctx, &start) if err != nil { return nil, err } - return &pbuser.UserRegisterCountResp{Total: total, Count: count}, nil + count, err := s.CountRangeEverydayTotal(ctx, start, time.UnixMilli(req.End)) + if err != nil { + return nil, err + } + return &pbuser.UserRegisterCountResp{Total: total, Before: before, Count: count}, nil } diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index d2368ceb3..4619db4ce 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -1,26 +1,12 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package user import ( "context" + "errors" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "strings" "time" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/convert" @@ -37,9 +23,8 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient/notification" - "google.golang.org/grpc" - "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" + "google.golang.org/grpc" ) type userServer struct { @@ -63,7 +48,7 @@ func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { } users := make([]*tablerelation.UserModel, 0) if len(config.Config.Manager.UserID) != len(config.Config.Manager.Nickname) { - return errs.ErrConfig.Wrap("len(config.Config.Manager.AppManagerUid) != len(config.Config.Manager.Nickname)") + return errors.New("len(config.Config.Manager.AppManagerUid) != len(config.Config.Manager.Nickname)") } for k, v := range config.Config.Manager.UserID { users = append(users, &tablerelation.UserModel{UserID: v, Nickname: config.Config.Manager.Nickname[k]}) @@ -74,22 +59,16 @@ func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { friendRpcClient := rpcclient.NewFriendRpcClient(client) msgRpcClient := rpcclient.NewMessageRpcClient(client) u := &userServer{ - UserDatabase: database, - RegisterCenter: client, - friendRpcClient: &friendRpcClient, - notificationSender: notification.NewFriendNotificationSender( - &msgRpcClient, - notification.WithDBFunc(database.FindWithError), - ), + UserDatabase: database, + RegisterCenter: client, + friendRpcClient: &friendRpcClient, + notificationSender: notification.NewFriendNotificationSender(&msgRpcClient, notification.WithDBFunc(database.FindWithError)), } pbuser.RegisterUserServer(server, u) return u.UserDatabase.InitOnce(context.Background(), users) } -func (s *userServer) GetDesignateUsers( - ctx context.Context, - req *pbuser.GetDesignateUsersReq, -) (resp *pbuser.GetDesignateUsersResp, err error) { +func (s *userServer) GetDesignateUsers(ctx context.Context, req *pbuser.GetDesignateUsersReq) (resp *pbuser.GetDesignateUsersResp, err error) { resp = &pbuser.GetDesignateUsersResp{} users, err := s.FindWithError(ctx, req.UserIDs) if err != nil { @@ -102,10 +81,7 @@ func (s *userServer) GetDesignateUsers( return resp, nil } -func (s *userServer) UpdateUserInfo( - ctx context.Context, - req *pbuser.UpdateUserInfoReq, -) (resp *pbuser.UpdateUserInfoResp, err error) { +func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInfoReq) (resp *pbuser.UpdateUserInfoResp, err error) { resp = &pbuser.UpdateUserInfoResp{} err = tokenverify.CheckAccessV3(ctx, req.UserInfo.UserID) if err != nil { @@ -130,10 +106,7 @@ func (s *userServer) UpdateUserInfo( return resp, nil } -func (s *userServer) SetGlobalRecvMessageOpt( - ctx context.Context, - req *pbuser.SetGlobalRecvMessageOptReq, -) (resp *pbuser.SetGlobalRecvMessageOptResp, err error) { +func (s *userServer) SetGlobalRecvMessageOpt(ctx context.Context, req *pbuser.SetGlobalRecvMessageOptReq) (resp *pbuser.SetGlobalRecvMessageOptResp, err error) { resp = &pbuser.SetGlobalRecvMessageOptResp{} if _, err := s.FindWithError(ctx, []string{req.UserID}); err != nil { return nil, err @@ -147,10 +120,7 @@ func (s *userServer) SetGlobalRecvMessageOpt( return resp, nil } -func (s *userServer) AccountCheck( - ctx context.Context, - req *pbuser.AccountCheckReq, -) (resp *pbuser.AccountCheckResp, err error) { +func (s *userServer) AccountCheck(ctx context.Context, req *pbuser.AccountCheckReq) (resp *pbuser.AccountCheckResp, err error) { resp = &pbuser.AccountCheckResp{} if utils.Duplicate(req.CheckUserIDs) { return nil, errs.ErrArgs.Wrap("userID repeated") @@ -179,10 +149,7 @@ func (s *userServer) AccountCheck( return resp, nil } -func (s *userServer) GetPaginationUsers( - ctx context.Context, - req *pbuser.GetPaginationUsersReq, -) (resp *pbuser.GetPaginationUsersResp, err error) { +func (s *userServer) GetPaginationUsers(ctx context.Context, req *pbuser.GetPaginationUsersReq) (resp *pbuser.GetPaginationUsersResp, err error) { var pageNumber, showNumber int32 if req.Pagination != nil { pageNumber = req.Pagination.PageNumber @@ -195,17 +162,14 @@ func (s *userServer) GetPaginationUsers( return &pbuser.GetPaginationUsersResp{Total: int32(total), Users: convert.UsersDB2Pb(users)}, err } -func (s *userServer) UserRegister( - ctx context.Context, - req *pbuser.UserRegisterReq, -) (resp *pbuser.UserRegisterResp, err error) { +func (s *userServer) UserRegister(ctx context.Context, req *pbuser.UserRegisterReq) (resp *pbuser.UserRegisterResp, err error) { resp = &pbuser.UserRegisterResp{} if len(req.Users) == 0 { return nil, errs.ErrArgs.Wrap("users is empty") } if req.Secret != config.Config.Secret { log.ZDebug(ctx, "UserRegister", config.Config.Secret, req.Secret) - return nil, errs.ErrIdentity.Wrap("secret invalid") + return nil, errs.ErrNoPermission.Wrap("secret invalid") } if utils.DuplicateAny(req.Users, func(e *sdkws.UserInfo) string { return e.UserID }) { return nil, errs.ErrArgs.Wrap("userID repeated") @@ -246,10 +210,7 @@ func (s *userServer) UserRegister( return resp, nil } -func (s *userServer) GetGlobalRecvMessageOpt( - ctx context.Context, - req *pbuser.GetGlobalRecvMessageOptReq, -) (resp *pbuser.GetGlobalRecvMessageOptResp, err error) { +func (s *userServer) GetGlobalRecvMessageOpt(ctx context.Context, req *pbuser.GetGlobalRecvMessageOptReq) (resp *pbuser.GetGlobalRecvMessageOptResp, err error) { user, err := s.FindWithError(ctx, []string{req.UserID}) if err != nil { return nil, err @@ -257,10 +218,7 @@ func (s *userServer) GetGlobalRecvMessageOpt( return &pbuser.GetGlobalRecvMessageOptResp{GlobalRecvMsgOpt: user[0].GlobalRecvMsgOpt}, nil } -func (s *userServer) GetAllUserID( - ctx context.Context, - req *pbuser.GetAllUserIDReq, -) (resp *pbuser.GetAllUserIDResp, err error) { +func (s *userServer) GetAllUserID(ctx context.Context, req *pbuser.GetAllUserIDReq) (resp *pbuser.GetAllUserIDResp, err error) { userIDs, err := s.UserDatabase.GetAllUserID(ctx) if err != nil { return nil, err diff --git a/internal/tools/conversation.go b/internal/tools/conversation.go new file mode 100644 index 000000000..1cad58248 --- /dev/null +++ b/internal/tools/conversation.go @@ -0,0 +1,38 @@ +package tools + +import ( + "context" + "time" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" + "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" +) + +func (c *MsgTool) ConversationsDestructMsgs() { + log.ZInfo(context.Background(), "start msg destruct cron task") + ctx := mcontext.NewCtx(utils.GetSelfFuncName()) + conversations, err := c.conversationDatabase.GetConversationIDsNeedDestruct(ctx) + if err != nil { + log.ZError(ctx, "get conversation id need destruct failed", err) + return + } + log.ZDebug(context.Background(), "nums conversations need destruct", "nums", len(conversations)) + for _, conversation := range conversations { + log.ZDebug(ctx, "UserMsgsDestruct", "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID, "msgDestructTime", conversation.MsgDestructTime, "lastMsgDestructTime", conversation.LatestMsgDestructTime) + seqs, err := c.msgDatabase.UserMsgsDestruct(ctx, conversation.OwnerUserID, conversation.ConversationID, conversation.MsgDestructTime, conversation.LatestMsgDestructTime) + if err != nil { + log.ZError(ctx, "user msg destruct failed", err, "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID) + continue + } + if err := c.conversationDatabase.UpdateUsersConversationFiled(ctx, []string{conversation.OwnerUserID}, conversation.ConversationID, map[string]interface{}{"latest_msg_destruct_time": time.Now()}); err != nil { + log.ZError(ctx, "updateUsersConversationFiled failed", err, "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID) + continue + } + if len(seqs) > 0 { + if err := c.msgNotificationSender.UserDeleteMsgsNotification(ctx, conversation.OwnerUserID, conversation.ConversationID, seqs); err != nil { + log.ZError(ctx, "userDeleteMsgsNotification failed", err, "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID) + } + } + } +} diff --git a/internal/tools/cron_task.go b/internal/tools/cron_task.go index 365c6cbf4..5e4183615 100644 --- a/internal/tools/cron_task.go +++ b/internal/tools/cron_task.go @@ -26,7 +26,6 @@ import ( ) func StartCronTask() error { - log.ZInfo(context.Background(), "start cron task", "cron config", config.Config.ChatRecordsClearTime) fmt.Println("cron task start, config", config.Config.ChatRecordsClearTime) msgTool, err := InitMsgTool() if err != nil { @@ -35,10 +34,17 @@ func StartCronTask() error { c := cron.New() var wg sync.WaitGroup wg.Add(1) + log.ZInfo(context.Background(), "start chatRecordsClearTime cron task", "cron config", config.Config.ChatRecordsClearTime) _, err = c.AddFunc(config.Config.ChatRecordsClearTime, msgTool.AllConversationClearMsgAndFixSeq) if err != nil { - fmt.Println("start cron failed", err.Error(), config.Config.ChatRecordsClearTime) - return err + fmt.Println("start allConversationClearMsgAndFixSeq cron failed", err.Error(), config.Config.ChatRecordsClearTime) + panic(err) + } + log.ZInfo(context.Background(), "start msgDestruct cron task", "cron config", config.Config.MsgDestructTime) + _, err = c.AddFunc(config.Config.MsgDestructTime, msgTool.ConversationsDestructMsgs) + if err != nil { + fmt.Println("start conversationsDestructMsgs cron failed", err.Error(), config.Config.ChatRecordsClearTime) + panic(err) } c.Start() wg.Wait() diff --git a/internal/tools/msg.go b/internal/tools/msg.go index 8389352a8..7247d32eb 100644 --- a/internal/tools/msg.go +++ b/internal/tools/msg.go @@ -1,24 +1,10 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package tools import ( "context" - "errors" "fmt" "math" + "time" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" @@ -28,29 +14,33 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/unrelation" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mw" + "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry/zookeeper" + "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" + "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" + "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient/notification" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" + "github.com/redis/go-redis/v9" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" ) type MsgTool struct { - msgDatabase controller.CommonMsgDatabase - conversationDatabase controller.ConversationDatabase - userDatabase controller.UserDatabase - groupDatabase controller.GroupDatabase + msgDatabase controller.CommonMsgDatabase + conversationDatabase controller.ConversationDatabase + userDatabase controller.UserDatabase + groupDatabase controller.GroupDatabase + msgNotificationSender *notification.MsgNotificationSender } -var errSeq = errors.New("cache max seq and mongo max seq is diff > 10") - -func NewMsgTool( - msgDatabase controller.CommonMsgDatabase, - userDatabase controller.UserDatabase, - groupDatabase controller.GroupDatabase, - conversationDatabase controller.ConversationDatabase, -) *MsgTool { +func NewMsgTool(msgDatabase controller.CommonMsgDatabase, userDatabase controller.UserDatabase, + groupDatabase controller.GroupDatabase, conversationDatabase controller.ConversationDatabase, msgNotificationSender *notification.MsgNotificationSender) *MsgTool { return &MsgTool{ - msgDatabase: msgDatabase, - userDatabase: userDatabase, - groupDatabase: groupDatabase, - conversationDatabase: conversationDatabase, + msgDatabase: msgDatabase, + userDatabase: userDatabase, + groupDatabase: groupDatabase, + conversationDatabase: conversationDatabase, + msgNotificationSender: msgNotificationSender, } } @@ -67,20 +57,21 @@ func InitMsgTool() (*MsgTool, error) { if err != nil { return nil, err } + discov, err := zookeeper.NewClient(config.Config.Zookeeper.ZkAddr, config.Config.Zookeeper.Schema, + zookeeper.WithFreq(time.Hour), zookeeper.WithRoundRobin(), zookeeper.WithUserNameAndPassword(config.Config.Zookeeper.Username, + config.Config.Zookeeper.Password), zookeeper.WithTimeout(10), zookeeper.WithLogger(log.NewZkLogger())) + if err != nil { + return nil, err + } + discov.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials())) userDB := relation.NewUserGorm(db) msgDatabase := controller.InitCommonMsgDatabase(rdb, mongo.GetDatabase()) - userDatabase := controller.NewUserDatabase( - userDB, - cache.NewUserCacheRedis(rdb, relation.NewUserGorm(db), cache.GetDefaultOpt()), - tx.NewGorm(db), - ) + userDatabase := controller.NewUserDatabase(userDB, cache.NewUserCacheRedis(rdb, relation.NewUserGorm(db), cache.GetDefaultOpt()), tx.NewGorm(db)) groupDatabase := controller.InitGroupDatabase(db, rdb, mongo.GetDatabase()) - conversationDatabase := controller.NewConversationDatabase( - relation.NewConversationGorm(db), - cache.NewConversationRedis(rdb, cache.GetDefaultOpt(), relation.NewConversationGorm(db)), - tx.NewGorm(db), - ) - msgTool := NewMsgTool(msgDatabase, userDatabase, groupDatabase, conversationDatabase) + conversationDatabase := controller.NewConversationDatabase(relation.NewConversationGorm(db), cache.NewConversationRedis(rdb, cache.GetDefaultOpt(), relation.NewConversationGorm(db)), tx.NewGorm(db)) + msgRpcClient := rpcclient.NewMessageRpcClient(discov) + msgNotificationSender := notification.NewMsgNotificationSender(rpcclient.WithRpcClient(&msgRpcClient)) + msgTool := NewMsgTool(msgDatabase, userDatabase, groupDatabase, conversationDatabase, msgNotificationSender) return msgTool, nil } @@ -102,30 +93,21 @@ func (c *MsgTool) AllConversationClearMsgAndFixSeq() { func (c *MsgTool) ClearConversationsMsg(ctx context.Context, conversationIDs []string) { for _, conversationID := range conversationIDs { if err := c.msgDatabase.DeleteConversationMsgsAndSetMinSeq(ctx, conversationID, int64(config.Config.RetainChatRecords*24*60*60)); err != nil { - log.ZError( - ctx, - "DeleteUserSuperGroupMsgsAndSetMinSeq failed", - err, - "conversationID", - conversationID, - "DBRetainChatRecords", - config.Config.RetainChatRecords, - ) + log.ZError(ctx, "DeleteUserSuperGroupMsgsAndSetMinSeq failed", err, "conversationID", conversationID, "DBRetainChatRecords", config.Config.RetainChatRecords) } if err := c.checkMaxSeq(ctx, conversationID); err != nil { log.ZError(ctx, "fixSeq failed", err, "conversationID", conversationID) } - } } func (c *MsgTool) checkMaxSeqWithMongo(ctx context.Context, conversationID string, maxSeqCache int64) error { - maxSeqMongo, _, err := c.msgDatabase.GetMongoMaxAndMinSeq(ctx, conversationID) + minSeqMongo, maxSeqMongo, err := c.msgDatabase.GetMongoMaxAndMinSeq(ctx, conversationID) if err != nil { return err } if math.Abs(float64(maxSeqMongo-maxSeqCache)) > 10 { - return errSeq + log.ZError(ctx, "cache max seq and mongo max seq is diff > 10", nil, "maxSeqMongo", maxSeqMongo, "minSeqMongo", minSeqMongo, "maxSeqCache", maxSeqCache, "conversationID", conversationID) } return nil } @@ -133,6 +115,9 @@ func (c *MsgTool) checkMaxSeqWithMongo(ctx context.Context, conversationID strin func (c *MsgTool) checkMaxSeq(ctx context.Context, conversationID string) error { maxSeq, err := c.msgDatabase.GetMaxSeq(ctx, conversationID) if err != nil { + if errs.Unwrap(err) == redis.Nil { + return nil + } return err } if err := c.checkMaxSeqWithMongo(ctx, conversationID, maxSeq); err != nil { diff --git a/pkg/apiresp/format.go b/pkg/apiresp/format.go new file mode 100644 index 000000000..34fb85464 --- /dev/null +++ b/pkg/apiresp/format.go @@ -0,0 +1,5 @@ +package apiresp + +type ApiFormat interface { + ApiFormat() +} diff --git a/pkg/apiresp/resp.go b/pkg/apiresp/resp.go index a7bd271a3..638c70dbe 100644 --- a/pkg/apiresp/resp.go +++ b/pkg/apiresp/resp.go @@ -49,6 +49,9 @@ func isAllFieldsPrivate(v any) bool { } func ApiSuccess(data any) *ApiResponse { + if format, ok := data.(ApiFormat); ok { + format.ApiFormat() + } if isAllFieldsPrivate(data) { return &ApiResponse{} } diff --git a/pkg/apistruct/msg.go b/pkg/apistruct/msg.go index 4b776cb48..274c31a61 100644 --- a/pkg/apistruct/msg.go +++ b/pkg/apistruct/msg.go @@ -1,27 +1,8 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package apistruct -import ( - "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" - sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" -) - type DelMsgReq struct { - UserID string `json:"userID,omitempty" binding:"required"` - SeqList []uint32 `json:"seqList,omitempty" binding:"required"` + UserID string `json:"userID,omitempty" binding:"required"` + SeqList []uint32 `json:"seqList,omitempty" binding:"required"` OperationID string `json:"operationID,omitempty" binding:"required"` } @@ -29,19 +10,19 @@ type DelMsgResp struct { } type CleanUpMsgReq struct { - UserID string `json:"userID" binding:"required"` - OperationID string `json:"operationID" binding:"required"` + UserID string `json:"userID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` } type CleanUpMsgResp struct { } type DelSuperGroupMsgReq struct { - UserID string `json:"userID" binding:"required"` - GroupID string `json:"groupID" binding:"required"` + UserID string `json:"userID" binding:"required"` + GroupID string `json:"groupID" binding:"required"` SeqList []uint32 `json:"seqList,omitempty"` IsAllDelete bool `json:"isAllDelete"` - OperationID string `json:"operationID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` } type DelSuperGroupMsgResp struct { @@ -54,92 +35,28 @@ type MsgDeleteNotificationElem struct { } type SetMsgMinSeqReq struct { - UserID string `json:"userID" binding:"required"` + UserID string `json:"userID" binding:"required"` GroupID string `json:"groupID"` - MinSeq uint32 `json:"minSeq" binding:"required"` - OperationID string `json:"operationID" binding:"required"` + MinSeq uint32 `json:"minSeq" binding:"required"` + OperationID string `json:"operationID" binding:"required"` } type SetMsgMinSeqResp struct { } -type ModifyMessageReactionExtensionsReq struct { - OperationID string `json:"operationID" binding:"required"` - conversationID string `json:"conversationID" binding:"required"` - SessionType int32 `json:"sessionType" binding:"required"` - ReactionExtensionList map[string]*sdkws.KeyValue `json:"reactionExtensionList,omitempty" binding:"required"` - ClientMsgID string `json:"clientMsgID" binding:"required"` - Ex *string `json:"ex"` - AttachedInfo *string `json:"attachedInfo"` - IsReact bool `json:"isReact"` - IsExternalExtensions bool `json:"isExternalExtensions"` - MsgFirstModifyTime int64 `json:"msgFirstModifyTime"` -} - -type ModifyMessageReactionExtensionsResp struct { - Data struct { - ResultKeyValue []*msg.KeyValueResp `json:"result"` - MsgFirstModifyTime int64 `json:"msgFirstModifyTime"` - IsReact bool `json:"isReact"` - } `json:"data"` -} - -//type OperateMessageListReactionExtensionsReq struct { -// OperationID string `json:"operationID" -// binding:"required"` conversationID string -// `json:"conversationID" binding:"required"` SessionType string -// `json:"sessionType" binding:"required"` MessageReactionKeyList -// []*msg.GetMessageListReactionExtensionsReq_MessageReactionKey `json:"messageReactionKeyList" binding:"required"` -//} - -type OperateMessageListReactionExtensionsResp struct { - Data struct { - SuccessList []*msg.ExtendMsgResp `json:"successList"` - FailedList []*msg.ExtendMsgResp `json:"failedList"` - } `json:"data"` -} - -type SetMessageReactionExtensionsCallbackReq ModifyMessageReactionExtensionsReq - -type SetMessageReactionExtensionsCallbackResp ModifyMessageReactionExtensionsResp - -//type GetMessageListReactionExtensionsReq OperateMessageListReactionExtensionsReq - -type GetMessageListReactionExtensionsResp struct { - Data []*msg.SingleMessageExtensionResult `json:"data"` -} - -type AddMessageReactionExtensionsReq ModifyMessageReactionExtensionsReq - -type AddMessageReactionExtensionsResp ModifyMessageReactionExtensionsResp - -type DeleteMessageReactionExtensionsReq struct { - OperationID string `json:"operationID" binding:"required"` - conversationID string `json:"conversationID" binding:"required"` - SessionType int32 `json:"sessionType" binding:"required"` - ClientMsgID string `json:"clientMsgID" binding:"required"` - IsExternalExtensions bool `json:"isExternalExtensions"` - MsgFirstModifyTime int64 `json:"msgFirstModifyTime" binding:"required"` - ReactionExtensionList []*sdkws.KeyValue `json:"reactionExtensionList" binding:"required"` -} - -type DeleteMessageReactionExtensionsResp struct { - Data []*msg.KeyValueResp -} - type PictureBaseInfo struct { UUID string `mapstructure:"uuid"` - Type string `mapstructure:"type"` - Size int64 `mapstructure:"size"` - Width int32 `mapstructure:"width"` + Type string `mapstructure:"type" ` + Size int64 `mapstructure:"size" ` + Width int32 `mapstructure:"width" ` Height int32 `mapstructure:"height"` - Url string `mapstructure:"url"` + Url string `mapstructure:"url" ` } type PictureElem struct { SourcePath string `mapstructure:"sourcePath"` SourcePicture PictureBaseInfo `mapstructure:"sourcePicture"` - BigPicture PictureBaseInfo `mapstructure:"bigPicture"` + BigPicture PictureBaseInfo `mapstructure:"bigPicture" ` SnapshotPicture PictureBaseInfo `mapstructure:"snapshotPicture"` } type SoundElem struct { @@ -181,35 +98,35 @@ type LocationElem struct { Latitude float64 `mapstructure:"latitude"` } type CustomElem struct { - Data string `mapstructure:"data" validate:"required"` + Data string `mapstructure:"data" validate:"required"` Description string `mapstructure:"description"` Extension string `mapstructure:"extension"` } type TextElem struct { - Text string `mapstructure:"text" validate:"required"` + Content string `mapstructure:"content" validate:"required"` } type RevokeElem struct { RevokeMsgClientID string `mapstructure:"revokeMsgClientID" validate:"required"` } type OANotificationElem struct { - NotificationName string `mapstructure:"notificationName" json:"notificationName" validate:"required"` + NotificationName string `mapstructure:"notificationName" json:"notificationName" validate:"required"` NotificationFaceURL string `mapstructure:"notificationFaceURL" json:"notificationFaceURL"` - NotificationType int32 `mapstructure:"notificationType" json:"notificationType" validate:"required"` - Text string `mapstructure:"text" json:"text" validate:"required"` - Url string `mapstructure:"url" json:"url"` - MixType int32 `mapstructure:"mixType" json:"mixType"` - PictureElem PictureElem `mapstructure:"pictureElem" json:"pictureElem"` - SoundElem SoundElem `mapstructure:"soundElem" json:"soundElem"` - VideoElem VideoElem `mapstructure:"videoElem" json:"videoElem"` - FileElem FileElem `mapstructure:"fileElem" json:"fileElem"` - Ex string `mapstructure:"ex" json:"ex"` + NotificationType int32 `mapstructure:"notificationType" json:"notificationType" validate:"required"` + Text string `mapstructure:"text" json:"text" validate:"required"` + Url string `mapstructure:"url" json:"url"` + MixType int32 `mapstructure:"mixType" json:"mixType"` + PictureElem PictureElem `mapstructure:"pictureElem" json:"pictureElem"` + SoundElem SoundElem `mapstructure:"soundElem" json:"soundElem"` + VideoElem VideoElem `mapstructure:"videoElem" json:"videoElem"` + FileElem FileElem `mapstructure:"fileElem" json:"fileElem"` + Ex string `mapstructure:"ex" json:"ex"` } type MessageRevoked struct { - RevokerID string `mapstructure:"revokerID" json:"revokerID" validate:"required"` - RevokerRole int32 `mapstructure:"revokerRole" json:"revokerRole" validate:"required"` - ClientMsgID string `mapstructure:"clientMsgID" json:"clientMsgID" validate:"required"` + RevokerID string `mapstructure:"revokerID" json:"revokerID" validate:"required"` + RevokerRole int32 `mapstructure:"revokerRole" json:"revokerRole" validate:"required"` + ClientMsgID string `mapstructure:"clientMsgID" json:"clientMsgID" validate:"required"` RevokerNickname string `mapstructure:"revokerNickname" json:"revokerNickname"` - SessionType int32 `mapstructure:"sessionType" json:"sessionType" validate:"required"` - Seq uint32 `mapstructure:"seq" json:"seq" validate:"required"` + SessionType int32 `mapstructure:"sessionType" json:"sessionType" validate:"required"` + Seq uint32 `mapstructure:"seq" json:"seq" validate:"required"` } diff --git a/pkg/callbackstruct/message.go b/pkg/callbackstruct/message.go index 677e70f27..dabdc1d55 100644 --- a/pkg/callbackstruct/message.go +++ b/pkg/callbackstruct/message.go @@ -15,7 +15,6 @@ package callbackstruct import ( - "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" ) @@ -80,71 +79,3 @@ type CallbackMsgModifyCommandResp struct { AttachedInfo *string `json:"attachedInfo"` Ex *string `json:"ex"` } -type CallbackBeforeSetMessageReactionExtReq struct { - OperationID string `json:"operationID"` - CallbackCommand `json:"callbackCommand"` - ConversationID string `json:"conversationID"` - OpUserID string `json:"opUserID"` - SessionType int32 `json:"sessionType"` - ReactionExtensionList map[string]*sdkws.KeyValue `json:"reactionExtensionList"` - ClientMsgID string `json:"clientMsgID"` - IsReact bool `json:"isReact"` - IsExternalExtensions bool `json:"isExternalExtensions"` - MsgFirstModifyTime int64 `json:"msgFirstModifyTime"` -} -type CallbackBeforeSetMessageReactionExtResp struct { - CommonCallbackResp - ResultReactionExtensionList []*msg.KeyValueResp `json:"resultReactionExtensionList"` - MsgFirstModifyTime int64 `json:"msgFirstModifyTime"` -} -type CallbackDeleteMessageReactionExtReq struct { - CallbackCommand `json:"callbackCommand"` - OperationID string `json:"operationID"` - ConversationID string `json:"conversationID"` - OpUserID string `json:"opUserID"` - SessionType int32 `json:"sessionType"` - ReactionExtensionList []*sdkws.KeyValue `json:"reactionExtensionList"` - ClientMsgID string `json:"clientMsgID"` - IsExternalExtensions bool `json:"isExternalExtensions"` - MsgFirstModifyTime int64 `json:"msgFirstModifyTime"` -} -type CallbackDeleteMessageReactionExtResp struct { - CommonCallbackResp - ResultReactionExtensionList []*msg.KeyValueResp `json:"resultReactionExtensionList"` - MsgFirstModifyTime int64 `json:"msgFirstModifyTime"` -} - -type CallbackGetMessageListReactionExtReq struct { - OperationID string `json:"operationID"` - CallbackCommand `json:"callbackCommand"` - ConversationID string `json:"conversationID"` - OpUserID string `json:"opUserID"` - SessionType int32 `json:"sessionType"` - TypeKeyList []string `json:"typeKeyList"` - //MessageKeyList []*msg.GetMessageListReactionExtensionsReq_MessageReactionKey `json:"messageKeyList"` -} - -type CallbackGetMessageListReactionExtResp struct { - CommonCallbackResp - MessageResultList []*msg.SingleMessageExtensionResult `json:"messageResultList"` -} - -type CallbackAddMessageReactionExtReq struct { - OperationID string `json:"operationID"` - CallbackCommand `json:"callbackCommand"` - ConversationID string `json:"conversationID"` - OpUserID string `json:"opUserID"` - SessionType int32 `json:"sessionType"` - ReactionExtensionList map[string]*sdkws.KeyValue `json:"reactionExtensionList"` - ClientMsgID string `json:"clientMsgID"` - IsReact bool `json:"isReact"` - IsExternalExtensions bool `json:"isExternalExtensions"` - MsgFirstModifyTime int64 `json:"msgFirstModifyTime"` -} - -type CallbackAddMessageReactionExtResp struct { - CommonCallbackResp - ResultReactionExtensionList []*msg.KeyValueResp `json:"resultReactionExtensionList"` - IsReact bool `json:"isReact"` - MsgFirstModifyTime int64 `json:"msgFirstModifyTime"` -} diff --git a/pkg/common/cmd/cron_task.go b/pkg/common/cmd/cron_task.go index c26cf2e4f..25dc9aae4 100644 --- a/pkg/common/cmd/cron_task.go +++ b/pkg/common/cmd/cron_task.go @@ -21,7 +21,7 @@ type CronTaskCmd struct { } func NewCronTaskCmd() *CronTaskCmd { - return &CronTaskCmd{NewRootCmd("cronTask")} + return &CronTaskCmd{NewRootCmd("cronTask", WithCronTaskLogName())} } func (c *CronTaskCmd) addRunE(f func() error) { diff --git a/pkg/common/cmd/root.go b/pkg/common/cmd/root.go index b15d984a0..c5fce2c95 100644 --- a/pkg/common/cmd/root.go +++ b/pkg/common/cmd/root.go @@ -31,17 +31,40 @@ type RootCmd struct { prometheusPort int } -func NewRootCmd(name string) (rootCmd *RootCmd) { +type CmdOpts struct { + loggerPrefixName string +} + +func WithCronTaskLogName() func(*CmdOpts) { + return func(opts *CmdOpts) { + opts.loggerPrefixName = "OpenIM.CronTask.log.all" + } +} + +func WithLogName(logName string) func(*CmdOpts) { + return func(opts *CmdOpts) { + opts.loggerPrefixName = logName + } +} + +func NewRootCmd(name string, opts ...func(*CmdOpts)) (rootCmd *RootCmd) { rootCmd = &RootCmd{Name: name} c := cobra.Command{ - Use: "start", - Short: fmt.Sprintf(`Start %s server`, name), - Long: fmt.Sprintf(`Start %s server`, name), + Use: "start openIM application", + Short: fmt.Sprintf(`Start %s `, name), + Long: fmt.Sprintf(`Start %s `, name), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { if err := rootCmd.getConfFromCmdAndInit(cmd); err != nil { panic(err) } - if err := log.InitFromConfig("OpenIM.log.all", name, config.Config.Log.RemainLogLevel, config.Config.Log.IsStdout, config.Config.Log.IsJson, config.Config.Log.StorageLocation, config.Config.Log.RemainRotationCount); err != nil { + cmdOpts := &CmdOpts{} + for _, opt := range opts { + opt(cmdOpts) + } + if cmdOpts.loggerPrefixName == "" { + cmdOpts.loggerPrefixName = "OpenIM.log.all" + } + if err := log.InitFromConfig(cmdOpts.loggerPrefixName, name, config.Config.Log.RemainLogLevel, config.Config.Log.IsStdout, config.Config.Log.IsJson, config.Config.Log.StorageLocation, config.Config.Log.RemainRotationCount); err != nil { panic(err) } return nil diff --git a/pkg/common/cmd/rpc.go b/pkg/common/cmd/rpc.go index bfaef5305..8ff4195d2 100644 --- a/pkg/common/cmd/rpc.go +++ b/pkg/common/cmd/rpc.go @@ -1,27 +1,12 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package cmd import ( "errors" - "github.com/spf13/cobra" - "google.golang.org/grpc" - "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/OpenIMSDK/Open-IM-Server/pkg/startrpc" + "github.com/spf13/cobra" + "google.golang.org/grpc" ) type RpcCmd struct { @@ -41,10 +26,7 @@ func (a *RpcCmd) Exec() error { return a.Execute() } -func (a *RpcCmd) StartSvr( - name string, - rpcFn func(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error, -) error { +func (a *RpcCmd) StartSvr(name string, rpcFn func(discov discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error) error { if a.GetPortFlag() == 0 { return errors.New("port is required") } diff --git a/pkg/common/config/config.go b/pkg/common/config/config.go index eef803e12..ae5599891 100644 --- a/pkg/common/config/config.go +++ b/pkg/common/config/config.go @@ -91,15 +91,11 @@ type config struct { MsgToPush struct { Topic string `yaml:"topic"` } `yaml:"msgToPush"` - MsgToModify struct { - Topic string `yaml:"topic"` - } `yaml:"msgToModify"` ConsumerGroupID struct { - MsgToRedis string `yaml:"msgToRedis"` - MsgToMongo string `yaml:"msgToMongo"` - MsgToMySql string `yaml:"msgToMySql"` - MsgToPush string `yaml:"msgToPush"` - MsgToModify string `yaml:"msgToModify"` + MsgToRedis string `yaml:"msgToRedis"` + MsgToMongo string `yaml:"msgToMongo"` + MsgToMySql string `yaml:"msgToMySql"` + MsgToPush string `yaml:"msgToPush"` } `yaml:"consumerGroupID"` } `yaml:"kafka"` @@ -117,42 +113,26 @@ type config struct { Enable string `yaml:"enable"` ApiURL string `yaml:"apiURL"` Minio struct { - TempBucket string `yaml:"tempBucket"` - DataBucket string `yaml:"dataBucket"` - Location string `yaml:"location"` - Endpoint string `yaml:"endpoint"` - AccessKeyID string `yaml:"accessKeyID"` - SecretAccessKey string `yaml:"secretAccessKey"` - IsDistributedMod bool `yaml:"isDistributedMod"` + Bucket string `yaml:"bucket"` + Endpoint string `yaml:"endpoint"` + AccessKeyID string `yaml:"accessKeyID"` + SecretAccessKey string `yaml:"secretAccessKey"` + SessionToken string `yaml:"sessionToken"` } `yaml:"minio"` - Tencent struct { - AppID string `yaml:"appID"` - Region string `yaml:"region"` - Bucket string `yaml:"bucket"` - SecretID string `yaml:"secretID"` - SecretKey string `yaml:"secretKey"` - } `yaml:"tencent"` - Ali struct { - RegionID string `yaml:"regionID"` - AccessKeyID string `yaml:"accessKeyID"` - AccessKeySecret string `yaml:"accessKeySecret"` - StsEndpoint string `yaml:"stsEndpoint"` - OssEndpoint string `yaml:"ossEndpoint"` - Bucket string `yaml:"bucket"` - FinalHost string `yaml:"finalHost"` - StsDurationSeconds int64 `yaml:"stsDurationSeconds"` - OssRoleArn string `yaml:"OssRoleArn"` - } `yaml:"ali"` - Aws struct { + Cos struct { + BucketURL string `yaml:"bucketURL"` + SecretID string `yaml:"secretID"` + SecretKey string `yaml:"secretKey"` + SessionToken string `yaml:"sessionToken"` + } `yaml:"cos"` + Oss struct { + Endpoint string `yaml:"endpoint"` + Bucket string `yaml:"bucket"` + BucketURL string `yaml:"bucketURL"` AccessKeyID string `yaml:"accessKeyID"` AccessKeySecret string `yaml:"accessKeySecret"` - Region string `yaml:"region"` - Bucket string `yaml:"bucket"` - FinalHost string `yaml:"finalHost"` - RoleArn string `yaml:"roleArn"` - ExternalId string `yaml:"externalId"` - RoleSessionName string `yaml:"roleSessionName"` - } `yaml:"aws"` + SessionToken string `yaml:"sessionToken"` + } `yaml:"oss"` } `yaml:"object"` RpcPort struct { @@ -229,6 +209,7 @@ type config struct { SingleMessageHasReadReceiptEnable bool `yaml:"singleMessageHasReadReceiptEnable"` RetainChatRecords int `yaml:"retainChatRecords"` ChatRecordsClearTime string `yaml:"chatRecordsClearTime"` + MsgDestructTime string `yaml:"msgDestructTime"` Secret string `yaml:"secret"` TokenPolicy struct { Expire int64 `yaml:"expire"` diff --git a/pkg/common/convert/conversation.go b/pkg/common/convert/conversation.go index f00cae58f..4bb051a7c 100644 --- a/pkg/common/convert/conversation.go +++ b/pkg/common/convert/conversation.go @@ -22,6 +22,7 @@ import ( func ConversationDB2Pb(conversationDB *relation.ConversationModel) *conversation.Conversation { conversationPB := &conversation.Conversation{} + conversationPB.LatestMsgDestructTime = conversationDB.LatestMsgDestructTime.Unix() if err := utils.CopyStructFields(conversationPB, conversationDB); err != nil { return nil } @@ -34,6 +35,7 @@ func ConversationsDB2Pb(conversationsDB []*relation.ConversationModel) (conversa if err := utils.CopyStructFields(conversationPB, conversationDB); err != nil { continue } + conversationPB.LatestMsgDestructTime = conversationDB.LatestMsgDestructTime.Unix() conversationsPB = append(conversationsPB, conversationPB) } return conversationsPB diff --git a/pkg/common/db/cache/extend_msg_set.go b/pkg/common/db/cache/extend_msg_set.go deleted file mode 100644 index d7750e66b..000000000 --- a/pkg/common/db/cache/extend_msg_set.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cache - -import ( - "context" - "time" - - "github.com/dtm-labs/rockscache" - "github.com/redis/go-redis/v9" - - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" -) - -const ( - extendMsgSetCache = "EXTEND_MSG_SET_CACHE:" - extendMsgCache = "EXTEND_MSG_CACHE:" -) - -type ExtendMsgSetCache interface { - metaCache - NewCache() ExtendMsgSetCache - GetExtendMsg( - ctx context.Context, - conversationID string, - sessionType int32, - clientMsgID string, - firstModifyTime int64, - ) (extendMsg *unrelation.ExtendMsgModel, err error) - DelExtendMsg(clientMsgID string) ExtendMsgSetCache -} - -type ExtendMsgSetCacheRedis struct { - metaCache - expireTime time.Duration - rcClient *rockscache.Client - extendMsgSetDB unrelation.ExtendMsgSetModelInterface -} - -func NewExtendMsgSetCacheRedis( - rdb redis.UniversalClient, - extendMsgSetDB unrelation.ExtendMsgSetModelInterface, - options rockscache.Options, -) ExtendMsgSetCache { - rcClient := rockscache.NewClient(rdb, options) - return &ExtendMsgSetCacheRedis{ - metaCache: NewMetaCacheRedis(rcClient), - expireTime: time.Second * 30 * 60, - extendMsgSetDB: extendMsgSetDB, - rcClient: rcClient, - } -} - -func (e *ExtendMsgSetCacheRedis) NewCache() ExtendMsgSetCache { - return &ExtendMsgSetCacheRedis{ - metaCache: NewMetaCacheRedis(e.rcClient, e.metaCache.GetPreDelKeys()...), - expireTime: e.expireTime, - extendMsgSetDB: e.extendMsgSetDB, - rcClient: e.rcClient, - } -} - -func (e *ExtendMsgSetCacheRedis) getKey(clientMsgID string) string { - return extendMsgCache + clientMsgID -} - -func (e *ExtendMsgSetCacheRedis) GetExtendMsg( - ctx context.Context, - conversationID string, - sessionType int32, - clientMsgID string, - firstModifyTime int64, -) (extendMsg *unrelation.ExtendMsgModel, err error) { - return getCache( - ctx, - e.rcClient, - e.getKey(clientMsgID), - e.expireTime, - func(ctx context.Context) (*unrelation.ExtendMsgModel, error) { - return e.extendMsgSetDB.TakeExtendMsg(ctx, conversationID, sessionType, clientMsgID, firstModifyTime) - }, - ) -} - -func (e *ExtendMsgSetCacheRedis) DelExtendMsg(clientMsgID string) ExtendMsgSetCache { - new := e.NewCache() - new.AddKeys(e.getKey(clientMsgID)) - return new -} diff --git a/pkg/common/db/controller/conversation.go b/pkg/common/db/controller/conversation.go index 8e7b7a350..7908d163a 100644 --- a/pkg/common/db/controller/conversation.go +++ b/pkg/common/db/controller/conversation.go @@ -1,21 +1,8 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package controller import ( "context" + "time" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" @@ -27,22 +14,13 @@ import ( type ConversationDatabase interface { //UpdateUserConversationFiled 更新用户该会话的属性信息 - UpdateUsersConversationFiled( - ctx context.Context, - userIDs []string, - conversationID string, - args map[string]interface{}, - ) error + UpdateUsersConversationFiled(ctx context.Context, userIDs []string, conversationID string, args map[string]interface{}) error //CreateConversation 创建一批新的会话 CreateConversation(ctx context.Context, conversations []*relationTb.ConversationModel) error //SyncPeerUserPrivateConversation 同步对端私聊会话内部保证事务操作 SyncPeerUserPrivateConversationTx(ctx context.Context, conversation []*relationTb.ConversationModel) error //FindConversations 根据会话ID获取某个用户的多个会话 - FindConversations( - ctx context.Context, - ownerUserID string, - conversationIDs []string, - ) ([]*relationTb.ConversationModel, error) + FindConversations(ctx context.Context, ownerUserID string, conversationIDs []string) ([]*relationTb.ConversationModel, error) //FindRecvMsgNotNotifyUserIDs 获取超级大群开启免打扰的用户ID FindRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) ([]string, error) //GetUserAllConversation 获取一个用户在服务器上所有的会话 @@ -50,28 +28,17 @@ type ConversationDatabase interface { //SetUserConversations 设置用户多个会话属性,如果会话不存在则创建,否则更新,内部保证原子性 SetUserConversations(ctx context.Context, ownerUserID string, conversations []*relationTb.ConversationModel) error //SetUsersConversationFiledTx 设置多个用户会话关于某个字段的更新操作,如果会话不存在则创建,否则更新,内部保证事务操作 - SetUsersConversationFiledTx( - ctx context.Context, - userIDs []string, - conversation *relationTb.ConversationModel, - filedMap map[string]interface{}, - ) error + SetUsersConversationFiledTx(ctx context.Context, userIDs []string, conversation *relationTb.ConversationModel, filedMap map[string]interface{}) error CreateGroupChatConversation(ctx context.Context, groupID string, userIDs []string) error GetConversationIDs(ctx context.Context, userID string) ([]string, error) GetUserConversationIDsHash(ctx context.Context, ownerUserID string) (hash uint64, err error) GetAllConversationIDs(ctx context.Context) ([]string, error) GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (map[string]int64, error) - GetConversationsByConversationID( - ctx context.Context, - conversationIDs []string, - ) ([]*relationTb.ConversationModel, error) + GetConversationsByConversationID(ctx context.Context, conversationIDs []string) ([]*relationTb.ConversationModel, error) + GetConversationIDsNeedDestruct(ctx context.Context) ([]*relationTb.ConversationModel, error) } -func NewConversationDatabase( - conversation relationTb.ConversationModelInterface, - cache cache.ConversationCache, - tx tx.Tx, -) ConversationDatabase { +func NewConversationDatabase(conversation relationTb.ConversationModelInterface, cache cache.ConversationCache, tx tx.Tx) ConversationDatabase { return &conversationDatabase{ conversationDB: conversation, cache: cache, @@ -85,12 +52,7 @@ type conversationDatabase struct { tx tx.Tx } -func (c *conversationDatabase) SetUsersConversationFiledTx( - ctx context.Context, - userIDs []string, - conversation *relationTb.ConversationModel, - filedMap map[string]interface{}, -) (err error) { +func (c *conversationDatabase) SetUsersConversationFiledTx(ctx context.Context, userIDs []string, conversation *relationTb.ConversationModel, filedMap map[string]interface{}) (err error) { cache := c.cache.NewCache() if err := c.tx.Transaction(func(tx any) error { conversationTx := c.conversationDB.NewTx(tx) @@ -113,12 +75,14 @@ func (c *conversationDatabase) SetUsersConversationFiledTx( NotUserIDs := utils.DifferenceString(haveUserIDs, userIDs) log.ZDebug(ctx, "SetUsersConversationFiledTx", "NotUserIDs", NotUserIDs, "haveUserIDs", haveUserIDs, "userIDs", userIDs) var conversations []*relationTb.ConversationModel + now := time.Now() for _, v := range NotUserIDs { temp := new(relationTb.ConversationModel) if err := utils.CopyStructFields(temp, conversation); err != nil { return err } temp.OwnerUserID = v + temp.CreateTime = now conversations = append(conversations, temp) } @@ -136,12 +100,7 @@ func (c *conversationDatabase) SetUsersConversationFiledTx( return cache.ExecDel(ctx) } -func (c *conversationDatabase) UpdateUsersConversationFiled( - ctx context.Context, - userIDs []string, - conversationID string, - args map[string]interface{}, -) error { +func (c *conversationDatabase) UpdateUsersConversationFiled(ctx context.Context, userIDs []string, conversationID string, args map[string]interface{}) error { _, err := c.conversationDB.UpdateByMap(ctx, userIDs, conversationID, args) if err != nil { return err @@ -149,10 +108,7 @@ func (c *conversationDatabase) UpdateUsersConversationFiled( return c.cache.DelUsersConversation(conversationID, userIDs...).ExecDel(ctx) } -func (c *conversationDatabase) CreateConversation( - ctx context.Context, - conversations []*relationTb.ConversationModel, -) error { +func (c *conversationDatabase) CreateConversation(ctx context.Context, conversations []*relationTb.ConversationModel) error { if err := c.conversationDB.Create(ctx, conversations); err != nil { return err } @@ -165,35 +121,34 @@ func (c *conversationDatabase) CreateConversation( return cache.DelConversationIDs(userIDs...).DelUserConversationIDsHash(userIDs...).ExecDel(ctx) } -func (c *conversationDatabase) SyncPeerUserPrivateConversationTx( - ctx context.Context, - conversations []*relationTb.ConversationModel, -) error { +func (c *conversationDatabase) SyncPeerUserPrivateConversationTx(ctx context.Context, conversations []*relationTb.ConversationModel) error { cache := c.cache.NewCache() if err := c.tx.Transaction(func(tx any) error { conversationTx := c.conversationDB.NewTx(tx) for _, conversation := range conversations { for _, v := range [][2]string{{conversation.OwnerUserID, conversation.UserID}, {conversation.UserID, conversation.OwnerUserID}} { - haveUserIDs, err := conversationTx.FindUserID(ctx, []string{v[0]}, []string{conversation.ConversationID}) + ownerUserID := v[0] + userID := v[1] + haveUserIDs, err := conversationTx.FindUserID(ctx, []string{ownerUserID}, []string{conversation.ConversationID}) if err != nil { return err } if len(haveUserIDs) > 0 { - _, err := conversationTx.UpdateByMap(ctx, []string{v[0]}, conversation.ConversationID, map[string]interface{}{"is_private_chat": conversation.IsPrivateChat}) + _, err := conversationTx.UpdateByMap(ctx, []string{ownerUserID}, conversation.ConversationID, map[string]interface{}{"is_private_chat": conversation.IsPrivateChat}) if err != nil { return err } - cache = cache.DelUsersConversation(conversation.ConversationID, v[0]) + cache = cache.DelUsersConversation(conversation.ConversationID, ownerUserID) } else { newConversation := *conversation - newConversation.OwnerUserID = v[0] - newConversation.UserID = v[1] + newConversation.OwnerUserID = ownerUserID + newConversation.UserID = userID newConversation.ConversationID = conversation.ConversationID newConversation.IsPrivateChat = conversation.IsPrivateChat if err := conversationTx.Create(ctx, []*relationTb.ConversationModel{&newConversation}); err != nil { return err } - cache = cache.DelConversationIDs(v[0]).DelUserConversationIDsHash(v[0]) + cache = cache.DelConversationIDs(ownerUserID).DelUserConversationIDsHash(ownerUserID) } } } @@ -201,37 +156,22 @@ func (c *conversationDatabase) SyncPeerUserPrivateConversationTx( }); err != nil { return err } - return c.cache.ExecDel(ctx) + return cache.ExecDel(ctx) } -func (c *conversationDatabase) FindConversations( - ctx context.Context, - ownerUserID string, - conversationIDs []string, -) ([]*relationTb.ConversationModel, error) { +func (c *conversationDatabase) FindConversations(ctx context.Context, ownerUserID string, conversationIDs []string) ([]*relationTb.ConversationModel, error) { return c.cache.GetConversations(ctx, ownerUserID, conversationIDs) } -func (c *conversationDatabase) GetConversation( - ctx context.Context, - ownerUserID string, - conversationID string, -) (*relationTb.ConversationModel, error) { +func (c *conversationDatabase) GetConversation(ctx context.Context, ownerUserID string, conversationID string) (*relationTb.ConversationModel, error) { return c.cache.GetConversation(ctx, ownerUserID, conversationID) } -func (c *conversationDatabase) GetUserAllConversation( - ctx context.Context, - ownerUserID string, -) ([]*relationTb.ConversationModel, error) { +func (c *conversationDatabase) GetUserAllConversation(ctx context.Context, ownerUserID string) ([]*relationTb.ConversationModel, error) { return c.cache.GetUserAllConversations(ctx, ownerUserID) } -func (c *conversationDatabase) SetUserConversations( - ctx context.Context, - ownerUserID string, - conversations []*relationTb.ConversationModel, -) error { +func (c *conversationDatabase) SetUserConversations(ctx context.Context, ownerUserID string, conversations []*relationTb.ConversationModel) error { cache := c.cache.NewCache() if err := c.tx.Transaction(func(tx any) error { var conversationIDs []string @@ -281,11 +221,7 @@ func (c *conversationDatabase) FindRecvMsgNotNotifyUserIDs(ctx context.Context, return c.cache.GetSuperGroupRecvMsgNotNotifyUserIDs(ctx, groupID) } -func (c *conversationDatabase) CreateGroupChatConversation( - ctx context.Context, - groupID string, - userIDs []string, -) error { +func (c *conversationDatabase) CreateGroupChatConversation(ctx context.Context, groupID string, userIDs []string) error { cache := c.cache.NewCache() conversationID := utils.GetConversationIDBySessionType(constant.SuperGroupChatType, groupID) if err := c.tx.Transaction(func(tx any) error { @@ -325,10 +261,7 @@ func (c *conversationDatabase) GetConversationIDs(ctx context.Context, userID st return c.cache.GetUserConversationIDs(ctx, userID) } -func (c *conversationDatabase) GetUserConversationIDsHash( - ctx context.Context, - ownerUserID string, -) (hash uint64, err error) { +func (c *conversationDatabase) GetUserConversationIDsHash(ctx context.Context, ownerUserID string) (hash uint64, err error) { return c.cache.GetUserConversationIDsHash(ctx, ownerUserID) } @@ -336,16 +269,14 @@ func (c *conversationDatabase) GetAllConversationIDs(ctx context.Context) ([]str return c.conversationDB.GetAllConversationIDs(ctx) } -func (c *conversationDatabase) GetUserAllHasReadSeqs( - ctx context.Context, - ownerUserID string, -) (map[string]int64, error) { +func (c *conversationDatabase) GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (map[string]int64, error) { return c.cache.GetUserAllHasReadSeqs(ctx, ownerUserID) } -func (c *conversationDatabase) GetConversationsByConversationID( - ctx context.Context, - conversationIDs []string, -) ([]*relationTb.ConversationModel, error) { +func (c *conversationDatabase) GetConversationsByConversationID(ctx context.Context, conversationIDs []string) ([]*relationTb.ConversationModel, error) { return c.conversationDB.GetConversationsByConversationID(ctx, conversationIDs) } + +func (c *conversationDatabase) GetConversationIDsNeedDestruct(ctx context.Context) ([]*relationTb.ConversationModel, error) { + return c.conversationDB.GetConversationIDsNeedDestruct(ctx) +} diff --git a/pkg/common/db/controller/extend_msg.go b/pkg/common/db/controller/extend_msg.go deleted file mode 100644 index 73b6d9330..000000000 --- a/pkg/common/db/controller/extend_msg.go +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "context" - - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache" - unRelationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/tx" -) - -// for mongoDB -type ExtendMsgDatabase interface { - CreateExtendMsgSet(ctx context.Context, set *unRelationTb.ExtendMsgSetModel) error - GetAllExtendMsgSet( - ctx context.Context, - ID string, - opts *unRelationTb.GetAllExtendMsgSetOpts, - ) (sets []*unRelationTb.ExtendMsgSetModel, err error) - GetExtendMsgSet( - ctx context.Context, - conversationID string, - sessionType int32, - maxMsgUpdateTime int64, - ) (*unRelationTb.ExtendMsgSetModel, error) - InsertExtendMsg( - ctx context.Context, - conversationID string, - sessionType int32, - msg *unRelationTb.ExtendMsgModel, - ) error - InsertOrUpdateReactionExtendMsgSet( - ctx context.Context, - conversationID string, - sessionType int32, - clientMsgID string, - msgFirstModifyTime int64, - reactionExtensionList map[string]*unRelationTb.KeyValueModel, - ) error - DeleteReactionExtendMsgSet( - ctx context.Context, - conversationID string, - sessionType int32, - clientMsgID string, - msgFirstModifyTime int64, - reactionExtensionList map[string]*unRelationTb.KeyValueModel, - ) error - GetExtendMsg( - ctx context.Context, - conversationID string, - sessionType int32, - clientMsgID string, - maxMsgUpdateTime int64, - ) (extendMsg *unRelationTb.ExtendMsgModel, err error) -} - -type extendMsgDatabase struct { - database unRelationTb.ExtendMsgSetModelInterface - cache cache.ExtendMsgSetCache - ctxTx tx.CtxTx -} - -func NewExtendMsgDatabase( - extendMsgModel unRelationTb.ExtendMsgSetModelInterface, - cache cache.ExtendMsgSetCache, - ctxTx tx.CtxTx, -) ExtendMsgDatabase { - return &extendMsgDatabase{database: extendMsgModel, cache: cache, ctxTx: ctxTx} -} - -func (e *extendMsgDatabase) CreateExtendMsgSet(ctx context.Context, set *unRelationTb.ExtendMsgSetModel) error { - return e.database.CreateExtendMsgSet(ctx, set) -} - -func (e *extendMsgDatabase) GetAllExtendMsgSet( - ctx context.Context, - conversationID string, - opts *unRelationTb.GetAllExtendMsgSetOpts, -) (sets []*unRelationTb.ExtendMsgSetModel, err error) { - return e.database.GetAllExtendMsgSet(ctx, conversationID, opts) -} - -func (e *extendMsgDatabase) GetExtendMsgSet( - ctx context.Context, - conversationID string, - sessionType int32, - maxMsgUpdateTime int64, -) (*unRelationTb.ExtendMsgSetModel, error) { - return e.database.GetExtendMsgSet(ctx, conversationID, sessionType, maxMsgUpdateTime) -} - -func (e *extendMsgDatabase) InsertExtendMsg( - ctx context.Context, - conversationID string, - sessionType int32, - msg *unRelationTb.ExtendMsgModel, -) error { - return e.database.InsertExtendMsg(ctx, conversationID, sessionType, msg) -} - -func (e *extendMsgDatabase) InsertOrUpdateReactionExtendMsgSet( - ctx context.Context, - conversationID string, - sessionType int32, - clientMsgID string, - msgFirstModifyTime int64, - reactionExtensionList map[string]*unRelationTb.KeyValueModel, -) error { - return e.database.InsertOrUpdateReactionExtendMsgSet( - ctx, - conversationID, - sessionType, - clientMsgID, - msgFirstModifyTime, - reactionExtensionList, - ) -} - -func (e *extendMsgDatabase) DeleteReactionExtendMsgSet( - ctx context.Context, - conversationID string, - sessionType int32, - clientMsgID string, - msgFirstModifyTime int64, - reactionExtensionList map[string]*unRelationTb.KeyValueModel, -) error { - return e.database.DeleteReactionExtendMsgSet( - ctx, - conversationID, - sessionType, - clientMsgID, - msgFirstModifyTime, - reactionExtensionList, - ) -} - -func (e *extendMsgDatabase) GetExtendMsg( - ctx context.Context, - conversationID string, - sessionType int32, - clientMsgID string, - maxMsgUpdateTime int64, -) (extendMsg *unRelationTb.ExtendMsgModel, err error) { - return e.cache.GetExtendMsg(ctx, conversationID, sessionType, clientMsgID, maxMsgUpdateTime) -} diff --git a/pkg/common/db/controller/group.go b/pkg/common/db/controller/group.go index 18b10aca5..9010e3350 100644 --- a/pkg/common/db/controller/group.go +++ b/pkg/common/db/controller/group.go @@ -17,6 +17,7 @@ package controller import ( "context" "fmt" + "time" "github.com/dtm-labs/rockscache" "github.com/redis/go-redis/v9" @@ -121,6 +122,11 @@ type GroupDatabase interface { DeleteSuperGroup(ctx context.Context, groupID string) error DeleteSuperGroupMember(ctx context.Context, groupID string, userIDs []string) error CreateSuperGroupMember(ctx context.Context, groupID string, userIDs []string) error + + // 获取群总数 + CountTotal(ctx context.Context, before *time.Time) (count int64, err error) + // 获取范围内群增量 + CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) } func NewGroupDatabase( @@ -562,3 +568,11 @@ func (g *groupDatabase) CreateSuperGroupMember(ctx context.Context, groupID stri } return g.cache.DelSuperGroupMemberIDs(groupID).DelJoinedSuperGroupIDs(userIDs...).ExecDel(ctx) } + +func (g *groupDatabase) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) { + return g.groupDB.CountTotal(ctx, before) +} + +func (g *groupDatabase) CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) { + return g.groupDB.CountRangeEverydayTotal(ctx, start, end) +} diff --git a/pkg/common/db/controller/msg.go b/pkg/common/db/controller/msg.go index 435e3e70b..f23e7580c 100644 --- a/pkg/common/db/controller/msg.go +++ b/pkg/common/db/controller/msg.go @@ -1,21 +1,6 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package controller import ( - "fmt" "time" "github.com/redis/go-redis/v9" @@ -33,11 +18,10 @@ import ( "context" "errors" - "go.mongodb.org/mongo-driver/mongo" - pbMsg "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" + "go.mongodb.org/mongo-driver/mongo" ) const ( @@ -56,28 +40,17 @@ type CommonMsgDatabase interface { DeleteMessagesFromCache(ctx context.Context, conversationID string, seqs []int64) error DelUserDeleteMsgsList(ctx context.Context, conversationID string, seqs []int64) // incrSeq然后批量插入缓存 - BatchInsertChat2Cache( - ctx context.Context, - conversationID string, - msgs []*sdkws.MsgData, - ) (seq int64, isNewConversation bool, err error) + BatchInsertChat2Cache(ctx context.Context, conversationID string, msgs []*sdkws.MsgData) (seq int64, isNewConversation bool, err error) // 通过seqList获取mongo中写扩散消息 - GetMsgBySeqsRange( - ctx context.Context, - userID string, - conversationID string, - begin, end, num, userMaxSeq int64, - ) (minSeq int64, maxSeq int64, seqMsg []*sdkws.MsgData, err error) + GetMsgBySeqsRange(ctx context.Context, userID string, conversationID string, begin, end, num, userMaxSeq int64) (minSeq int64, maxSeq int64, seqMsg []*sdkws.MsgData, err error) // 通过seqList获取大群在 mongo里面的消息 - GetMsgBySeqs( - ctx context.Context, - userID string, - conversationID string, - seqs []int64, - ) (minSeq int64, maxSeq int64, seqMsg []*sdkws.MsgData, err error) + GetMsgBySeqs(ctx context.Context, userID string, conversationID string, seqs []int64) (minSeq int64, maxSeq int64, seqMsg []*sdkws.MsgData, err error) // 删除会话消息重置最小seq, remainTime为消息保留的时间单位秒,超时消息删除, 传0删除所有消息(此方法不删除redis cache) DeleteConversationMsgsAndSetMinSeq(ctx context.Context, conversationID string, remainTime int64) error + // 用户标记删除过期消息返回标记删除的seq列表 + UserMsgsDestruct(cte context.Context, userID string, conversationID string, destructTime int64, lastMsgDestructTime time.Time) (seqs []int64, err error) + // 用户根据seq删除消息 DeleteUserMsgsBySeqs(ctx context.Context, userID string, conversationID string, seqs []int64) error // 物理删除消息置空 @@ -101,11 +74,8 @@ type CommonMsgDatabase interface { GetHasReadSeq(ctx context.Context, userID string, conversationID string) (int64, error) UserSetHasReadSeqs(ctx context.Context, userID string, hasReadSeqs map[string]int64) error - GetMongoMaxAndMinSeq(ctx context.Context, conversationID string) (maxSeq, minSeq int64, err error) - GetConversationMinMaxSeqInMongoAndCache( - ctx context.Context, - conversationID string, - ) (minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache int64, err error) + GetMongoMaxAndMinSeq(ctx context.Context, conversationID string) (minSeqMongo, maxSeqMongo int64, err error) + GetConversationMinMaxSeqInMongoAndCache(ctx context.Context, conversationID string) (minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache int64, err error) SetSendMsgStatus(ctx context.Context, id string, status int32) error GetSendMsgStatus(ctx context.Context, id string) (int32, error) @@ -115,51 +85,17 @@ type CommonMsgDatabase interface { MsgToPushMQ(ctx context.Context, key, conversarionID string, msg2mq *sdkws.MsgData) (int32, int64, error) MsgToMongoMQ(ctx context.Context, key, conversarionID string, msgs []*sdkws.MsgData, lastSeq int64) error - // modify - JudgeMessageReactionExist(ctx context.Context, clientMsgID string, sessionType int32) (bool, error) - SetMessageTypeKeyValue(ctx context.Context, clientMsgID string, sessionType int32, typeKey, value string) error - SetMessageReactionExpire( - ctx context.Context, - clientMsgID string, - sessionType int32, - expiration time.Duration, - ) (bool, error) - GetExtendMsg( - ctx context.Context, - conversationID string, - sessionType int32, - clientMsgID string, - maxMsgUpdateTime int64, - ) (*pbMsg.ExtendMsg, error) - InsertOrUpdateReactionExtendMsgSet( - ctx context.Context, - conversationID string, - sessionType int32, - clientMsgID string, - msgFirstModifyTime int64, - reactionExtensionList map[string]*sdkws.KeyValue, - ) error - GetMessageTypeKeyValue(ctx context.Context, clientMsgID string, sessionType int32, typeKey string) (string, error) - GetOneMessageAllReactionList(ctx context.Context, clientMsgID string, sessionType int32) (map[string]string, error) - DeleteOneMessageKey(ctx context.Context, clientMsgID string, sessionType int32, subKey string) error - DeleteReactionExtendMsgSet( - ctx context.Context, - conversationID string, - sessionType int32, - clientMsgID string, - msgFirstModifyTime int64, - reactionExtensionList map[string]*sdkws.KeyValue, - ) error + RangeUserSendCount(ctx context.Context, start time.Time, end time.Time, group bool, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, users []*unRelationTb.UserCount, dateCount map[string]int64, err error) + RangeGroupSendCount(ctx context.Context, start time.Time, end time.Time, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, groups []*unRelationTb.GroupCount, dateCount map[string]int64, err error) } func NewCommonMsgDatabase(msgDocModel unRelationTb.MsgDocModelInterface, cacheModel cache.MsgModel) CommonMsgDatabase { return &commonMsgDatabase{ - msgDocDatabase: msgDocModel, - cache: cacheModel, - producer: kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.LatestMsgToRedis.Topic), - producerToMongo: kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.MsgToMongo.Topic), - producerToPush: kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.MsgToPush.Topic), - producerToModify: kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.MsgToModify.Topic), + msgDocDatabase: msgDocModel, + cache: cacheModel, + producer: kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.LatestMsgToRedis.Topic), + producerToMongo: kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.MsgToMongo.Topic), + producerToPush: kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.MsgToPush.Topic), } } @@ -171,15 +107,13 @@ func InitCommonMsgDatabase(rdb redis.UniversalClient, database *mongo.Database) } type commonMsgDatabase struct { - msgDocDatabase unRelationTb.MsgDocModelInterface - extendMsgDatabase unRelationTb.ExtendMsgSetModelInterface - extendMsgSetModel unRelationTb.ExtendMsgSetModel - msg unRelationTb.MsgDocModel - cache cache.MsgModel - producer *kafka.Producer - producerToMongo *kafka.Producer - producerToModify *kafka.Producer - producerToPush *kafka.Producer + msgDocDatabase unRelationTb.MsgDocModelInterface + msg unRelationTb.MsgDocModel + cache cache.MsgModel + producer *kafka.Producer + producerToMongo *kafka.Producer + producerToModify *kafka.Producer + producerToPush *kafka.Producer } func (db *commonMsgDatabase) MsgToMQ(ctx context.Context, key string, msg2mq *sdkws.MsgData) error { @@ -187,32 +121,16 @@ func (db *commonMsgDatabase) MsgToMQ(ctx context.Context, key string, msg2mq *sd return err } -func (db *commonMsgDatabase) MsgToModifyMQ( - ctx context.Context, - key, conversationID string, - messages []*sdkws.MsgData, -) error { +func (db *commonMsgDatabase) MsgToModifyMQ(ctx context.Context, key, conversationID string, messages []*sdkws.MsgData) error { if len(messages) > 0 { - _, _, err := db.producerToModify.SendMessage( - ctx, - key, - &pbMsg.MsgDataToModifyByMQ{ConversationID: conversationID, Messages: messages}, - ) + _, _, err := db.producerToModify.SendMessage(ctx, key, &pbMsg.MsgDataToModifyByMQ{ConversationID: conversationID, Messages: messages}) return err } return nil } -func (db *commonMsgDatabase) MsgToPushMQ( - ctx context.Context, - key, conversationID string, - msg2mq *sdkws.MsgData, -) (int32, int64, error) { - partition, offset, err := db.producerToPush.SendMessage( - ctx, - key, - &pbMsg.PushMsgDataToMQ{MsgData: msg2mq, ConversationID: conversationID}, - ) +func (db *commonMsgDatabase) MsgToPushMQ(ctx context.Context, key, conversationID string, msg2mq *sdkws.MsgData) (int32, int64, error) { + partition, offset, err := db.producerToPush.SendMessage(ctx, key, &pbMsg.PushMsgDataToMQ{MsgData: msg2mq, ConversationID: conversationID}) if err != nil { log.ZError(ctx, "MsgToPushMQ", err, "key", key, "msg2mq", msg2mq) return 0, 0, err @@ -220,30 +138,15 @@ func (db *commonMsgDatabase) MsgToPushMQ( return partition, offset, nil } -func (db *commonMsgDatabase) MsgToMongoMQ( - ctx context.Context, - key, conversationID string, - messages []*sdkws.MsgData, - lastSeq int64, -) error { +func (db *commonMsgDatabase) MsgToMongoMQ(ctx context.Context, key, conversationID string, messages []*sdkws.MsgData, lastSeq int64) error { if len(messages) > 0 { - _, _, err := db.producerToMongo.SendMessage( - ctx, - key, - &pbMsg.MsgDataToMongoByMQ{LastSeq: lastSeq, ConversationID: conversationID, MsgData: messages}, - ) + _, _, err := db.producerToMongo.SendMessage(ctx, key, &pbMsg.MsgDataToMongoByMQ{LastSeq: lastSeq, ConversationID: conversationID, MsgData: messages}) return err } return nil } -func (db *commonMsgDatabase) BatchInsertBlock( - ctx context.Context, - conversationID string, - fields []any, - key int8, - firstSeq int64, -) error { +func (db *commonMsgDatabase) BatchInsertBlock(ctx context.Context, conversationID string, fields []any, key int8, firstSeq int64) error { if len(fields) == 0 { return nil } @@ -344,12 +247,7 @@ func (db *commonMsgDatabase) BatchInsertBlock( return nil } -func (db *commonMsgDatabase) BatchInsertChat2DB( - ctx context.Context, - conversationID string, - msgList []*sdkws.MsgData, - currentMaxSeq int64, -) error { +func (db *commonMsgDatabase) BatchInsertChat2DB(ctx context.Context, conversationID string, msgList []*sdkws.MsgData, currentMaxSeq int64) error { if len(msgList) == 0 { return errs.ErrArgs.Wrap("msgList is empty") } @@ -395,21 +293,11 @@ func (db *commonMsgDatabase) BatchInsertChat2DB( return db.BatchInsertBlock(ctx, conversationID, msgs, updateKeyMsg, msgList[0].Seq) } -func (db *commonMsgDatabase) RevokeMsg( - ctx context.Context, - conversationID string, - seq int64, - revoke *unRelationTb.RevokeModel, -) error { +func (db *commonMsgDatabase) RevokeMsg(ctx context.Context, conversationID string, seq int64, revoke *unRelationTb.RevokeModel) error { return db.BatchInsertBlock(ctx, conversationID, []any{revoke}, updateKeyRevoke, seq) } -func (db *commonMsgDatabase) MarkSingleChatMsgsAsRead( - ctx context.Context, - userID string, - conversationID string, - totalSeqs []int64, -) error { +func (db *commonMsgDatabase) MarkSingleChatMsgsAsRead(ctx context.Context, userID string, conversationID string, totalSeqs []int64) error { for docID, seqs := range db.msg.GetDocIDSeqsMap(conversationID, totalSeqs) { var indexes []int64 for _, seq := range seqs { @@ -432,11 +320,7 @@ func (db *commonMsgDatabase) DelUserDeleteMsgsList(ctx context.Context, conversa db.cache.DelUserDeleteMsgsList(ctx, conversationID, seqs) } -func (db *commonMsgDatabase) BatchInsertChat2Cache( - ctx context.Context, - conversationID string, - msgs []*sdkws.MsgData, -) (seq int64, isNew bool, err error) { +func (db *commonMsgDatabase) BatchInsertChat2Cache(ctx context.Context, conversationID string, msgs []*sdkws.MsgData) (seq int64, isNew bool, err error) { currentMaxSeq, err := db.cache.GetMaxSeq(ctx, conversationID) if err != nil && errs.Unwrap(err) != redis.Nil { prome.Inc(prome.SeqGetFailedCounter) @@ -483,11 +367,7 @@ func (db *commonMsgDatabase) BatchInsertChat2Cache( return lastMaxSeq, isNew, utils.Wrap(err, "") } -func (db *commonMsgDatabase) getMsgBySeqs( - ctx context.Context, - userID, conversationID string, - seqs []int64, -) (totalMsgs []*sdkws.MsgData, err error) { +func (db *commonMsgDatabase) getMsgBySeqs(ctx context.Context, userID, conversationID string, seqs []int64) (totalMsgs []*sdkws.MsgData, err error) { for docID, seqs := range db.msg.GetDocIDSeqsMap(conversationID, seqs) { //log.ZDebug(ctx, "getMsgBySeqs", "docID", docID, "seqs", seqs) msgs, err := db.findMsgInfoBySeq(ctx, userID, docID, seqs) @@ -501,50 +381,7 @@ func (db *commonMsgDatabase) getMsgBySeqs( return totalMsgs, nil } -// func (db *commonMsgDatabase) refetchDelSeqsMsgs(ctx context.Context, conversationID string, delNums, rangeBegin, -// begin int64) (seqMsgs []*unRelationTb.MsgDataModel, err error) { -// var reFetchSeqs []int64 -// if delNums > 0 { -// newBeginSeq := rangeBegin - delNums -// if newBeginSeq >= begin { -// newEndSeq := rangeBegin - 1 -// for i := newBeginSeq; i <= newEndSeq; i++ { -// reFetchSeqs = append(reFetchSeqs, i) -// } -// } -// } -// if len(reFetchSeqs) == 0 { -// return -// } -// if len(reFetchSeqs) > 0 { -// m := db.msg.GetDocIDSeqsMap(conversationID, reFetchSeqs) -// for docID, seqs := range m { -// msgs, _, err := db.findMsgInfoBySeq(ctx, docID, seqs) -// if err != nil { -// return nil, err -// } -// for _, msg := range msgs { -// if msg.Status != constant.MsgDeleted { -// seqMsgs = append(seqMsgs, msg) -// } -// } -// } -// } -// if len(seqMsgs) < int(delNums) { -// seqMsgs2, err := db.refetchDelSeqsMsgs(ctx, conversationID, delNums-int64(len(seqMsgs)), rangeBegin-1, begin) -// if err != nil { -// return seqMsgs, err -// } -// seqMsgs = append(seqMsgs, seqMsgs2...) -// } -// return seqMsgs, nil -// } - -func (db *commonMsgDatabase) findMsgInfoBySeq( - ctx context.Context, - userID, docID string, - seqs []int64, -) (totalMsgs []*unRelationTb.MsgInfoModel, err error) { +func (db *commonMsgDatabase) findMsgInfoBySeq(ctx context.Context, userID, docID string, seqs []int64) (totalMsgs []*unRelationTb.MsgInfoModel, err error) { msgs, err := db.msgDocDatabase.GetMsgBySeqIndexIn1Doc(ctx, userID, docID, seqs) for _, msg := range msgs { if msg.IsRead { @@ -554,25 +391,8 @@ func (db *commonMsgDatabase) findMsgInfoBySeq( return msgs, err } -func (db *commonMsgDatabase) getMsgBySeqsRange( - ctx context.Context, - userID string, - conversationID string, - allSeqs []int64, - begin, end int64, -) (seqMsgs []*sdkws.MsgData, err error) { - log.ZDebug( - ctx, - "getMsgBySeqsRange", - "conversationID", - conversationID, - "allSeqs", - allSeqs, - "begin", - begin, - "end", - end, - ) +func (db *commonMsgDatabase) getMsgBySeqsRange(ctx context.Context, userID string, conversationID string, allSeqs []int64, begin, end int64) (seqMsgs []*sdkws.MsgData, err error) { + log.ZDebug(ctx, "getMsgBySeqsRange", "conversationID", conversationID, "allSeqs", allSeqs, "begin", begin, "end", end) for docID, seqs := range db.msg.GetDocIDSeqsMap(conversationID, allSeqs) { log.ZDebug(ctx, "getMsgBySeqsRange", "docID", docID, "seqs", seqs) msgs, err := db.findMsgInfoBySeq(ctx, userID, docID, seqs) @@ -589,12 +409,7 @@ func (db *commonMsgDatabase) getMsgBySeqsRange( return seqMsgs, nil } -func (db *commonMsgDatabase) GetMsgBySeqsRange( - ctx context.Context, - userID string, - conversationID string, - begin, end, num, userMaxSeq int64, -) (int64, int64, []*sdkws.MsgData, error) { +func (db *commonMsgDatabase) GetMsgBySeqsRange(ctx context.Context, userID string, conversationID string, begin, end, num, userMaxSeq int64) (int64, int64, []*sdkws.MsgData, error) { userMinSeq, err := db.cache.GetConversationUserMinSeq(ctx, conversationID, userID) if err != nil && errs.Unwrap(err) != redis.Nil { return 0, 0, nil, err @@ -614,18 +429,7 @@ func (db *commonMsgDatabase) GetMsgBySeqsRange( if err != nil && errs.Unwrap(err) != redis.Nil { return 0, 0, nil, err } - log.ZDebug( - ctx, - "GetMsgBySeqsRange", - "userMinSeq", - userMinSeq, - "conMinSeq", - minSeq, - "conMaxSeq", - maxSeq, - "userMaxSeq", - userMaxSeq, - ) + log.ZDebug(ctx, "GetMsgBySeqsRange", "userMinSeq", userMinSeq, "conMinSeq", minSeq, "conMaxSeq", maxSeq, "userMaxSeq", userMaxSeq) if userMaxSeq != 0 { if userMaxSeq < maxSeq { maxSeq = userMaxSeq @@ -675,18 +479,7 @@ func (db *commonMsgDatabase) GetMsgBySeqsRange( cacheDelNum += 1 } } - log.ZDebug( - ctx, - "get delSeqs from redis", - "delSeqs", - delSeqs, - "userID", - userID, - "conversationID", - conversationID, - "cacheDelNum", - cacheDelNum, - ) + log.ZDebug(ctx, "get delSeqs from redis", "delSeqs", delSeqs, "userID", userID, "conversationID", conversationID, "cacheDelNum", cacheDelNum) var reGetSeqsCache []int64 for i := 1; i <= cacheDelNum; { newSeq := newBegin - int64(i) @@ -706,15 +499,7 @@ func (db *commonMsgDatabase) GetMsgBySeqsRange( if err != nil { if err != redis.Nil { prome.Add(prome.MsgPullFromRedisFailedCounter, len(failedSeqs2)) - log.ZError( - ctx, - "get message from redis exception", - err, - "conversationID", - conversationID, - "seqs", - reGetSeqsCache, - ) + log.ZError(ctx, "get message from redis exception", err, "conversationID", conversationID, "seqs", reGetSeqsCache) } } failedSeqs = append(failedSeqs, failedSeqs2...) @@ -740,12 +525,7 @@ func (db *commonMsgDatabase) GetMsgBySeqsRange( return minSeq, maxSeq, successMsgs, nil } -func (db *commonMsgDatabase) GetMsgBySeqs( - ctx context.Context, - userID string, - conversationID string, - seqs []int64, -) (int64, int64, []*sdkws.MsgData, error) { +func (db *commonMsgDatabase) GetMsgBySeqs(ctx context.Context, userID string, conversationID string, seqs []int64) (int64, int64, []*sdkws.MsgData, error) { userMinSeq, err := db.cache.GetConversationUserMinSeq(ctx, conversationID, userID) if err != nil && errs.Unwrap(err) != redis.Nil { return 0, 0, nil, err @@ -771,33 +551,10 @@ func (db *commonMsgDatabase) GetMsgBySeqs( if err != nil { if err != redis.Nil { prome.Add(prome.MsgPullFromRedisFailedCounter, len(failedSeqs)) - log.ZError( - ctx, - "get message from redis exception", - err, - "failedSeqs", - failedSeqs, - "conversationID", - conversationID, - ) + log.ZError(ctx, "get message from redis exception", err, "failedSeqs", failedSeqs, "conversationID", conversationID) } } - log.ZInfo( - ctx, - "db.cache.GetMessagesBySeq", - "userID", - userID, - "conversationID", - conversationID, - "seqs", - seqs, - "successMsgs", - len(successMsgs), - "failedSeqs", - failedSeqs, - "conversationID", - conversationID, - ) + log.ZInfo(ctx, "db.cache.GetMessagesBySeq", "userID", userID, "conversationID", conversationID, "seqs", seqs, "successMsgs", len(successMsgs), "failedSeqs", failedSeqs, "conversationID", conversationID) prome.Add(prome.MsgPullFromRedisSuccessCounter, len(successMsgs)) if len(failedSeqs) > 0 { mongoMsgs, err := db.getMsgBySeqs(ctx, userID, conversationID, failedSeqs) @@ -811,11 +568,7 @@ func (db *commonMsgDatabase) GetMsgBySeqs( return minSeq, maxSeq, successMsgs, nil } -func (db *commonMsgDatabase) DeleteConversationMsgsAndSetMinSeq( - ctx context.Context, - conversationID string, - remainTime int64, -) error { +func (db *commonMsgDatabase) DeleteConversationMsgsAndSetMinSeq(ctx context.Context, conversationID string, remainTime int64) error { var delStruct delMsgRecursionStruct var skip int64 minSeq, err := db.deleteMsgRecursion(ctx, conversationID, skip, &delStruct, remainTime) @@ -835,6 +588,49 @@ func (db *commonMsgDatabase) DeleteConversationMsgsAndSetMinSeq( return db.cache.SetMinSeq(ctx, conversationID, minSeq) } +func (db *commonMsgDatabase) UserMsgsDestruct(ctx context.Context, userID string, conversationID string, destructTime int64, lastMsgDestructTime time.Time) (seqs []int64, err error) { + var index int64 + for { + // from oldest 2 newest + msgDocModel, err := db.msgDocDatabase.GetMsgDocModelByIndex(ctx, conversationID, index, 1) + if err != nil || msgDocModel.DocID == "" { + if err != nil { + if err == unrelation.ErrMsgListNotExist { + log.ZDebug(ctx, "deleteMsgRecursion finished", "conversationID", conversationID, "userID", userID, "index", index) + } else { + log.ZError(ctx, "deleteMsgRecursion GetUserMsgListByIndex failed", err, "conversationID", conversationID, "index", index) + } + } + // 获取报错,或者获取不到了,物理删除并且返回seq delMongoMsgsPhysical(delStruct.delDocIDList), 结束递归 + break + } + index++ + //&& msgDocModel.Msg[0].Msg.SendTime > lastMsgDestructTime.UnixMilli() + if len(msgDocModel.Msg) > 0 { + for _, msg := range msgDocModel.Msg { + if msg != nil && msg.Msg != nil && msg.Msg.SendTime+destructTime*1000 <= time.Now().UnixMilli() { + if msg.Msg.SendTime > lastMsgDestructTime.UnixMilli() && !utils.Contain(userID, msg.DelList...) { + seqs = append(seqs, msg.Msg.Seq) + } + } else { + log.ZDebug(ctx, "deleteMsgRecursion finished", "conversationID", conversationID, "userID", userID, "index", index) + break + } + + } + } + } + + log.ZDebug(ctx, "UserMsgsDestruct", "conversationID", conversationID, "userID", userID, "seqs", seqs) + if len(seqs) > 0 { + latestSeq := seqs[len(seqs)-1] + if err := db.cache.SetConversationUserMinSeq(ctx, conversationID, userID, latestSeq); err != nil { + return nil, err + } + } + return seqs, nil +} + // this is struct for recursion type delMsgRecursionStruct struct { minSeq int64 @@ -849,26 +645,13 @@ func (d *delMsgRecursionStruct) getSetMinSeq() int64 { // seq 70 // set minSeq 21 // recursion 删除list并且返回设置的最小seq -func (db *commonMsgDatabase) deleteMsgRecursion( - ctx context.Context, - conversationID string, - index int64, - delStruct *delMsgRecursionStruct, - remainTime int64, -) (int64, error) { +func (db *commonMsgDatabase) deleteMsgRecursion(ctx context.Context, conversationID string, index int64, delStruct *delMsgRecursionStruct, remainTime int64) (int64, error) { // find from oldest list msgDocModel, err := db.msgDocDatabase.GetMsgDocModelByIndex(ctx, conversationID, index, 1) if err != nil || msgDocModel.DocID == "" { if err != nil { if err == unrelation.ErrMsgListNotExist { - log.ZDebug( - ctx, - "deleteMsgRecursion ErrMsgListNotExist", - "conversationID", - conversationID, - "index:", - index, - ) + log.ZDebug(ctx, "deleteMsgRecursion ErrMsgListNotExist", "conversationID", conversationID, "index:", index) } else { log.ZError(ctx, "deleteMsgRecursion GetUserMsgListByIndex failed", err, "conversationID", conversationID, "index", index) } @@ -880,23 +663,11 @@ func (db *commonMsgDatabase) deleteMsgRecursion( } return delStruct.getSetMinSeq() + 1, nil } - log.ZDebug( - ctx, - "doc info", - "conversationID", - conversationID, - "index", - index, - "docID", - msgDocModel.DocID, - "len", - len(msgDocModel.Msg), - ) + log.ZDebug(ctx, "doc info", "conversationID", conversationID, "index", index, "docID", msgDocModel.DocID, "len", len(msgDocModel.Msg)) if int64(len(msgDocModel.Msg)) > db.msg.GetSingleGocMsgNum() { log.ZWarn(ctx, "msgs too large", nil, "lenth", len(msgDocModel.Msg), "docID:", msgDocModel.DocID) } - if msgDocModel.IsFull() && - msgDocModel.Msg[len(msgDocModel.Msg)-1].Msg.SendTime+(remainTime*1000) < utils.GetCurrentTimestampByMill() { + if msgDocModel.IsFull() && msgDocModel.Msg[len(msgDocModel.Msg)-1].Msg.SendTime+(remainTime*1000) < utils.GetCurrentTimestampByMill() { log.ZDebug(ctx, "doc is full and all msg is expired", "docID", msgDocModel.DocID) delStruct.delDocIDs = append(delStruct.delDocIDs, msgDocModel.DocID) delStruct.minSeq = msgDocModel.Msg[len(msgDocModel.Msg)-1].Msg.Seq @@ -933,11 +704,7 @@ func (db *commonMsgDatabase) deleteMsgRecursion( return seq, err } -func (db *commonMsgDatabase) DeleteMsgsPhysicalBySeqs( - ctx context.Context, - conversationID string, - allSeqs []int64, -) error { +func (db *commonMsgDatabase) DeleteMsgsPhysicalBySeqs(ctx context.Context, conversationID string, allSeqs []int64) error { if err := db.cache.DeleteMessages(ctx, conversationID, allSeqs); err != nil { return err } @@ -953,12 +720,7 @@ func (db *commonMsgDatabase) DeleteMsgsPhysicalBySeqs( return nil } -func (db *commonMsgDatabase) DeleteUserMsgsBySeqs( - ctx context.Context, - userID string, - conversationID string, - seqs []int64, -) error { +func (db *commonMsgDatabase) DeleteUserMsgsBySeqs(ctx context.Context, userID string, conversationID string, seqs []int64) error { cachedMsgs, _, err := db.cache.GetMessagesBySeq(ctx, conversationID, seqs) if err != nil && errs.Unwrap(err) != redis.Nil { log.ZWarn(ctx, "DeleteUserMsgsBySeqs", err, "conversationID", conversationID, "seqs", seqs) @@ -1027,70 +789,31 @@ func (db *commonMsgDatabase) GetMinSeqs(ctx context.Context, conversationIDs []s func (db *commonMsgDatabase) GetMinSeq(ctx context.Context, conversationID string) (int64, error) { return db.cache.GetMinSeq(ctx, conversationID) } - -func (db *commonMsgDatabase) GetConversationUserMinSeq( - ctx context.Context, - conversationID string, - userID string, -) (int64, error) { +func (db *commonMsgDatabase) GetConversationUserMinSeq(ctx context.Context, conversationID string, userID string) (int64, error) { return db.cache.GetConversationUserMinSeq(ctx, conversationID, userID) } - -func (db *commonMsgDatabase) GetConversationUserMinSeqs( - ctx context.Context, - conversationID string, - userIDs []string, -) (map[string]int64, error) { +func (db *commonMsgDatabase) GetConversationUserMinSeqs(ctx context.Context, conversationID string, userIDs []string) (map[string]int64, error) { return db.cache.GetConversationUserMinSeqs(ctx, conversationID, userIDs) } - -func (db *commonMsgDatabase) SetConversationUserMinSeq( - ctx context.Context, - conversationID string, - userID string, - minSeq int64, -) error { +func (db *commonMsgDatabase) SetConversationUserMinSeq(ctx context.Context, conversationID string, userID string, minSeq int64) error { return db.cache.SetConversationUserMinSeq(ctx, conversationID, userID, minSeq) } - -func (db *commonMsgDatabase) SetConversationUserMinSeqs( - ctx context.Context, - conversationID string, - seqs map[string]int64, -) (err error) { +func (db *commonMsgDatabase) SetConversationUserMinSeqs(ctx context.Context, conversationID string, seqs map[string]int64) (err error) { return db.cache.SetConversationUserMinSeqs(ctx, conversationID, seqs) } -func (db *commonMsgDatabase) SetUserConversationsMinSeqs( - ctx context.Context, - userID string, - seqs map[string]int64, -) error { +func (db *commonMsgDatabase) SetUserConversationsMinSeqs(ctx context.Context, userID string, seqs map[string]int64) error { return db.cache.SetUserConversationsMinSeqs(ctx, userID, seqs) } -func (db *commonMsgDatabase) UserSetHasReadSeqs( - ctx context.Context, - userID string, - hasReadSeqs map[string]int64, -) error { +func (db *commonMsgDatabase) UserSetHasReadSeqs(ctx context.Context, userID string, hasReadSeqs map[string]int64) error { return db.cache.UserSetHasReadSeqs(ctx, userID, hasReadSeqs) } -func (db *commonMsgDatabase) SetHasReadSeq( - ctx context.Context, - userID string, - conversationID string, - hasReadSeq int64, -) error { +func (db *commonMsgDatabase) SetHasReadSeq(ctx context.Context, userID string, conversationID string, hasReadSeq int64) error { return db.cache.SetHasReadSeq(ctx, userID, conversationID, hasReadSeq) } - -func (db *commonMsgDatabase) GetHasReadSeqs( - ctx context.Context, - userID string, - conversationIDs []string, -) (map[string]int64, error) { +func (db *commonMsgDatabase) GetHasReadSeqs(ctx context.Context, userID string, conversationIDs []string) (map[string]int64, error) { return db.cache.GetHasReadSeqs(ctx, userID, conversationIDs) } @@ -1106,10 +829,7 @@ func (db *commonMsgDatabase) GetSendMsgStatus(ctx context.Context, id string) (i return db.cache.GetSendMsgStatus(ctx, id) } -func (db *commonMsgDatabase) GetConversationMinMaxSeqInMongoAndCache( - ctx context.Context, - conversationID string, -) (minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache int64, err error) { +func (db *commonMsgDatabase) GetConversationMinMaxSeqInMongoAndCache(ctx context.Context, conversationID string) (minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache int64, err error) { minSeqMongo, maxSeqMongo, err = db.GetMinMaxSeqMongo(ctx, conversationID) if err != nil { return @@ -1125,17 +845,11 @@ func (db *commonMsgDatabase) GetConversationMinMaxSeqInMongoAndCache( return } -func (db *commonMsgDatabase) GetMongoMaxAndMinSeq( - ctx context.Context, - conversationID string, -) (maxSeq, minSeq int64, err error) { +func (db *commonMsgDatabase) GetMongoMaxAndMinSeq(ctx context.Context, conversationID string) (minSeqMongo, maxSeqMongo int64, err error) { return db.GetMinMaxSeqMongo(ctx, conversationID) } -func (db *commonMsgDatabase) GetMinMaxSeqMongo( - ctx context.Context, - conversationID string, -) (minSeqMongo, maxSeqMongo int64, err error) { +func (db *commonMsgDatabase) GetMinMaxSeqMongo(ctx context.Context, conversationID string) (minSeqMongo, maxSeqMongo int64, err error) { oldestMsgMongo, err := db.msgDocDatabase.GetOldestMsg(ctx, conversationID) if err != nil { return @@ -1149,124 +863,10 @@ func (db *commonMsgDatabase) GetMinMaxSeqMongo( return } -func (db *commonMsgDatabase) JudgeMessageReactionExist( - ctx context.Context, - clientMsgID string, - sessionType int32, -) (bool, error) { - return db.cache.JudgeMessageReactionExist(ctx, clientMsgID, sessionType) -} - -func (db *commonMsgDatabase) SetMessageTypeKeyValue( - ctx context.Context, - clientMsgID string, - sessionType int32, - typeKey, value string, -) error { - return db.cache.SetMessageTypeKeyValue(ctx, clientMsgID, sessionType, typeKey, value) -} - -func (db *commonMsgDatabase) SetMessageReactionExpire( - ctx context.Context, - clientMsgID string, - sessionType int32, - expiration time.Duration, -) (bool, error) { - return db.cache.SetMessageReactionExpire(ctx, clientMsgID, sessionType, expiration) -} - -func (db *commonMsgDatabase) GetMessageTypeKeyValue( - ctx context.Context, - clientMsgID string, - sessionType int32, - typeKey string, -) (string, error) { - return db.cache.GetMessageTypeKeyValue(ctx, clientMsgID, sessionType, typeKey) -} - -func (db *commonMsgDatabase) GetOneMessageAllReactionList( - ctx context.Context, - clientMsgID string, - sessionType int32, -) (map[string]string, error) { - return db.cache.GetOneMessageAllReactionList(ctx, clientMsgID, sessionType) -} - -func (db *commonMsgDatabase) DeleteOneMessageKey( - ctx context.Context, - clientMsgID string, - sessionType int32, - subKey string, -) error { - return db.cache.DeleteOneMessageKey(ctx, clientMsgID, sessionType, subKey) -} - -func (db *commonMsgDatabase) InsertOrUpdateReactionExtendMsgSet( - ctx context.Context, - conversationID string, - sessionType int32, - clientMsgID string, - msgFirstModifyTime int64, - reactionExtensions map[string]*sdkws.KeyValue, -) error { - return db.extendMsgDatabase.InsertOrUpdateReactionExtendMsgSet( - ctx, - conversationID, - sessionType, - clientMsgID, - msgFirstModifyTime, - db.extendMsgSetModel.Pb2Model(reactionExtensions), - ) -} - -func (db *commonMsgDatabase) GetExtendMsg( - ctx context.Context, - conversationID string, - sessionType int32, - clientMsgID string, - maxMsgUpdateTime int64, -) (*pbMsg.ExtendMsg, error) { - extendMsgSet, err := db.extendMsgDatabase.GetExtendMsgSet(ctx, conversationID, sessionType, maxMsgUpdateTime) - if err != nil { - return nil, err - } - extendMsg, ok := extendMsgSet.ExtendMsgs[clientMsgID] - if !ok { - return nil, errs.ErrRecordNotFound.Wrap(fmt.Sprintf("cant find client msg id: %s", clientMsgID)) - } - reactionExtensionList := make(map[string]*pbMsg.KeyValueResp) - for key, model := range extendMsg.ReactionExtensionList { - reactionExtensionList[key] = &pbMsg.KeyValueResp{ - KeyValue: &sdkws.KeyValue{ - TypeKey: model.TypeKey, - Value: model.Value, - LatestUpdateTime: model.LatestUpdateTime, - }, - } - } - return &pbMsg.ExtendMsg{ - ReactionExtensions: reactionExtensionList, - ClientMsgID: extendMsg.ClientMsgID, - MsgFirstModifyTime: extendMsg.MsgFirstModifyTime, - AttachedInfo: extendMsg.AttachedInfo, - Ex: extendMsg.Ex, - }, nil +func (db *commonMsgDatabase) RangeUserSendCount(ctx context.Context, start time.Time, end time.Time, group bool, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, users []*unRelationTb.UserCount, dateCount map[string]int64, err error) { + return db.msgDocDatabase.RangeUserSendCount(ctx, start, end, group, ase, pageNumber, showNumber) } -func (db *commonMsgDatabase) DeleteReactionExtendMsgSet( - ctx context.Context, - conversationID string, - sessionType int32, - clientMsgID string, - msgFirstModifyTime int64, - reactionExtensions map[string]*sdkws.KeyValue, -) error { - return db.extendMsgDatabase.DeleteReactionExtendMsgSet( - ctx, - conversationID, - sessionType, - clientMsgID, - msgFirstModifyTime, - db.extendMsgSetModel.Pb2Model(reactionExtensions), - ) +func (db *commonMsgDatabase) RangeGroupSendCount(ctx context.Context, start time.Time, end time.Time, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, groups []*unRelationTb.GroupCount, dateCount map[string]int64, err error) { + return db.msgDocDatabase.RangeGroupSendCount(ctx, start, end, ase, pageNumber, showNumber) } diff --git a/pkg/common/db/controller/s3.go b/pkg/common/db/controller/s3.go new file mode 100644 index 000000000..f192dadc5 --- /dev/null +++ b/pkg/common/db/controller/s3.go @@ -0,0 +1,76 @@ +package controller + +import "C" +import ( + "context" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3/cont" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" + "path/filepath" + "time" +) + +type S3Database interface { + PartLimit() *s3.PartLimit + PartSize(ctx context.Context, size int64) (int64, error) + AuthSign(ctx context.Context, uploadID string, partNumbers []int) (*s3.AuthSignResult, error) + InitiateMultipartUpload(ctx context.Context, hash string, size int64, expire time.Duration, maxParts int) (*cont.InitiateUploadResult, error) + CompleteMultipartUpload(ctx context.Context, uploadID string, parts []string) (*cont.UploadResult, error) + AccessURL(ctx context.Context, name string, expire time.Duration) (time.Time, string, error) + SetObject(ctx context.Context, info *relation.ObjectModel) error +} + +func NewS3Database(s3 s3.Interface, obj relation.ObjectInfoModelInterface) S3Database { + return &s3Database{ + s3: cont.New(s3), + obj: obj, + } +} + +type s3Database struct { + s3 *cont.Controller + obj relation.ObjectInfoModelInterface +} + +func (s *s3Database) PartSize(ctx context.Context, size int64) (int64, error) { + return s.s3.PartSize(ctx, size) +} + +func (s *s3Database) PartLimit() *s3.PartLimit { + return s.s3.PartLimit() +} + +func (s *s3Database) AuthSign(ctx context.Context, uploadID string, partNumbers []int) (*s3.AuthSignResult, error) { + return s.s3.AuthSign(ctx, uploadID, partNumbers) +} + +func (s *s3Database) InitiateMultipartUpload(ctx context.Context, hash string, size int64, expire time.Duration, maxParts int) (*cont.InitiateUploadResult, error) { + return s.s3.InitiateUpload(ctx, hash, size, expire, maxParts) +} + +func (s *s3Database) CompleteMultipartUpload(ctx context.Context, uploadID string, parts []string) (*cont.UploadResult, error) { + return s.s3.CompleteUpload(ctx, uploadID, parts) +} + +func (s *s3Database) SetObject(ctx context.Context, info *relation.ObjectModel) error { + return s.obj.SetObject(ctx, info) +} + +func (s *s3Database) AccessURL(ctx context.Context, name string, expire time.Duration) (time.Time, string, error) { + obj, err := s.obj.Take(ctx, name) + if err != nil { + return time.Time{}, "", err + } + opt := &s3.AccessURLOption{ + ContentType: obj.ContentType, + } + if filename := filepath.Base(obj.Name); filename != "" { + opt.ContentDisposition = `attachment; filename=` + filename + } + expireTime := time.Now().Add(expire) + rawURL, err := s.s3.AccessURL(ctx, obj.Key, expire, opt) + if err != nil { + return time.Time{}, "", err + } + return expireTime, rawURL, nil +} diff --git a/pkg/common/db/controller/storage.go b/pkg/common/db/controller/storage.go deleted file mode 100644 index c383eb8ec..000000000 --- a/pkg/common/db/controller/storage.go +++ /dev/null @@ -1,567 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "bytes" - "context" - "crypto/md5" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "io" - "net/url" - "path" - "strconv" - "time" - - "github.com/google/uuid" - - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/obj" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" - "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" - "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/third" - "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" -) - -const ( - hashPrefix = "hash" - tempPrefix = "temp" - fragmentPrefix = "fragment_" - urlsName = "urls.json" -) - -type S3Database interface { - ApplyPut(ctx context.Context, req *third.ApplyPutReq) (*third.ApplyPutResp, error) - GetPut(ctx context.Context, req *third.GetPutReq) (*third.GetPutResp, error) - ConfirmPut(ctx context.Context, req *third.ConfirmPutReq) (*third.ConfirmPutResp, error) - GetUrl(ctx context.Context, req *third.GetUrlReq) (*third.GetUrlResp, error) - GetHashInfo(ctx context.Context, req *third.GetHashInfoReq) (*third.GetHashInfoResp, error) - CleanExpirationObject(ctx context.Context, t time.Time) -} - -func NewS3Database( - obj obj.Interface, - hash relation.ObjectHashModelInterface, - info relation.ObjectInfoModelInterface, - put relation.ObjectPutModelInterface, - url *url.URL, -) S3Database { - return &s3Database{ - url: url, - obj: obj, - hash: hash, - info: info, - put: put, - } -} - -type s3Database struct { - url *url.URL - obj obj.Interface - hash relation.ObjectHashModelInterface - info relation.ObjectInfoModelInterface - put relation.ObjectPutModelInterface -} - -// today 今天的日期 -func (c *s3Database) today() string { - return time.Now().Format("20060102") -} - -// fragmentName 根据序号生成文件名 -func (c *s3Database) fragmentName(index int) string { - return fragmentPrefix + strconv.Itoa(index+1) -} - -// getFragmentNum 获取分片大小和分片数量 -func (c *s3Database) getFragmentNum(fragmentSize int64, objectSize int64) (int64, int) { - if size := c.obj.MinFragmentSize(); fragmentSize < size { - fragmentSize = size - } - if fragmentSize <= 0 || objectSize <= fragmentSize { - return objectSize, 1 - } else { - num := int(objectSize / fragmentSize) - if objectSize%fragmentSize > 0 { - num++ - } - if n := c.obj.MaxFragmentNum(); num > n { - num = n - } - return fragmentSize, num - } -} - -func (c *s3Database) CheckHash(hash string) error { - val, err := hex.DecodeString(hash) - if err != nil { - return err - } - if len(val) != md5.Size { - return errs.ErrArgs.Wrap("invalid hash") - } - return nil -} - -func (c *s3Database) urlName(name string) string { - u := url.URL{ - Scheme: c.url.Scheme, - Opaque: c.url.Opaque, - User: c.url.User, - Host: c.url.Host, - Path: c.url.Path, - RawPath: c.url.RawPath, - ForceQuery: c.url.ForceQuery, - RawQuery: c.url.RawQuery, - Fragment: c.url.Fragment, - RawFragment: c.url.RawFragment, - } - v := make(url.Values, 1) - v.Set("name", name) - u.RawQuery = v.Encode() - return u.String() -} - -func (c *s3Database) UUID() string { - return uuid.New().String() -} - -func (c *s3Database) HashName(hash string) string { - return path.Join(hashPrefix, hash+"_"+c.today()+"_"+c.UUID()) -} - -func (c *s3Database) isNotFound(err error) bool { - return relation.IsNotFound(err) -} - -func (c *s3Database) ApplyPut(ctx context.Context, req *third.ApplyPutReq) (*third.ApplyPutResp, error) { - if err := c.CheckHash(req.Hash); err != nil { - return nil, err - } - if err := c.obj.CheckName(req.Name); err != nil { - return nil, err - } - if req.ValidTime != 0 && req.ValidTime <= time.Now().UnixMilli() { - return nil, errors.New("invalid ValidTime") - } - var expirationTime *time.Time - if req.ValidTime != 0 { - expirationTime = utils.ToPtr(time.UnixMilli(req.ValidTime)) - } - if hash, err := c.hash.Take(ctx, req.Hash, c.obj.Name()); err == nil { - o := relation.ObjectInfoModel{ - Name: req.Name, - Hash: hash.Hash, - ValidTime: expirationTime, - ContentType: req.ContentType, - CreateTime: time.Now(), - } - if err := c.info.SetObject(ctx, &o); err != nil { - return nil, err - } - return &third.ApplyPutResp{Url: c.urlName(o.Name)}, nil // 服务器已存在 - } else if !c.isNotFound(err) { - return nil, err - } - // 新上传 - var fragmentNum int - const effective = time.Hour * 24 * 2 - req.FragmentSize, fragmentNum = c.getFragmentNum(req.FragmentSize, req.Size) - put := relation.ObjectPutModel{ - PutID: req.PutID, - Hash: req.Hash, - Name: req.Name, - ObjectSize: req.Size, - ContentType: req.ContentType, - FragmentSize: req.FragmentSize, - ValidTime: expirationTime, - EffectiveTime: time.Now().Add(effective), - } - if put.PutID == "" { - put.PutID = c.UUID() - } - if v, err := c.put.Take(ctx, put.PutID); err == nil { - now := time.Now().UnixMilli() - if v.EffectiveTime.UnixMilli() <= now { - if err := c.put.DelPut(ctx, []string{v.PutID}); err != nil { - return nil, err - } - } else { - return nil, errs.ErrDuplicateKey.Wrap(fmt.Sprintf("duplicate put id %s", put.PutID)) - } - } else if !c.isNotFound(err) { - return nil, err - } - put.Path = path.Join(tempPrefix, c.today(), req.Hash, put.PutID) - putURLs := make([]string, 0, fragmentNum) - for i := 0; i < fragmentNum; i++ { - url, err := c.obj.PresignedPutURL(ctx, &obj.ApplyPutArgs{ - Bucket: c.obj.TempBucket(), - Name: path.Join(put.Path, c.fragmentName(i)), - Effective: effective, - MaxObjectSize: req.FragmentSize, - }) - if err != nil { - return nil, err - } - putURLs = append(putURLs, url) - } - urlsJsonData, err := json.Marshal(putURLs) - if err != nil { - return nil, err - } - t := md5.Sum(urlsJsonData) - put.PutURLsHash = hex.EncodeToString(t[:]) - _, err = c.obj.PutObject( - ctx, - &obj.BucketObject{Bucket: c.obj.TempBucket(), Name: path.Join(put.Path, urlsName)}, - bytes.NewReader(urlsJsonData), - int64(len(urlsJsonData)), - ) - if err != nil { - return nil, err - } - put.CreateTime = time.Now() - if err := c.put.Create(ctx, []*relation.ObjectPutModel{&put}); err != nil { - return nil, err - } - return &third.ApplyPutResp{ - PutID: put.PutID, - FragmentSize: put.FragmentSize, - PutURLs: putURLs, - ValidTime: put.EffectiveTime.UnixMilli(), - }, nil -} - -func (c *s3Database) GetPut(ctx context.Context, req *third.GetPutReq) (*third.GetPutResp, error) { - up, err := c.put.Take(ctx, req.PutID) - if err != nil { - return nil, err - } - reader, err := c.obj.GetObject( - ctx, - &obj.BucketObject{Bucket: c.obj.TempBucket(), Name: path.Join(up.Path, urlsName)}, - ) - if err != nil { - return nil, err - } - urlsData, err := io.ReadAll(reader) - if err != nil { - return nil, err - } - t := md5.Sum(urlsData) - if h := hex.EncodeToString(t[:]); h != up.PutURLsHash { - return nil, fmt.Errorf("invalid put urls hash %s %s", h, up.PutURLsHash) - } - var urls []string - if err := json.Unmarshal(urlsData, &urls); err != nil { - return nil, err - } - _, fragmentNum := c.getFragmentNum(up.FragmentSize, up.ObjectSize) - if len(urls) != fragmentNum { - return nil, fmt.Errorf("invalid urls length %d fragment %d", len(urls), fragmentNum) - } - fragments := make([]*third.GetPutFragment, fragmentNum) - for i := 0; i < fragmentNum; i++ { - name := path.Join(up.Path, c.fragmentName(i)) - o, err := c.obj.GetObjectInfo(ctx, &obj.BucketObject{ - Bucket: c.obj.TempBucket(), - Name: name, - }) - if err != nil { - if c.obj.IsNotFound(err) { - fragments[i] = &third.GetPutFragment{Url: urls[i]} - continue - } - return nil, err - } - fragments[i] = &third.GetPutFragment{Size: o.Size, Hash: o.Hash, Url: urls[i]} - } - var validTime int64 - if up.ValidTime != nil { - validTime = up.ValidTime.UnixMilli() - } - return &third.GetPutResp{ - FragmentSize: up.FragmentSize, - Size: up.ObjectSize, - Name: up.Name, - Hash: up.Hash, - Fragments: fragments, - PutURLsHash: up.PutURLsHash, - ContentType: up.ContentType, - ValidTime: validTime, - }, nil -} - -func (c *s3Database) ConfirmPut(ctx context.Context, req *third.ConfirmPutReq) (_ *third.ConfirmPutResp, _err error) { - put, err := c.put.Take(ctx, req.PutID) - if err != nil { - return nil, err - } - _, pack := c.getFragmentNum(put.FragmentSize, put.ObjectSize) - defer func() { - if _err == nil { - // 清理上传的碎片 - err := c.obj.DeleteObject(ctx, &obj.BucketObject{Bucket: c.obj.TempBucket(), Name: put.Path}) - if err != nil { - log.ZError(ctx, "deleteObject failed", err, "Bucket", c.obj.TempBucket(), "Path", put.Path) - } - } - }() - now := time.Now().UnixMilli() - if put.EffectiveTime.UnixMilli() < now { - return nil, errs.ErrFileUploadedExpired.Wrap("put expired") - } - if put.ValidTime != nil && put.ValidTime.UnixMilli() < now { - return nil, errs.ErrFileUploadedExpired.Wrap("object expired") - } - if hash, err := c.hash.Take(ctx, put.Hash, c.obj.Name()); err == nil { - o := relation.ObjectInfoModel{ - Name: put.Name, - Hash: hash.Hash, - ValidTime: put.ValidTime, - ContentType: put.ContentType, - CreateTime: time.Now(), - } - if err := c.info.SetObject(ctx, &o); err != nil { - return nil, err - } - defer func() { - err := c.obj.DeleteObject(ctx, &obj.BucketObject{ - Bucket: c.obj.TempBucket(), - Name: put.Path, - }) - if err != nil { - log.ZError(ctx, "DeleteObject", err, "Bucket", c.obj.TempBucket(), "Path", put.Path) - } - }() - // 服务端已存在 - return &third.ConfirmPutResp{ - Url: c.urlName(o.Name), - }, nil - } else if !c.isNotFound(err) { - return nil, err - } - src := make([]obj.BucketObject, pack) - for i := 0; i < pack; i++ { - name := path.Join(put.Path, c.fragmentName(i)) - o, err := c.obj.GetObjectInfo(ctx, &obj.BucketObject{ - Bucket: c.obj.TempBucket(), - Name: name, - }) - if err != nil { - return nil, err - } - if i+1 == pack { // 最后一个 - size := put.ObjectSize - put.FragmentSize*int64(i) - if size != o.Size { - return nil, fmt.Errorf("last fragment %d size %d not equal to %d hash %s", i, o.Size, size, o.Hash) - } - } else { - if o.Size != put.FragmentSize { - return nil, fmt.Errorf("fragment %d size %d not equal to %d hash %s", i, o.Size, put.FragmentSize, o.Hash) - } - } - src[i] = obj.BucketObject{ - Bucket: c.obj.TempBucket(), - Name: name, - } - } - dst := &obj.BucketObject{ - Bucket: c.obj.DataBucket(), - Name: c.HashName(put.Hash), - } - if len(src) == 1 { // 未分片直接触发copy - // 检查数据完整性,避免脏数据 - o, err := c.obj.GetObjectInfo(ctx, &src[0]) - if err != nil { - return nil, err - } - if put.ObjectSize != o.Size { - return nil, fmt.Errorf("size mismatching should %d reality %d", put.ObjectSize, o.Size) - } - if put.Hash != o.Hash { - return nil, fmt.Errorf("hash mismatching should %s reality %s", put.Hash, o.Hash) - } - if err := c.obj.CopyObject(ctx, &src[0], dst); err != nil { - return nil, err - } - } else { - tempBucket := &obj.BucketObject{ - Bucket: c.obj.TempBucket(), - Name: path.Join(put.Path, "merge_"+c.UUID()), - } - defer func() { // 清理合成的文件 - if err := c.obj.DeleteObject(ctx, tempBucket); err != nil { - log.ZError(ctx, "DeleteObject", err, "Bucket", tempBucket.Bucket, "Path", tempBucket.Name) - } - }() - err := c.obj.ComposeObject(ctx, src, tempBucket) - if err != nil { - return nil, err - } - info, err := c.obj.GetObjectInfo(ctx, tempBucket) - if err != nil { - return nil, err - } - if put.ObjectSize != info.Size { - return nil, fmt.Errorf("size mismatch should %d reality %d", put.ObjectSize, info.Size) - } - if put.Hash != info.Hash { - return nil, fmt.Errorf("hash mismatch should %s reality %s", put.Hash, info.Hash) - } - if err := c.obj.CopyObject(ctx, tempBucket, dst); err != nil { - return nil, err - } - } - h := &relation.ObjectHashModel{ - Hash: put.Hash, - Engine: c.obj.Name(), - Size: put.ObjectSize, - Bucket: c.obj.DataBucket(), - Name: dst.Name, - CreateTime: time.Now(), - } - if err := c.hash.Create(ctx, []*relation.ObjectHashModel{h}); err != nil { - return nil, err - } - o := &relation.ObjectInfoModel{ - Name: put.Name, - Hash: put.Hash, - ContentType: put.ContentType, - ValidTime: put.ValidTime, - CreateTime: time.Now(), - } - if err := c.info.SetObject(ctx, o); err != nil { - return nil, err - } - if err := c.put.DelPut(ctx, []string{put.PutID}); err != nil { - log.ZError(ctx, "DelPut", err, "PutID", put.PutID) - } - return &third.ConfirmPutResp{ - Url: c.urlName(o.Name), - }, nil -} - -func (c *s3Database) GetUrl(ctx context.Context, req *third.GetUrlReq) (*third.GetUrlResp, error) { - info, err := c.info.Take(ctx, req.Name) - if err != nil { - return nil, err - } - if info.ValidTime != nil && info.ValidTime.Before(time.Now()) { - return nil, errs.ErrRecordNotFound.Wrap("object expired") - } - hash, err := c.hash.Take(ctx, info.Hash, c.obj.Name()) - if err != nil { - return nil, err - } - opt := obj.HeaderOption{ContentType: info.ContentType} - if req.Attachment { - opt.Filename = info.Name - } - u, err := c.obj.PresignedGetURL(ctx, hash.Bucket, hash.Name, time.Duration(req.Expires)*time.Millisecond, &opt) - if err != nil { - return nil, err - } - return &third.GetUrlResp{ - Url: u, - Size: hash.Size, - Hash: hash.Hash, - }, nil -} - -func (c *s3Database) CleanExpirationObject(ctx context.Context, t time.Time) { - // 清理上传产生的临时文件 - c.cleanPutTemp(ctx, t, 10) - // 清理hash引用全过期的文件 - c.cleanExpirationObject(ctx, t) - // 清理没有引用的hash对象 - c.clearNoCitation(ctx, c.obj.Name(), 10) -} - -func (c *s3Database) cleanPutTemp(ctx context.Context, t time.Time, num int) { - for { - puts, err := c.put.FindExpirationPut(ctx, t, num) - if err != nil { - log.ZError(ctx, "FindExpirationPut", err, "Time", t, "Num", num) - return - } - if len(puts) == 0 { - return - } - for _, put := range puts { - err := c.obj.DeleteObject(ctx, &obj.BucketObject{Bucket: c.obj.TempBucket(), Name: put.Path}) - if err != nil { - log.ZError(ctx, "DeleteObject", err, "Bucket", c.obj.TempBucket(), "Path", put.Path) - return - } - } - ids := utils.Slice(puts, func(e *relation.ObjectPutModel) string { return e.PutID }) - err = c.put.DelPut(ctx, ids) - if err != nil { - log.ZError(ctx, "DelPut", err, "PutID", ids) - return - } - } -} - -func (c *s3Database) cleanExpirationObject(ctx context.Context, t time.Time) { - err := c.info.DeleteExpiration(ctx, t) - if err != nil { - log.ZError(ctx, "DeleteExpiration", err, "Time", t) - } -} - -func (c *s3Database) clearNoCitation(ctx context.Context, engine string, limit int) { - for { - list, err := c.hash.DeleteNoCitation(ctx, engine, limit) - if err != nil { - log.ZError(ctx, "DeleteNoCitation", err, "Engine", engine, "Limit", limit) - return - } - if len(list) == 0 { - return - } - var hasErr bool - for _, h := range list { - err := c.obj.DeleteObject(ctx, &obj.BucketObject{Bucket: h.Bucket, Name: h.Name}) - if err != nil { - hasErr = true - log.ZError(ctx, "DeleteObject", err, "Bucket", h.Bucket, "Path", h.Name) - continue - } - } - if hasErr { - return - } - } -} - -func (c *s3Database) GetHashInfo(ctx context.Context, req *third.GetHashInfoReq) (*third.GetHashInfoResp, error) { - if err := c.CheckHash(req.Hash); err != nil { - return nil, err - } - o, err := c.hash.Take(ctx, req.Hash, c.obj.Name()) - if err != nil { - return nil, err - } - return &third.GetHashInfoResp{ - Hash: o.Hash, - Size: o.Size, - }, nil -} diff --git a/pkg/common/db/controller/user.go b/pkg/common/db/controller/user.go index 03224b61e..db725ae60 100644 --- a/pkg/common/db/controller/user.go +++ b/pkg/common/db/controller/user.go @@ -45,7 +45,7 @@ type UserDatabase interface { //函数内部先查询db中是否存在,存在则什么都不做;不存在则插入 InitOnce(ctx context.Context, users []*relation.UserModel) (err error) // 获取用户总数 - CountTotal(ctx context.Context) (int64, error) + CountTotal(ctx context.Context, before *time.Time) (int64, error) // 获取范围内用户增量 CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) } @@ -151,8 +151,8 @@ func (u *userDatabase) GetAllUserID(ctx context.Context) (userIDs []string, err return u.userDB.GetAllUserID(ctx) } -func (u *userDatabase) CountTotal(ctx context.Context) (count int64, err error) { - return u.userDB.CountTotal(ctx) +func (u *userDatabase) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) { + return u.userDB.CountTotal(ctx, before) } func (u *userDatabase) CountRangeEverydayTotal( diff --git a/pkg/common/db/obj/minio.go b/pkg/common/db/obj/minio.go deleted file mode 100644 index ffd8684c9..000000000 --- a/pkg/common/db/obj/minio.go +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package obj - -import ( - "context" - "errors" - "fmt" - "io" - "net/http" - "net/url" - "time" - - "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" - "github.com/minio/minio-go/v7/pkg/s3utils" - - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" - "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" -) - -func NewMinioInterface() (Interface, error) { - conf := config.Config.Object.Minio - u, err := url.Parse(conf.Endpoint) - if err != nil { - return nil, fmt.Errorf("minio endpoint parse %w", err) - } - if u.Scheme != "http" && u.Scheme != "https" { - return nil, fmt.Errorf("invalid minio endpoint scheme %s", u.Scheme) - } - client, err := minio.New(u.Host, &minio.Options{ - Creds: credentials.NewStaticV4(conf.AccessKeyID, conf.SecretAccessKey, ""), - Secure: u.Scheme == "https", - }) - if err != nil { - return nil, fmt.Errorf("minio new client %w", err) - } - ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) - defer cancel() - initBucket := func(ctx context.Context) error { - for _, bucket := range utils.Distinct([]string{conf.TempBucket, conf.DataBucket}) { - exists, err := client.BucketExists(ctx, bucket) - if err != nil { - return fmt.Errorf("minio bucket %s exists %w", bucket, err) - } - if exists { - continue - } - opt := minio.MakeBucketOptions{ - Region: conf.Location, - ObjectLocking: conf.IsDistributedMod, - } - if err := client.MakeBucket(ctx, bucket, opt); err != nil { - return fmt.Errorf("minio make bucket %s %w", bucket, err) - } - } - return nil - } - if err := initBucket(ctx); err != nil { - fmt.Println("minio init error:", err) - } - return &minioImpl{ - client: client, - tempBucket: conf.TempBucket, - dataBucket: conf.DataBucket, - }, nil -} - -type minioImpl struct { - tempBucket string // 上传桶 - dataBucket string // 永久桶 - urlstr string // 访问地址 - client *minio.Client -} - -func (m *minioImpl) Name() string { - return "minio" -} - -func (m *minioImpl) MinFragmentSize() int64 { - return 1024 * 1024 * 5 // 每个分片最小大小 minio.absMinPartSize -} - -func (m *minioImpl) MaxFragmentNum() int { - return 1000 // 最大分片数量 minio.maxPartsCount -} - -func (m *minioImpl) MinExpirationTime() time.Duration { - return time.Hour * 24 -} - -func (m *minioImpl) TempBucket() string { - return m.tempBucket -} - -func (m *minioImpl) DataBucket() string { - return m.dataBucket -} - -func (m *minioImpl) PresignedGetURL( - ctx context.Context, - bucket string, - name string, - expires time.Duration, - opt *HeaderOption, -) (string, error) { - var reqParams url.Values - if opt != nil { - reqParams = make(url.Values) - if opt.ContentType != "" { - reqParams.Set("response-content-type", opt.ContentType) - } - if opt.Filename != "" { - reqParams.Set("response-content-disposition", "attachment;filename="+opt.Filename) - } - } - u, err := m.client.PresignedGetObject(ctx, bucket, name, expires, reqParams) - if err != nil { - return "", err - } - return u.String(), nil -} - -func (m *minioImpl) PresignedPutURL(ctx context.Context, args *ApplyPutArgs) (string, error) { - if args.Effective <= 0 { - return "", errors.New("EffectiveTime <= 0") - } - _, err := m.GetObjectInfo(ctx, &BucketObject{ - Bucket: m.tempBucket, - Name: args.Name, - }) - if err == nil { - return "", fmt.Errorf("minio bucket %s name %s already exists", args.Bucket, args.Name) - } else if !m.IsNotFound(err) { - return "", err - } - u, err := m.client.PresignedPutObject(ctx, m.tempBucket, args.Name, args.Effective) - if err != nil { - return "", fmt.Errorf("minio apply error: %w", err) - } - return u.String(), nil -} - -func (m *minioImpl) GetObjectInfo(ctx context.Context, args *BucketObject) (*ObjectInfo, error) { - info, err := m.client.StatObject(ctx, args.Bucket, args.Name, minio.StatObjectOptions{}) - if err != nil { - return nil, err - } - return &ObjectInfo{ - Size: info.Size, - Hash: info.ETag, - }, nil -} - -func (m *minioImpl) CopyObject(ctx context.Context, src *BucketObject, dst *BucketObject) error { - _, err := m.client.CopyObject(ctx, minio.CopyDestOptions{ - Bucket: dst.Bucket, - Object: dst.Name, - }, minio.CopySrcOptions{ - Bucket: src.Bucket, - Object: src.Name, - }) - return err -} - -func (m *minioImpl) DeleteObject(ctx context.Context, info *BucketObject) error { - return m.client.RemoveObject(ctx, info.Bucket, info.Name, minio.RemoveObjectOptions{}) -} - -func (m *minioImpl) MoveObjectInfo(ctx context.Context, src *BucketObject, dst *BucketObject) error { - if err := m.CopyObject(ctx, src, dst); err != nil { - return err - } - return m.DeleteObject(ctx, src) -} - -func (m *minioImpl) ComposeObject(ctx context.Context, src []BucketObject, dst *BucketObject) error { - destOptions := minio.CopyDestOptions{ - Bucket: dst.Bucket, - Object: dst.Name + ".temp", - } - sources := make([]minio.CopySrcOptions, len(src)) - for i, s := range src { - sources[i] = minio.CopySrcOptions{ - Bucket: s.Bucket, - Object: s.Name, - } - } - _, err := m.client.ComposeObject(ctx, destOptions, sources...) - if err != nil { - return err - } - return m.MoveObjectInfo(ctx, &BucketObject{ - Bucket: destOptions.Bucket, - Name: destOptions.Object, - }, &BucketObject{ - Bucket: dst.Bucket, - Name: dst.Name, - }) -} - -func (m *minioImpl) IsNotFound(err error) bool { - if err == nil { - return false - } - switch e := err.(type) { - case minio.ErrorResponse: - return e.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey" - case *minio.ErrorResponse: - return e.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey" - default: - return false - } -} - -func (m *minioImpl) PutObject( - ctx context.Context, - info *BucketObject, - reader io.Reader, - size int64, -) (*ObjectInfo, error) { - update, err := m.client.PutObject(ctx, info.Bucket, info.Name, reader, size, minio.PutObjectOptions{}) - if err != nil { - return nil, err - } - return &ObjectInfo{ - Size: update.Size, - Hash: update.ETag, - }, nil -} - -func (m *minioImpl) GetObject(ctx context.Context, info *BucketObject) (SizeReader, error) { - object, err := m.client.GetObject(ctx, info.Bucket, info.Name, minio.GetObjectOptions{}) - if err != nil { - return nil, err - } - stat, err := object.Stat() - if err != nil { - return nil, err - } - return NewSizeReader(object, stat.Size), nil -} - -func (m *minioImpl) CheckName(name string) error { - return s3utils.CheckValidObjectName(name) -} diff --git a/pkg/common/db/obj/obj.go b/pkg/common/db/obj/obj.go deleted file mode 100644 index d8f5d9390..000000000 --- a/pkg/common/db/obj/obj.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package obj - -import ( - "context" - "io" - "net/http" - "time" -) - -type BucketObject struct { - Bucket string `json:"bucket"` - Name string `json:"name"` -} - -type ApplyPutArgs struct { - Bucket string - Name string - Effective time.Duration // 申请有效时间 - Header http.Header // header - MaxObjectSize int64 -} - -type HeaderOption struct { - ContentType string - Filename string -} - -type ObjectInfo struct { - Size int64 - Hash string -} - -type SizeReader interface { - io.ReadCloser - Size() int64 -} - -func NewSizeReader(r io.ReadCloser, size int64) SizeReader { - if r == nil { - return nil - } - return &sizeReader{ - size: size, - ReadCloser: r, - } -} - -type sizeReader struct { - size int64 - io.ReadCloser -} - -func (r *sizeReader) Size() int64 { - return r.size -} - -type Interface interface { - // Name 存储名字 - Name() string - // MinFragmentSize 最小允许的分片大小 - MinFragmentSize() int64 - // MaxFragmentNum 最大允许的分片数量 - MaxFragmentNum() int - // MinExpirationTime 最小过期时间 - MinExpirationTime() time.Duration - // TempBucket 临时桶名,用于上传 - TempBucket() string - // DataBucket 永久存储的桶名 - DataBucket() string - // PresignedGetURL 通过桶名和对象名返回URL - PresignedGetURL( - ctx context.Context, - bucket string, - name string, - expires time.Duration, - opt *HeaderOption, - ) (string, error) - // PresignedPutURL 申请上传,返回PUT的上传地址 - PresignedPutURL(ctx context.Context, args *ApplyPutArgs) (string, error) - // GetObjectInfo 获取对象信息 - GetObjectInfo(ctx context.Context, args *BucketObject) (*ObjectInfo, error) - // CopyObject 复制对象 - CopyObject(ctx context.Context, src *BucketObject, dst *BucketObject) error - // DeleteObject 删除对象(不存在返回nil) - DeleteObject(ctx context.Context, info *BucketObject) error - // ComposeObject 合并对象 - ComposeObject(ctx context.Context, src []BucketObject, dst *BucketObject) error - // IsNotFound 判断是不是不存在导致的错误 - IsNotFound(err error) bool - // CheckName 检查名字是否可用 - CheckName(name string) error - // PutObject 上传文件 - PutObject(ctx context.Context, info *BucketObject, reader io.Reader, size int64) (*ObjectInfo, error) - // GetObject 下载文件 - GetObject(ctx context.Context, info *BucketObject) (SizeReader, error) -} diff --git a/pkg/common/db/relation/conversation_model.go b/pkg/common/db/relation/conversation_model.go index 47be971c0..04300f704 100644 --- a/pkg/common/db/relation/conversation_model.go +++ b/pkg/common/db/relation/conversation_model.go @@ -1,27 +1,12 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package relation import ( "context" - "gorm.io/gorm" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" + "gorm.io/gorm" ) type ConversationGorm struct { @@ -44,173 +29,66 @@ func (c *ConversationGorm) Delete(ctx context.Context, groupIDs []string) (err e return utils.Wrap(c.db(ctx).Where("group_id in (?)", groupIDs).Delete(&relation.ConversationModel{}).Error, "") } -func (c *ConversationGorm) UpdateByMap( - ctx context.Context, - userIDList []string, - conversationID string, - args map[string]interface{}, -) (rows int64, err error) { +func (c *ConversationGorm) UpdateByMap(ctx context.Context, userIDList []string, conversationID string, args map[string]interface{}) (rows int64, err error) { result := c.db(ctx).Where("owner_user_id IN (?) and conversation_id=?", userIDList, conversationID).Updates(args) return result.RowsAffected, utils.Wrap(result.Error, "") } func (c *ConversationGorm) Update(ctx context.Context, conversation *relation.ConversationModel) (err error) { - return utils.Wrap( - c.db(ctx). - Where("owner_user_id = ? and conversation_id = ?", conversation.OwnerUserID, conversation.ConversationID). - Updates(conversation). - Error, - "", - ) -} - -func (c *ConversationGorm) Find( - ctx context.Context, - ownerUserID string, - conversationIDs []string, -) (conversations []*relation.ConversationModel, err error) { - err = utils.Wrap( - c.db(ctx). - Where("owner_user_id=? and conversation_id IN (?)", ownerUserID, conversationIDs). - Find(&conversations). - Error, - "", - ) + return utils.Wrap(c.db(ctx).Where("owner_user_id = ? and conversation_id = ?", conversation.OwnerUserID, conversation.ConversationID).Updates(conversation).Error, "") +} + +func (c *ConversationGorm) Find(ctx context.Context, ownerUserID string, conversationIDs []string) (conversations []*relation.ConversationModel, err error) { + err = utils.Wrap(c.db(ctx).Where("owner_user_id=? and conversation_id IN (?)", ownerUserID, conversationIDs).Find(&conversations).Error, "") return conversations, err } -func (c *ConversationGorm) Take( - ctx context.Context, - userID, conversationID string, -) (conversation *relation.ConversationModel, err error) { +func (c *ConversationGorm) Take(ctx context.Context, userID, conversationID string) (conversation *relation.ConversationModel, err error) { cc := &relation.ConversationModel{} - return cc, utils.Wrap( - c.db(ctx).Where("conversation_id = ? And owner_user_id = ?", conversationID, userID).Take(cc).Error, - "", - ) -} - -func (c *ConversationGorm) FindUserID( - ctx context.Context, - userIDs []string, - conversationIDs []string, -) (existUserID []string, err error) { - return existUserID, utils.Wrap( - c.db(ctx). - Where(" owner_user_id IN (?) and conversation_id in (?)", userIDs, conversationIDs). - Pluck("owner_user_id", &existUserID). - Error, - "", - ) -} - -func (c *ConversationGorm) FindConversationID( - ctx context.Context, - userID string, - conversationIDList []string, -) (existConversationID []string, err error) { - return existConversationID, utils.Wrap( - c.db(ctx). - Where(" conversation_id IN (?) and owner_user_id=?", conversationIDList, userID). - Pluck("conversation_id", &existConversationID). - Error, - "", - ) -} - -func (c *ConversationGorm) FindUserIDAllConversationID( - ctx context.Context, - userID string, -) (conversationIDList []string, err error) { - return conversationIDList, utils.Wrap( - c.db(ctx).Where("owner_user_id=?", userID).Pluck("conversation_id", &conversationIDList).Error, - "", - ) -} - -func (c *ConversationGorm) FindUserIDAllConversations( - ctx context.Context, - userID string, -) (conversations []*relation.ConversationModel, err error) { + return cc, utils.Wrap(c.db(ctx).Where("conversation_id = ? And owner_user_id = ?", conversationID, userID).Take(cc).Error, "") +} + +func (c *ConversationGorm) FindUserID(ctx context.Context, userIDs []string, conversationIDs []string) (existUserID []string, err error) { + return existUserID, utils.Wrap(c.db(ctx).Where(" owner_user_id IN (?) and conversation_id in (?)", userIDs, conversationIDs).Pluck("owner_user_id", &existUserID).Error, "") +} + +func (c *ConversationGorm) FindConversationID(ctx context.Context, userID string, conversationIDList []string) (existConversationID []string, err error) { + return existConversationID, utils.Wrap(c.db(ctx).Where(" conversation_id IN (?) and owner_user_id=?", conversationIDList, userID).Pluck("conversation_id", &existConversationID).Error, "") +} + +func (c *ConversationGorm) FindUserIDAllConversationID(ctx context.Context, userID string) (conversationIDList []string, err error) { + return conversationIDList, utils.Wrap(c.db(ctx).Where("owner_user_id=?", userID).Pluck("conversation_id", &conversationIDList).Error, "") +} + +func (c *ConversationGorm) FindUserIDAllConversations(ctx context.Context, userID string) (conversations []*relation.ConversationModel, err error) { return conversations, utils.Wrap(c.db(ctx).Where("owner_user_id=?", userID).Find(&conversations).Error, "") } -func (c *ConversationGorm) FindRecvMsgNotNotifyUserIDs( - ctx context.Context, - groupID string, -) (userIDs []string, err error) { - return userIDs, utils.Wrap( - c.db(ctx). - Where("group_id = ? and recv_msg_opt = ?", groupID, constant.ReceiveNotNotifyMessage). - Pluck("user_id", &userIDs). - Error, - "", - ) -} - -func (c *ConversationGorm) FindSuperGroupRecvMsgNotNotifyUserIDs( - ctx context.Context, - groupID string, -) (userIDs []string, err error) { - return userIDs, utils.Wrap( - c.db(ctx). - Where("group_id = ? and recv_msg_opt = ? and conversation_type = ?", groupID, constant.ReceiveNotNotifyMessage, constant.SuperGroupChatType). - Pluck("user_id", &userIDs). - Error, - "", - ) -} - -func (c *ConversationGorm) GetUserRecvMsgOpt( - ctx context.Context, - ownerUserID, conversationID string, -) (opt int, err error) { +func (c *ConversationGorm) FindRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) (userIDs []string, err error) { + return userIDs, utils.Wrap(c.db(ctx).Where("group_id = ? and recv_msg_opt = ?", groupID, constant.ReceiveNotNotifyMessage).Pluck("user_id", &userIDs).Error, "") +} + +func (c *ConversationGorm) FindSuperGroupRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) (userIDs []string, err error) { + return userIDs, utils.Wrap(c.db(ctx).Where("group_id = ? and recv_msg_opt = ? and conversation_type = ?", groupID, constant.ReceiveNotNotifyMessage, constant.SuperGroupChatType).Pluck("user_id", &userIDs).Error, "") +} + +func (c *ConversationGorm) GetUserRecvMsgOpt(ctx context.Context, ownerUserID, conversationID string) (opt int, err error) { var conversation relation.ConversationModel - return int( - conversation.RecvMsgOpt, - ), utils.Wrap( - c.db(ctx). - Where("conversation_id = ? And owner_user_id = ?", conversationID, ownerUserID). - Select("recv_msg_opt"). - Find(&conversation). - Error, - "", - ) + return int(conversation.RecvMsgOpt), utils.Wrap(c.db(ctx).Where("conversation_id = ? And owner_user_id = ?", conversationID, ownerUserID).Select("recv_msg_opt").Find(&conversation).Error, "") } func (c *ConversationGorm) GetAllConversationIDs(ctx context.Context) (conversationIDs []string, err error) { - return conversationIDs, utils.Wrap( - c.db(ctx).Distinct("conversation_id").Pluck("conversation_id", &conversationIDs).Error, - "", - ) -} - -func (c *ConversationGorm) GetUserAllHasReadSeqs( - ctx context.Context, - ownerUserID string, -) (hasReadSeqs map[string]int64, err error) { - var conversations []*relation.ConversationModel - err = utils.Wrap( - c.db(ctx). - Where("owner_user_id = ?", ownerUserID). - Select("conversation_id", "has_read_seq"). - Find(&conversations). - Error, - "", - ) - hasReadSeqs = make(map[string]int64, len(conversations)) - // for _, conversation := range conversations { - // hasReadSeqs[conversation.ConversationID] = conversation.HasReadSeq - // } - return hasReadSeqs, err -} - -func (c *ConversationGorm) GetConversationsByConversationID( - ctx context.Context, - conversationIDs []string, -) (conversations []*relation.ConversationModel, err error) { - return conversations, utils.Wrap( - c.db(ctx).Where("conversation_id IN (?)", conversationIDs).Find(&conversations).Error, - "", - ) + return conversationIDs, utils.Wrap(c.db(ctx).Distinct("conversation_id").Pluck("conversation_id", &conversationIDs).Error, "") +} + +func (c *ConversationGorm) GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (hasReadSeqs map[string]int64, err error) { + return nil, nil +} + +func (c *ConversationGorm) GetConversationsByConversationID(ctx context.Context, conversationIDs []string) (conversations []*relation.ConversationModel, err error) { + return conversations, utils.Wrap(c.db(ctx).Where("conversation_id IN (?)", conversationIDs).Find(&conversations).Error, "") +} + +func (c *ConversationGorm) GetConversationIDsNeedDestruct(ctx context.Context) (conversations []*relation.ConversationModel, err error) { + return conversations, utils.Wrap(c.db(ctx).Where("is_msg_destruct = 1 && UNIX_TIMESTAMP(NOW()) > (msg_destruct_time + UNIX_TIMESTAMP(latest_msg_destruct_time)) && msg_destruct_time != 0").Find(&conversations).Error, "") } diff --git a/pkg/common/db/relation/group_model.go b/pkg/common/db/relation/group_model.go index 8e3431c83..f3e1aec2f 100644 --- a/pkg/common/db/relation/group_model.go +++ b/pkg/common/db/relation/group_model.go @@ -1,27 +1,13 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package relation import ( "context" - - "gorm.io/gorm" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/ormutil" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" + "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" + "gorm.io/gorm" + "time" ) var _ relation.GroupModelInterface = (*GroupGorm)(nil) @@ -47,13 +33,7 @@ func (g *GroupGorm) UpdateMap(ctx context.Context, groupID string, args map[stri } func (g *GroupGorm) UpdateStatus(ctx context.Context, groupID string, status int32) (err error) { - return utils.Wrap( - g.DB.Where("group_id = ?", groupID). - Model(&relation.GroupModel{}). - Updates(map[string]any{"status": status}). - Error, - "", - ) + return utils.Wrap(g.DB.Where("group_id = ?", groupID).Model(&relation.GroupModel{}).Updates(map[string]any{"status": status}).Error, "") } func (g *GroupGorm) Find(ctx context.Context, groupIDs []string) (groups []*relation.GroupModel, err error) { @@ -65,17 +45,37 @@ func (g *GroupGorm) Take(ctx context.Context, groupID string) (group *relation.G return group, utils.Wrap(g.DB.Where("group_id = ?", groupID).Take(group).Error, "") } -func (g *GroupGorm) Search( - ctx context.Context, - keyword string, - pageNumber, showNumber int32, -) (total uint32, groups []*relation.GroupModel, err error) { +func (g *GroupGorm) Search(ctx context.Context, keyword string, pageNumber, showNumber int32) (total uint32, groups []*relation.GroupModel, err error) { return ormutil.GormSearch[relation.GroupModel](g.DB, []string{"name"}, keyword, pageNumber, showNumber) } func (g *GroupGorm) GetGroupIDsByGroupType(ctx context.Context, groupType int) (groupIDs []string, err error) { - return groupIDs, utils.Wrap( - g.DB.Model(&relation.GroupModel{}).Where("group_type = ? ", groupType).Pluck("group_id", &groupIDs).Error, - "", - ) + return groupIDs, utils.Wrap(g.DB.Model(&relation.GroupModel{}).Where("group_type = ? ", groupType).Pluck("group_id", &groupIDs).Error, "") +} + +func (g *GroupGorm) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) { + db := g.db(ctx).Model(&relation.GroupModel{}) + if before != nil { + db = db.Where("create_time < ?", before) + } + if err := db.Count(&count).Error; err != nil { + return 0, err + } + return count, nil +} + +func (g *GroupGorm) CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) { + var res []struct { + Date time.Time `gorm:"column:date"` + Count int64 `gorm:"column:count"` + } + err := g.db(ctx).Model(&relation.GroupModel{}).Select("DATE(create_time) AS date, count(1) AS count").Where("create_time >= ? and create_time < ?", start, end).Group("date").Find(&res).Error + if err != nil { + return nil, errs.Wrap(err) + } + v := make(map[string]int64) + for _, r := range res { + v[r.Date.Format("2006-01-02")] = r.Count + } + return v, nil } diff --git a/pkg/common/db/relation/object_hash_model.go b/pkg/common/db/relation/object_hash_model.go deleted file mode 100644 index 5ff8cd47a..000000000 --- a/pkg/common/db/relation/object_hash_model.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package relation - -import ( - "context" - - "gorm.io/gorm" - - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" - "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" -) - -type ObjectHashGorm struct { - *MetaDB -} - -func NewObjectHash(db *gorm.DB) relation.ObjectHashModelInterface { - return &ObjectHashGorm{ - NewMetaDB(db, &relation.ObjectHashModel{}), - } -} - -func (o *ObjectHashGorm) NewTx(tx any) relation.ObjectHashModelInterface { - return &ObjectHashGorm{ - NewMetaDB(tx.(*gorm.DB), &relation.ObjectHashModel{}), - } -} - -func (o *ObjectHashGorm) Take( - ctx context.Context, - hash string, - engine string, -) (oh *relation.ObjectHashModel, err error) { - oh = &relation.ObjectHashModel{} - return oh, utils.Wrap1(o.DB.Where("hash = ? and engine = ?", hash, engine).Take(oh).Error) -} - -func (o *ObjectHashGorm) Create(ctx context.Context, h []*relation.ObjectHashModel) (err error) { - return utils.Wrap1(o.DB.Create(h).Error) -} - -func (o *ObjectHashGorm) DeleteNoCitation( - ctx context.Context, - engine string, - num int, -) (list []*relation.ObjectHashModel, err error) { - err = o.DB.Table(relation.ObjectHashModelTableName, "as h").Select("h.*"). - Joins("LEFT JOIN "+relation.ObjectInfoModelTableName+" as i ON h.hash = i.hash"). - Where("h.engine = ? AND i.hash IS NULL", engine). - Limit(num). - Find(&list).Error - return list, utils.Wrap1(err) -} diff --git a/pkg/common/db/relation/object_info_model.go b/pkg/common/db/relation/object_info_model.go deleted file mode 100644 index 642ae8ef2..000000000 --- a/pkg/common/db/relation/object_info_model.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package relation - -import ( - "context" - "time" - - "gorm.io/gorm" - - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" - "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" - "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" -) - -type ObjectInfoGorm struct { - *MetaDB -} - -func NewObjectInfo(db *gorm.DB) relation.ObjectInfoModelInterface { - return &ObjectInfoGorm{ - NewMetaDB(db, &relation.ObjectInfoModel{}), - } -} - -func (o *ObjectInfoGorm) NewTx(tx any) relation.ObjectInfoModelInterface { - return &ObjectInfoGorm{ - NewMetaDB(tx.(*gorm.DB), &relation.ObjectInfoModel{}), - } -} - -func (o *ObjectInfoGorm) SetObject(ctx context.Context, obj *relation.ObjectInfoModel) (err error) { - if err := o.DB.WithContext(ctx).Where("name = ?", obj.Name).Delete(&relation.ObjectInfoModel{}).Error; err != nil { - return errs.Wrap(err) - } - return errs.Wrap(o.DB.WithContext(ctx).Create(obj).Error) - //return errs.Wrap(o.DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error { - // if err := tx.Where("name = ?", obj.Name).Delete(&relation.ObjectInfoModel{}).Error; err != nil { - // return errs.Wrap(err) - // } - // return errs.Wrap(tx.Create(obj).Error) - //})) -} - -func (o *ObjectInfoGorm) Take(ctx context.Context, name string) (info *relation.ObjectInfoModel, err error) { - info = &relation.ObjectInfoModel{} - return info, utils.Wrap1(o.DB.WithContext(ctx).Where("name = ?", name).Take(info).Error) -} - -func (o *ObjectInfoGorm) DeleteExpiration(ctx context.Context, expiration time.Time) (err error) { - return utils.Wrap1( - o.DB.WithContext(ctx). - Where("expiration_time IS NOT NULL AND expiration_time <= ?", expiration). - Delete(&relation.ObjectInfoModel{}). - Error, - ) -} diff --git a/pkg/common/db/relation/object_model.go b/pkg/common/db/relation/object_model.go new file mode 100644 index 000000000..cfbd011b8 --- /dev/null +++ b/pkg/common/db/relation/object_model.go @@ -0,0 +1,36 @@ +package relation + +import ( + "context" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" + "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" + "gorm.io/gorm" +) + +type ObjectInfoGorm struct { + *MetaDB +} + +func NewObjectInfo(db *gorm.DB) relation.ObjectInfoModelInterface { + return &ObjectInfoGorm{ + NewMetaDB(db, &relation.ObjectModel{}), + } +} + +func (o *ObjectInfoGorm) NewTx(tx any) relation.ObjectInfoModelInterface { + return &ObjectInfoGorm{ + NewMetaDB(tx.(*gorm.DB), &relation.ObjectModel{}), + } +} + +func (o *ObjectInfoGorm) SetObject(ctx context.Context, obj *relation.ObjectModel) (err error) { + if err := o.DB.WithContext(ctx).Where("name = ?", obj.Name).FirstOrCreate(obj).Error; err != nil { + return errs.Wrap(err) + } + return nil +} + +func (o *ObjectInfoGorm) Take(ctx context.Context, name string) (info *relation.ObjectModel, err error) { + info = &relation.ObjectModel{} + return info, errs.Wrap(o.DB.WithContext(ctx).Where("name = ?", name).Take(info).Error) +} diff --git a/pkg/common/db/relation/object_put_model.go b/pkg/common/db/relation/object_put_model.go deleted file mode 100644 index d2df2636d..000000000 --- a/pkg/common/db/relation/object_put_model.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package relation - -import ( - "context" - "time" - - "gorm.io/gorm" - - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation" - "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" -) - -type ObjectPutGorm struct { - *MetaDB -} - -func NewObjectPut(db *gorm.DB) relation.ObjectPutModelInterface { - return &ObjectPutGorm{ - NewMetaDB(db, &relation.ObjectPutModel{}), - } -} - -func (o *ObjectPutGorm) NewTx(tx any) relation.ObjectPutModelInterface { - return &ObjectPutGorm{ - NewMetaDB(tx.(*gorm.DB), &relation.ObjectPutModel{}), - } -} - -func (o *ObjectPutGorm) Create(ctx context.Context, m []*relation.ObjectPutModel) (err error) { - return utils.Wrap1(o.DB.Create(m).Error) -} - -func (o *ObjectPutGorm) Take(ctx context.Context, putID string) (put *relation.ObjectPutModel, err error) { - put = &relation.ObjectPutModel{} - return put, utils.Wrap1(o.DB.Where("put_id = ?", putID).Take(put).Error) -} - -func (o *ObjectPutGorm) SetCompleted(ctx context.Context, putID string) (err error) { - return utils.Wrap1(o.DB.Model(&relation.ObjectPutModel{}).Where("put_id = ?", putID).Update("complete", true).Error) -} - -func (o *ObjectPutGorm) FindExpirationPut( - ctx context.Context, - expirationTime time.Time, - num int, -) (list []*relation.ObjectPutModel, err error) { - err = o.DB.Where("effective_time <= ?", expirationTime).Limit(num).Find(&list).Error - return list, utils.Wrap1(err) -} - -func (o *ObjectPutGorm) DelPut(ctx context.Context, ids []string) (err error) { - return utils.Wrap1(o.DB.Where("put_id IN ?", ids).Delete(&relation.ObjectPutModel{}).Error) -} diff --git a/pkg/common/db/relation/user_model.go b/pkg/common/db/relation/user_model.go index 89a1773d4..d03216d9b 100644 --- a/pkg/common/db/relation/user_model.go +++ b/pkg/common/db/relation/user_model.go @@ -41,7 +41,7 @@ func (u *UserGorm) Create(ctx context.Context, users []*relation.UserModel) (err // 更新用户信息 零值 func (u *UserGorm) UpdateByMap(ctx context.Context, userID string, args map[string]interface{}) (err error) { - return utils.Wrap(u.db(ctx).Where("user_id = ?", userID).Updates(args).Error, "") + return utils.Wrap(u.db(ctx).Model(&relation.UserModel{}).Where("user_id = ?", userID).Updates(args).Error, "") } // 更新多个用户信息 非零值 @@ -94,9 +94,15 @@ func (u *UserGorm) GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) ( return opt, err } -func (u *UserGorm) CountTotal(ctx context.Context) (count int64, err error) { - err = u.db(ctx).Model(&relation.UserModel{}).Count(&count).Error - return count, errs.Wrap(err) +func (u *UserGorm) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) { + db := u.db(ctx).Model(&relation.UserModel{}) + if before != nil { + db = db.Where("create_time < ?", before) + } + if err := db.Count(&count).Error; err != nil { + return 0, err + } + return count, nil } func (u *UserGorm) CountRangeEverydayTotal( diff --git a/pkg/common/db/s3/cont/consts.go b/pkg/common/db/s3/cont/consts.go new file mode 100644 index 000000000..144370a2d --- /dev/null +++ b/pkg/common/db/s3/cont/consts.go @@ -0,0 +1,9 @@ +package cont + +const ( + hashPath = "openim/data/hash/" + tempPath = "openim/temp/" + UploadTypeMultipart = 1 // 分片上传 + UploadTypePresigned = 2 // 预签名上传 + partSeparator = "," +) diff --git a/pkg/common/db/s3/cont/controller.go b/pkg/common/db/s3/cont/controller.go new file mode 100644 index 000000000..ba834a739 --- /dev/null +++ b/pkg/common/db/s3/cont/controller.go @@ -0,0 +1,244 @@ +package cont + +import ( + "context" + "crypto/md5" + "encoding/hex" + "errors" + "fmt" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" + "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" + "github.com/google/uuid" + "path" + "strings" + "time" +) + +func New(impl s3.Interface) *Controller { + return &Controller{impl: impl} +} + +type Controller struct { + impl s3.Interface +} + +func (c *Controller) HashPath(md5 string) string { + return path.Join(hashPath, md5) +} + +func (c *Controller) NowPath() string { + now := time.Now() + return path.Join( + fmt.Sprintf("%04d", now.Year()), + fmt.Sprintf("%02d", now.Month()), + fmt.Sprintf("%02d", now.Day()), + fmt.Sprintf("%02d", now.Hour()), + fmt.Sprintf("%02d", now.Minute()), + fmt.Sprintf("%02d", now.Second()), + ) +} + +func (c *Controller) UUID() string { + id := uuid.New() + return hex.EncodeToString(id[:]) +} + +func (c *Controller) PartSize(ctx context.Context, size int64) (int64, error) { + return c.impl.PartSize(ctx, size) +} + +func (c *Controller) PartLimit() *s3.PartLimit { + return c.impl.PartLimit() +} + +func (c *Controller) GetHashObject(ctx context.Context, hash string) (*s3.ObjectInfo, error) { + return c.impl.StatObject(ctx, c.HashPath(hash)) +} + +func (c *Controller) InitiateUpload(ctx context.Context, hash string, size int64, expire time.Duration, maxParts int) (*InitiateUploadResult, error) { + defer log.ZDebug(ctx, "return") + if size < 0 { + return nil, errors.New("invalid size") + } + if hashBytes, err := hex.DecodeString(hash); err != nil { + return nil, err + } else if len(hashBytes) != md5.Size { + return nil, errors.New("invalid md5") + } + partSize, err := c.impl.PartSize(ctx, size) + if err != nil { + return nil, err + } + partNumber := int(size / partSize) + if size%partSize > 0 { + partNumber++ + } + if maxParts > 0 && partNumber > 0 && partNumber < maxParts { + return nil, errors.New(fmt.Sprintf("too many parts: %d", partNumber)) + } + if info, err := c.impl.StatObject(ctx, c.HashPath(hash)); err == nil { + return nil, &HashAlreadyExistsError{Object: info} + } else if !c.impl.IsNotFound(err) { + return nil, err + } + if size <= partSize { + // 预签名上传 + key := path.Join(tempPath, c.NowPath(), fmt.Sprintf("%s_%d_%s.presigned", hash, size, c.UUID())) + rawURL, err := c.impl.PresignedPutObject(ctx, key, expire) + if err != nil { + return nil, err + } + return &InitiateUploadResult{ + UploadID: newMultipartUploadID(multipartUploadID{ + Type: UploadTypePresigned, + ID: "", + Key: key, + Size: size, + Hash: hash, + }), + PartSize: partSize, + Sign: &s3.AuthSignResult{ + Parts: []s3.SignPart{ + { + PartNumber: 1, + URL: rawURL, + }, + }, + }, + }, nil + } else { + // 分片上传 + upload, err := c.impl.InitiateMultipartUpload(ctx, c.HashPath(hash)) + if err != nil { + return nil, err + } + if maxParts < 0 { + maxParts = partNumber + } + var authSign *s3.AuthSignResult + if maxParts > 0 { + partNumbers := make([]int, partNumber) + for i := 0; i < maxParts; i++ { + partNumbers[i] = i + 1 + } + authSign, err = c.impl.AuthSign(ctx, upload.UploadID, upload.Key, time.Hour*24, partNumbers) + if err != nil { + return nil, err + } + } + return &InitiateUploadResult{ + UploadID: newMultipartUploadID(multipartUploadID{ + Type: UploadTypeMultipart, + ID: upload.UploadID, + Key: upload.Key, + Size: size, + Hash: hash, + }), + PartSize: partSize, + Sign: authSign, + }, nil + } +} + +func (c *Controller) CompleteUpload(ctx context.Context, uploadID string, partHashs []string) (*UploadResult, error) { + defer log.ZDebug(ctx, "return") + upload, err := parseMultipartUploadID(uploadID) + if err != nil { + return nil, err + } + if md5Sum := md5.Sum([]byte(strings.Join(partHashs, partSeparator))); hex.EncodeToString(md5Sum[:]) != upload.Hash { + fmt.Println("CompleteUpload sum:", hex.EncodeToString(md5Sum[:]), "upload hash:", upload.Hash) + return nil, errors.New("md5 mismatching") + } + if info, err := c.impl.StatObject(ctx, c.HashPath(upload.Hash)); err == nil { + return &UploadResult{ + Key: info.Key, + Size: info.Size, + Hash: info.ETag, + }, nil + } else if !c.impl.IsNotFound(err) { + return nil, err + } + cleanObject := make(map[string]struct{}) + defer func() { + for key := range cleanObject { + _ = c.impl.DeleteObject(ctx, key) + } + }() + var targetKey string + switch upload.Type { + case UploadTypeMultipart: + parts := make([]s3.Part, len(partHashs)) + for i, part := range partHashs { + parts[i] = s3.Part{ + PartNumber: i + 1, + ETag: part, + } + } + // todo: 验证大小 + result, err := c.impl.CompleteMultipartUpload(ctx, upload.ID, upload.Key, parts) + if err != nil { + return nil, err + } + targetKey = result.Key + case UploadTypePresigned: + uploadInfo, err := c.impl.StatObject(ctx, upload.Key) + if err != nil { + return nil, err + } + cleanObject[uploadInfo.Key] = struct{}{} + if uploadInfo.Size != upload.Size { + return nil, errors.New("upload size mismatching") + } + md5Sum := md5.Sum([]byte(strings.Join([]string{uploadInfo.ETag}, partSeparator))) + if md5val := hex.EncodeToString(md5Sum[:]); md5val != upload.Hash { + return nil, errs.ErrArgs.Wrap(fmt.Sprintf("md5 mismatching %s != %s", md5val, upload.Hash)) + } + // 防止在这个时候,并发操作,导致文件被覆盖 + copyInfo, err := c.impl.CopyObject(ctx, uploadInfo.Key, upload.Key+"."+c.UUID()) + if err != nil { + return nil, err + } + cleanObject[copyInfo.Key] = struct{}{} + if copyInfo.ETag != uploadInfo.ETag { + return nil, errors.New("[concurrency]copy md5 mismatching") + } + hashCopyInfo, err := c.impl.CopyObject(ctx, copyInfo.Key, c.HashPath(upload.Hash)) + if err != nil { + return nil, err + } + log.ZInfo(ctx, "hashCopyInfo", "value", fmt.Sprintf("%+v", hashCopyInfo)) + targetKey = hashCopyInfo.Key + default: + return nil, errors.New("invalid upload id type") + } + return &UploadResult{ + Key: targetKey, + Size: upload.Size, + Hash: upload.Hash, + }, nil +} + +func (c *Controller) AuthSign(ctx context.Context, uploadID string, partNumbers []int) (*s3.AuthSignResult, error) { + upload, err := parseMultipartUploadID(uploadID) + if err != nil { + return nil, err + } + switch upload.Type { + case UploadTypeMultipart: + return c.impl.AuthSign(ctx, upload.ID, upload.Key, time.Hour*24, partNumbers) + case UploadTypePresigned: + return nil, errors.New("presigned id not support auth sign") + default: + return nil, errors.New("invalid upload id type") + } +} + +func (c *Controller) IsNotFound(err error) bool { + return c.impl.IsNotFound(err) +} + +func (c *Controller) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) { + return c.impl.AccessURL(ctx, name, expire, opt) +} diff --git a/pkg/common/db/s3/cont/error.go b/pkg/common/db/s3/cont/error.go new file mode 100644 index 000000000..afd1d0eba --- /dev/null +++ b/pkg/common/db/s3/cont/error.go @@ -0,0 +1,14 @@ +package cont + +import ( + "fmt" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3" +) + +type HashAlreadyExistsError struct { + Object *s3.ObjectInfo +} + +func (e *HashAlreadyExistsError) Error() string { + return fmt.Sprintf("hash already exists: %s", e.Object.Key) +} diff --git a/pkg/common/db/s3/cont/id.go b/pkg/common/db/s3/cont/id.go new file mode 100644 index 000000000..67acfae37 --- /dev/null +++ b/pkg/common/db/s3/cont/id.go @@ -0,0 +1,35 @@ +package cont + +import ( + "encoding/base64" + "encoding/json" + "fmt" +) + +type multipartUploadID struct { + Type int `json:"a,omitempty"` + ID string `json:"b,omitempty"` + Key string `json:"c,omitempty"` + Size int64 `json:"d,omitempty"` + Hash string `json:"e,omitempty"` +} + +func newMultipartUploadID(id multipartUploadID) string { + data, err := json.Marshal(id) + if err != nil { + panic(err) + } + return base64.StdEncoding.EncodeToString(data) +} + +func parseMultipartUploadID(id string) (*multipartUploadID, error) { + data, err := base64.StdEncoding.DecodeString(id) + if err != nil { + return nil, fmt.Errorf("invalid multipart upload id: %w", err) + } + var upload multipartUploadID + if err := json.Unmarshal(data, &upload); err != nil { + return nil, fmt.Errorf("invalid multipart upload id: %w", err) + } + return &upload, nil +} diff --git a/pkg/common/db/s3/cont/structs.go b/pkg/common/db/s3/cont/structs.go new file mode 100644 index 000000000..160dfba70 --- /dev/null +++ b/pkg/common/db/s3/cont/structs.go @@ -0,0 +1,15 @@ +package cont + +import "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3" + +type InitiateUploadResult struct { + UploadID string `json:"uploadID"` // 上传ID + PartSize int64 `json:"partSize"` // 分片大小 + Sign *s3.AuthSignResult `json:"sign"` // 分片信息 +} + +type UploadResult struct { + Hash string `json:"hash"` + Size int64 `json:"size"` + Key string `json:"key"` +} diff --git a/pkg/common/db/s3/cos/cos.go b/pkg/common/db/s3/cos/cos.go new file mode 100644 index 000000000..4b1b4aa4d --- /dev/null +++ b/pkg/common/db/s3/cos/cos.go @@ -0,0 +1,254 @@ +package cos + +import ( + "context" + "errors" + "fmt" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3" + "github.com/tencentyun/cos-go-sdk-v5" + "net/http" + "net/url" + "strconv" + "strings" + "time" +) + +const ( + minPartSize = 1024 * 1024 * 1 // 1MB + maxPartSize = 1024 * 1024 * 1024 * 5 // 5GB + maxNumSize = 1000 +) + +func NewCos() (s3.Interface, error) { + conf := config.Config.Object.Cos + u, err := url.Parse(conf.BucketURL) + if err != nil { + panic(err) + } + client := cos.NewClient(&cos.BaseURL{BucketURL: u}, &http.Client{ + Transport: &cos.AuthorizationTransport{ + SecretID: conf.SecretID, + SecretKey: conf.SecretKey, + SessionToken: conf.SessionToken, + }, + }) + return &Cos{ + copyURL: u.Host + "/", + client: client, + credential: client.GetCredential(), + }, nil +} + +type Cos struct { + copyURL string + client *cos.Client + credential *cos.Credential +} + +func (c *Cos) Engine() string { + return "tencent-cos" +} + +func (c *Cos) PartLimit() *s3.PartLimit { + return &s3.PartLimit{ + MinPartSize: minPartSize, + MaxPartSize: maxPartSize, + MaxNumSize: maxNumSize, + } +} + +func (c *Cos) InitiateMultipartUpload(ctx context.Context, name string) (*s3.InitiateMultipartUploadResult, error) { + result, _, err := c.client.Object.InitiateMultipartUpload(ctx, name, nil) + if err != nil { + return nil, err + } + return &s3.InitiateMultipartUploadResult{ + UploadID: result.UploadID, + Bucket: result.Bucket, + Key: result.Key, + }, nil +} + +func (c *Cos) CompleteMultipartUpload(ctx context.Context, uploadID string, name string, parts []s3.Part) (*s3.CompleteMultipartUploadResult, error) { + opts := &cos.CompleteMultipartUploadOptions{ + Parts: make([]cos.Object, len(parts)), + } + for i, part := range parts { + opts.Parts[i] = cos.Object{ + PartNumber: part.PartNumber, + ETag: strings.ReplaceAll(part.ETag, `"`, ``), + } + } + result, _, err := c.client.Object.CompleteMultipartUpload(ctx, name, uploadID, opts) + if err != nil { + return nil, err + } + return &s3.CompleteMultipartUploadResult{ + Location: result.Location, + Bucket: result.Bucket, + Key: result.Key, + ETag: result.ETag, + }, nil +} + +func (c *Cos) PartSize(ctx context.Context, size int64) (int64, error) { + if size <= 0 { + return 0, errors.New("size must be greater than 0") + } + if size > maxPartSize*maxNumSize { + return 0, fmt.Errorf("size must be less than %db", maxPartSize*maxNumSize) + } + if size <= minPartSize*maxNumSize { + return minPartSize, nil + } + partSize := size / maxNumSize + if size%maxNumSize != 0 { + partSize++ + } + return partSize, nil +} + +func (c *Cos) AuthSign(ctx context.Context, uploadID string, name string, expire time.Duration, partNumbers []int) (*s3.AuthSignResult, error) { + result := s3.AuthSignResult{ + URL: c.client.BaseURL.BucketURL.String() + "/" + cos.EncodeURIComponent(name), + Query: url.Values{"uploadId": {uploadID}}, + Header: make(http.Header), + Parts: make([]s3.SignPart, len(partNumbers)), + } + req, err := http.NewRequestWithContext(ctx, http.MethodPut, result.URL, nil) + if err != nil { + return nil, err + } + cos.AddAuthorizationHeader(c.credential.SecretID, c.credential.SecretKey, c.credential.SessionToken, req, cos.NewAuthTime(expire)) + result.Header = req.Header + for i, partNumber := range partNumbers { + result.Parts[i] = s3.SignPart{ + PartNumber: partNumber, + Query: url.Values{"partNumber": {strconv.Itoa(partNumber)}}, + } + } + return &result, nil +} + +func (c *Cos) PresignedPutObject(ctx context.Context, name string, expire time.Duration) (string, error) { + rawURL, err := c.client.Object.GetPresignedURL(ctx, http.MethodPut, name, c.credential.SecretID, c.credential.SecretKey, expire, nil) + if err != nil { + return "", err + } + return rawURL.String(), nil +} + +func (c *Cos) DeleteObject(ctx context.Context, name string) error { + _, err := c.client.Object.Delete(ctx, name) + return err +} + +func (c *Cos) StatObject(ctx context.Context, name string) (*s3.ObjectInfo, error) { + if name != "" && name[0] == '/' { + name = name[1:] + } + info, err := c.client.Object.Head(ctx, name, nil) + if err != nil { + return nil, err + } + res := &s3.ObjectInfo{Key: name} + if res.ETag = strings.ToLower(strings.ReplaceAll(info.Header.Get("ETag"), `"`, "")); res.ETag == "" { + return nil, errors.New("StatObject etag not found") + } + if contentLengthStr := info.Header.Get("Content-Length"); contentLengthStr == "" { + return nil, errors.New("StatObject content-length not found") + } else { + res.Size, err = strconv.ParseInt(contentLengthStr, 10, 64) + if err != nil { + return nil, fmt.Errorf("StatObject content-length parse error: %w", err) + } + if res.Size < 0 { + return nil, errors.New("StatObject content-length must be greater than 0") + } + } + if lastModified := info.Header.Get("Last-Modified"); lastModified == "" { + return nil, errors.New("StatObject last-modified not found") + } else { + res.LastModified, err = time.Parse(http.TimeFormat, lastModified) + if err != nil { + return nil, fmt.Errorf("StatObject last-modified parse error: %w", err) + } + } + return res, nil +} + +func (c *Cos) CopyObject(ctx context.Context, src string, dst string) (*s3.CopyObjectInfo, error) { + sourceURL := c.copyURL + src + result, _, err := c.client.Object.Copy(ctx, dst, sourceURL, nil) + if err != nil { + return nil, err + } + return &s3.CopyObjectInfo{ + Key: dst, + ETag: strings.ReplaceAll(result.ETag, `"`, ``), + }, nil +} + +func (c *Cos) IsNotFound(err error) bool { + switch e := err.(type) { + case *cos.ErrorResponse: + return e.Response.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey" + default: + return false + } +} + +func (c *Cos) AbortMultipartUpload(ctx context.Context, uploadID string, name string) error { + _, err := c.client.Object.AbortMultipartUpload(ctx, name, uploadID) + return err +} + +func (c *Cos) ListUploadedParts(ctx context.Context, uploadID string, name string, partNumberMarker int, maxParts int) (*s3.ListUploadedPartsResult, error) { + result, _, err := c.client.Object.ListParts(ctx, name, uploadID, &cos.ObjectListPartsOptions{ + MaxParts: strconv.Itoa(maxParts), + PartNumberMarker: strconv.Itoa(partNumberMarker), + }) + if err != nil { + return nil, err + } + res := &s3.ListUploadedPartsResult{ + Key: result.Key, + UploadID: result.UploadID, + UploadedParts: make([]s3.UploadedPart, len(result.Parts)), + } + res.MaxParts, _ = strconv.Atoi(result.MaxParts) + res.NextPartNumberMarker, _ = strconv.Atoi(result.NextPartNumberMarker) + for i, part := range result.Parts { + lastModified, _ := time.Parse(http.TimeFormat, part.LastModified) + res.UploadedParts[i] = s3.UploadedPart{ + PartNumber: part.PartNumber, + LastModified: lastModified, + ETag: part.ETag, + Size: part.Size, + } + } + return res, nil +} + +func (c *Cos) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) { + //reqParams := make(url.Values) + //if opt != nil { + // if opt.ContentType != "" { + // reqParams.Set("Content-Type", opt.ContentType) + // } + // if opt.ContentDisposition != "" { + // reqParams.Set("Content-Disposition", opt.ContentDisposition) + // } + //} + if expire <= 0 { + expire = time.Hour * 24 * 365 * 99 // 99 years + } else if expire < time.Second { + expire = time.Second + } + rawURL, err := c.client.Object.GetPresignedURL(ctx, http.MethodGet, name, c.credential.SecretID, c.credential.SecretKey, expire, nil) + if err != nil { + return "", err + } + return rawURL.String(), nil +} diff --git a/pkg/common/db/s3/minio/minio.go b/pkg/common/db/s3/minio/minio.go new file mode 100644 index 000000000..367cbe8a8 --- /dev/null +++ b/pkg/common/db/s3/minio/minio.go @@ -0,0 +1,250 @@ +package minio + +import ( + "context" + "errors" + "fmt" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3" + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" + "github.com/minio/minio-go/v7/pkg/signer" + "net/http" + "net/url" + "strconv" + "strings" + "time" +) + +const ( + unsignedPayload = "UNSIGNED-PAYLOAD" +) + +const ( + minPartSize = 1024 * 1024 * 5 // 1MB + maxPartSize = 1024 * 1024 * 1024 * 5 // 5GB + maxNumSize = 10000 +) + +func NewMinio() (s3.Interface, error) { + conf := config.Config.Object.Minio + u, err := url.Parse(conf.Endpoint) + if err != nil { + return nil, err + } + opts := &minio.Options{ + Creds: credentials.NewStaticV4(conf.AccessKeyID, conf.SecretAccessKey, conf.SessionToken), + Secure: u.Scheme == "https", + } + client, err := minio.New(u.Host, opts) + if err != nil { + return nil, err + } + return &Minio{ + bucket: conf.Bucket, + bucketURL: conf.Endpoint + "/" + conf.Bucket + "/", + opts: opts, + core: &minio.Core{Client: client}, + }, nil +} + +type Minio struct { + bucket string + bucketURL string + opts *minio.Options + core *minio.Core +} + +func (m *Minio) Engine() string { + return "minio" +} + +func (m *Minio) PartLimit() *s3.PartLimit { + return &s3.PartLimit{ + MinPartSize: minPartSize, + MaxPartSize: maxPartSize, + MaxNumSize: maxNumSize, + } +} + +func (m *Minio) InitiateMultipartUpload(ctx context.Context, name string) (*s3.InitiateMultipartUploadResult, error) { + uploadID, err := m.core.NewMultipartUpload(ctx, m.bucket, name, minio.PutObjectOptions{}) + if err != nil { + return nil, err + } + return &s3.InitiateMultipartUploadResult{ + Bucket: m.bucket, + Key: name, + UploadID: uploadID, + }, nil +} + +func (m *Minio) CompleteMultipartUpload(ctx context.Context, uploadID string, name string, parts []s3.Part) (*s3.CompleteMultipartUploadResult, error) { + minioParts := make([]minio.CompletePart, len(parts)) + for i, part := range parts { + minioParts[i] = minio.CompletePart{ + PartNumber: part.PartNumber, + ETag: strings.ToLower(part.ETag), + } + } + upload, err := m.core.CompleteMultipartUpload(ctx, m.bucket, name, uploadID, minioParts, minio.PutObjectOptions{}) + if err != nil { + return nil, err + } + return &s3.CompleteMultipartUploadResult{ + Location: upload.Location, + Bucket: upload.Bucket, + Key: upload.Key, + ETag: strings.ToLower(upload.ETag), + }, nil +} + +func (m *Minio) PartSize(ctx context.Context, size int64) (int64, error) { + if size <= 0 { + return 0, errors.New("size must be greater than 0") + } + if size > maxPartSize*maxNumSize { + return 0, fmt.Errorf("size must be less than %db", maxPartSize*maxNumSize) + } + if size <= minPartSize*maxNumSize { + return minPartSize, nil + } + partSize := size / maxNumSize + if size%maxNumSize != 0 { + partSize++ + } + return partSize, nil +} + +func (m *Minio) AuthSign(ctx context.Context, uploadID string, name string, expire time.Duration, partNumbers []int) (*s3.AuthSignResult, error) { + creds, err := m.opts.Creds.Get() + if err != nil { + return nil, err + } + result := s3.AuthSignResult{ + URL: m.bucketURL + name, + Query: url.Values{"uploadId": {uploadID}}, + Parts: make([]s3.SignPart, len(partNumbers)), + } + for i, partNumber := range partNumbers { + rawURL := result.URL + "?partNumber=" + strconv.Itoa(partNumber) + "&uploadId=" + uploadID + request, err := http.NewRequestWithContext(ctx, http.MethodPut, rawURL, nil) + if err != nil { + return nil, err + } + request.Header.Set("X-Amz-Content-Sha256", unsignedPayload) + request = signer.SignV4Trailer(*request, creds.AccessKeyID, creds.SecretAccessKey, creds.SessionToken, "us-east-1", nil) + result.Parts[i] = s3.SignPart{ + PartNumber: partNumber, + URL: request.URL.String(), + Query: url.Values{"partNumber": {strconv.Itoa(partNumber)}}, + Header: request.Header, + } + } + return &result, nil +} + +func (m *Minio) PresignedPutObject(ctx context.Context, name string, expire time.Duration) (string, error) { + rawURL, err := m.core.Client.PresignedPutObject(ctx, m.bucket, name, expire) + if err != nil { + return "", err + } + return rawURL.String(), nil +} + +func (m *Minio) DeleteObject(ctx context.Context, name string) error { + return m.core.Client.RemoveObject(ctx, m.bucket, name, minio.RemoveObjectOptions{}) +} + +func (m *Minio) StatObject(ctx context.Context, name string) (*s3.ObjectInfo, error) { + info, err := m.core.Client.StatObject(ctx, m.bucket, name, minio.StatObjectOptions{}) + if err != nil { + return nil, err + } + return &s3.ObjectInfo{ + ETag: strings.ToLower(info.ETag), + Key: info.Key, + Size: info.Size, + LastModified: info.LastModified, + }, nil +} + +func (m *Minio) CopyObject(ctx context.Context, src string, dst string) (*s3.CopyObjectInfo, error) { + result, err := m.core.Client.CopyObject(ctx, minio.CopyDestOptions{ + Bucket: m.bucket, + Object: dst, + }, minio.CopySrcOptions{ + Bucket: m.bucket, + Object: src, + }) + if err != nil { + return nil, err + } + return &s3.CopyObjectInfo{ + Key: dst, + ETag: strings.ToLower(result.ETag), + }, nil +} + +func (m *Minio) IsNotFound(err error) bool { + if err == nil { + return false + } + switch e := err.(type) { + case minio.ErrorResponse: + return e.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey" + case *minio.ErrorResponse: + return e.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey" + default: + return false + } +} + +func (m *Minio) AbortMultipartUpload(ctx context.Context, uploadID string, name string) error { + return m.core.AbortMultipartUpload(ctx, m.bucket, name, uploadID) +} + +func (m *Minio) ListUploadedParts(ctx context.Context, uploadID string, name string, partNumberMarker int, maxParts int) (*s3.ListUploadedPartsResult, error) { + result, err := m.core.ListObjectParts(ctx, m.bucket, name, uploadID, partNumberMarker, maxParts) + if err != nil { + return nil, err + } + res := &s3.ListUploadedPartsResult{ + Key: result.Key, + UploadID: result.UploadID, + MaxParts: result.MaxParts, + NextPartNumberMarker: result.NextPartNumberMarker, + UploadedParts: make([]s3.UploadedPart, len(result.ObjectParts)), + } + for i, part := range result.ObjectParts { + res.UploadedParts[i] = s3.UploadedPart{ + PartNumber: part.PartNumber, + LastModified: part.LastModified, + ETag: part.ETag, + Size: part.Size, + } + } + return res, nil +} + +func (m *Minio) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) { + //reqParams := make(url.Values) + //if opt != nil { + // if opt.ContentType != "" { + // reqParams.Set("Content-Type", opt.ContentType) + // } + // if opt.ContentDisposition != "" { + // reqParams.Set("Content-Disposition", opt.ContentDisposition) + // } + //} + if expire <= 0 { + expire = time.Hour * 24 * 365 * 99 // 99 years + } else if expire < time.Second { + expire = time.Second + } + u, err := m.core.Client.PresignedGetObject(ctx, m.bucket, name, expire, nil) + if err != nil { + return "", err + } + return u.String(), nil +} diff --git a/pkg/common/db/s3/oss/oss.go b/pkg/common/db/s3/oss/oss.go new file mode 100644 index 000000000..f2b50da50 --- /dev/null +++ b/pkg/common/db/s3/oss/oss.go @@ -0,0 +1,259 @@ +package oss + +import ( + "context" + "errors" + "fmt" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3" + "github.com/aliyun/aliyun-oss-go-sdk/oss" + "net/http" + "net/url" + "strconv" + "strings" + "time" +) + +const ( + minPartSize = 1024 * 1024 * 1 // 1MB + maxPartSize = 1024 * 1024 * 1024 * 5 // 5GB + maxNumSize = 10000 +) + +func NewOSS() (s3.Interface, error) { + conf := config.Config.Object.Oss + if conf.BucketURL == "" { + return nil, errors.New("bucket url is empty") + } + client, err := oss.New(conf.Endpoint, conf.AccessKeyID, conf.AccessKeySecret) + if err != nil { + return nil, err + } + bucket, err := client.Bucket(conf.Bucket) + if err != nil { + return nil, err + } + if conf.BucketURL[len(conf.BucketURL)-1] != '/' { + conf.BucketURL += "/" + } + return &OSS{ + bucketURL: conf.BucketURL, + bucket: bucket, + credentials: client.Config.GetCredentials(), + }, nil +} + +type OSS struct { + bucketURL string + bucket *oss.Bucket + credentials oss.Credentials +} + +func (o *OSS) Engine() string { + return "ali-oss" +} + +func (o *OSS) PartLimit() *s3.PartLimit { + return &s3.PartLimit{ + MinPartSize: minPartSize, + MaxPartSize: maxPartSize, + MaxNumSize: maxNumSize, + } +} + +func (o *OSS) InitiateMultipartUpload(ctx context.Context, name string) (*s3.InitiateMultipartUploadResult, error) { + result, err := o.bucket.InitiateMultipartUpload(name) + if err != nil { + return nil, err + } + return &s3.InitiateMultipartUploadResult{ + UploadID: result.UploadID, + Bucket: result.Bucket, + Key: result.Key, + }, nil +} + +func (o *OSS) CompleteMultipartUpload(ctx context.Context, uploadID string, name string, parts []s3.Part) (*s3.CompleteMultipartUploadResult, error) { + ossParts := make([]oss.UploadPart, len(parts)) + for i, part := range parts { + ossParts[i] = oss.UploadPart{ + PartNumber: part.PartNumber, + ETag: strings.ToUpper(part.ETag), + } + } + result, err := o.bucket.CompleteMultipartUpload(oss.InitiateMultipartUploadResult{ + UploadID: uploadID, + Bucket: o.bucket.BucketName, + Key: name, + }, ossParts) + if err != nil { + return nil, err + } + return &s3.CompleteMultipartUploadResult{ + Location: result.Location, + Bucket: result.Bucket, + Key: result.Key, + ETag: strings.ToLower(strings.ReplaceAll(result.ETag, `"`, ``)), + }, nil +} + +func (o *OSS) PartSize(ctx context.Context, size int64) (int64, error) { + if size <= 0 { + return 0, errors.New("size must be greater than 0") + } + if size > maxPartSize*maxNumSize { + return 0, fmt.Errorf("size must be less than %db", maxPartSize*maxNumSize) + } + if size <= minPartSize*maxNumSize { + return minPartSize, nil + } + partSize := size / maxNumSize + if size%maxNumSize != 0 { + partSize++ + } + return partSize, nil +} + +func (o *OSS) AuthSign(ctx context.Context, uploadID string, name string, expire time.Duration, partNumbers []int) (*s3.AuthSignResult, error) { + result := s3.AuthSignResult{ + URL: o.bucketURL + name, + Query: url.Values{"uploadId": {uploadID}}, + Header: make(http.Header), + Parts: make([]s3.SignPart, len(partNumbers)), + } + for i, partNumber := range partNumbers { + rawURL := fmt.Sprintf(`%s%s?partNumber=%d&uploadId=%s`, o.bucketURL, name, partNumber, uploadID) + request, err := http.NewRequestWithContext(ctx, http.MethodPut, rawURL, nil) + if err != nil { + return nil, err + } + if o.credentials.GetSecurityToken() != "" { + request.Header.Set(oss.HTTPHeaderOssSecurityToken, o.credentials.GetSecurityToken()) + } + request.Header.Set(oss.HTTPHeaderHost, request.Host) + request.Header.Set(oss.HTTPHeaderDate, time.Now().UTC().Format(http.TimeFormat)) + authorization := fmt.Sprintf(`OSS %s:%s`, o.credentials.GetAccessKeyID(), o.getSignedStr(request, fmt.Sprintf(`/%s/%s?partNumber=%d&uploadId=%s`, o.bucket.BucketName, name, partNumber, uploadID), o.credentials.GetAccessKeySecret())) + request.Header.Set(oss.HTTPHeaderAuthorization, authorization) + result.Parts[i] = s3.SignPart{ + PartNumber: partNumber, + Query: url.Values{"partNumber": {strconv.Itoa(partNumber)}}, + URL: request.URL.String(), + Header: request.Header, + } + } + return &result, nil +} + +func (o *OSS) PresignedPutObject(ctx context.Context, name string, expire time.Duration) (string, error) { + return o.bucket.SignURL(name, http.MethodPut, int64(expire/time.Second)) +} + +func (o *OSS) StatObject(ctx context.Context, name string) (*s3.ObjectInfo, error) { + header, err := o.bucket.GetObjectMeta(name) + if err != nil { + return nil, err + } + res := &s3.ObjectInfo{Key: name} + if res.ETag = strings.ToLower(strings.ReplaceAll(header.Get("ETag"), `"`, ``)); res.ETag == "" { + return nil, errors.New("StatObject etag not found") + } + if contentLengthStr := header.Get("Content-Length"); contentLengthStr == "" { + return nil, errors.New("StatObject content-length not found") + } else { + res.Size, err = strconv.ParseInt(contentLengthStr, 10, 64) + if err != nil { + return nil, fmt.Errorf("StatObject content-length parse error: %w", err) + } + if res.Size < 0 { + return nil, errors.New("StatObject content-length must be greater than 0") + } + } + if lastModified := header.Get("Last-Modified"); lastModified == "" { + return nil, errors.New("StatObject last-modified not found") + } else { + res.LastModified, err = time.Parse(http.TimeFormat, lastModified) + if err != nil { + return nil, fmt.Errorf("StatObject last-modified parse error: %w", err) + } + } + return res, nil +} + +func (o *OSS) DeleteObject(ctx context.Context, name string) error { + return o.bucket.DeleteObject(name) +} + +func (o *OSS) CopyObject(ctx context.Context, src string, dst string) (*s3.CopyObjectInfo, error) { + result, err := o.bucket.CopyObject(src, dst) + if err != nil { + return nil, err + } + return &s3.CopyObjectInfo{ + Key: dst, + ETag: strings.ToLower(strings.ReplaceAll(result.ETag, `"`, ``)), + }, nil +} + +func (o *OSS) IsNotFound(err error) bool { + switch e := err.(type) { + case oss.ServiceError: + return e.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey" + case *oss.ServiceError: + return e.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey" + default: + return false + } +} + +func (o *OSS) AbortMultipartUpload(ctx context.Context, uploadID string, name string) error { + return o.bucket.AbortMultipartUpload(oss.InitiateMultipartUploadResult{ + UploadID: uploadID, + Key: name, + Bucket: o.bucket.BucketName, + }) +} + +func (o *OSS) ListUploadedParts(ctx context.Context, uploadID string, name string, partNumberMarker int, maxParts int) (*s3.ListUploadedPartsResult, error) { + result, err := o.bucket.ListUploadedParts(oss.InitiateMultipartUploadResult{ + UploadID: uploadID, + Key: name, + Bucket: o.bucket.BucketName, + }, oss.MaxUploads(100), oss.MaxParts(maxParts), oss.PartNumberMarker(partNumberMarker)) + if err != nil { + return nil, err + } + res := &s3.ListUploadedPartsResult{ + Key: result.Key, + UploadID: result.UploadID, + MaxParts: result.MaxParts, + UploadedParts: make([]s3.UploadedPart, len(result.UploadedParts)), + } + res.NextPartNumberMarker, _ = strconv.Atoi(result.NextPartNumberMarker) + for i, part := range result.UploadedParts { + res.UploadedParts[i] = s3.UploadedPart{ + PartNumber: part.PartNumber, + LastModified: part.LastModified, + ETag: part.ETag, + Size: int64(part.Size), + } + } + return res, nil +} + +func (o *OSS) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) { + //var opts []oss.Option + //if opt != nil { + // if opt.ContentType != "" { + // opts = append(opts, oss.ContentType(opt.ContentType)) + // } + // if opt.ContentDisposition != "" { + // opts = append(opts, oss.ContentDisposition(opt.ContentDisposition)) + // } + //} + if expire <= 0 { + expire = time.Hour * 24 * 365 * 99 // 99 years + } else if expire < time.Second { + expire = time.Second + } + return o.bucket.SignURL(name, http.MethodGet, int64(expire/time.Second)) +} diff --git a/pkg/common/db/s3/oss/sign.go b/pkg/common/db/s3/oss/sign.go new file mode 100644 index 000000000..82618d287 --- /dev/null +++ b/pkg/common/db/s3/oss/sign.go @@ -0,0 +1,81 @@ +package oss + +import ( + "crypto/hmac" + "crypto/sha1" + "crypto/sha256" + "encoding/base64" + "github.com/aliyun/aliyun-oss-go-sdk/oss" + "hash" + "io" + "net/http" + "sort" + "strings" +) + +func (o *OSS) getAdditionalHeaderKeys(req *http.Request) ([]string, map[string]string) { + var keysList []string + keysMap := make(map[string]string) + srcKeys := make(map[string]string) + + for k := range req.Header { + srcKeys[strings.ToLower(k)] = "" + } + + for _, v := range o.bucket.Client.Config.AdditionalHeaders { + if _, ok := srcKeys[strings.ToLower(v)]; ok { + keysMap[strings.ToLower(v)] = "" + } + } + + for k := range keysMap { + keysList = append(keysList, k) + } + sort.Strings(keysList) + return keysList, keysMap +} + +func (o *OSS) getSignedStr(req *http.Request, canonicalizedResource string, keySecret string) string { + // Find out the "x-oss-"'s address in header of the request + ossHeadersMap := make(map[string]string) + additionalList, additionalMap := o.getAdditionalHeaderKeys(req) + for k, v := range req.Header { + if strings.HasPrefix(strings.ToLower(k), "x-oss-") { + ossHeadersMap[strings.ToLower(k)] = v[0] + } else if o.bucket.Client.Config.AuthVersion == oss.AuthV2 { + if _, ok := additionalMap[strings.ToLower(k)]; ok { + ossHeadersMap[strings.ToLower(k)] = v[0] + } + } + } + hs := newHeaderSorter(ossHeadersMap) + + // Sort the ossHeadersMap by the ascending order + hs.Sort() + + // Get the canonicalizedOSSHeaders + canonicalizedOSSHeaders := "" + for i := range hs.Keys { + canonicalizedOSSHeaders += hs.Keys[i] + ":" + hs.Vals[i] + "\n" + } + + // Give other parameters values + // when sign URL, date is expires + date := req.Header.Get(oss.HTTPHeaderDate) + contentType := req.Header.Get(oss.HTTPHeaderContentType) + contentMd5 := req.Header.Get(oss.HTTPHeaderContentMD5) + + // default is v1 signature + signStr := req.Method + "\n" + contentMd5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedOSSHeaders + canonicalizedResource + h := hmac.New(func() hash.Hash { return sha1.New() }, []byte(keySecret)) + + // v2 signature + if o.bucket.Client.Config.AuthVersion == oss.AuthV2 { + signStr = req.Method + "\n" + contentMd5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedOSSHeaders + strings.Join(additionalList, ";") + "\n" + canonicalizedResource + h = hmac.New(func() hash.Hash { return sha256.New() }, []byte(keySecret)) + } + _, _ = io.WriteString(h, signStr) + signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil)) + + return signedStr +} diff --git a/pkg/common/db/s3/oss/sort.go b/pkg/common/db/s3/oss/sort.go new file mode 100644 index 000000000..40c9a5af1 --- /dev/null +++ b/pkg/common/db/s3/oss/sort.go @@ -0,0 +1,47 @@ +package oss + +import ( + "bytes" + "sort" +) + +// headerSorter defines the key-value structure for storing the sorted data in signHeader. +type headerSorter struct { + Keys []string + Vals []string +} + +// newHeaderSorter is an additional function for function SignHeader. +func newHeaderSorter(m map[string]string) *headerSorter { + hs := &headerSorter{ + Keys: make([]string, 0, len(m)), + Vals: make([]string, 0, len(m)), + } + + for k, v := range m { + hs.Keys = append(hs.Keys, k) + hs.Vals = append(hs.Vals, v) + } + return hs +} + +// Sort is an additional function for function SignHeader. +func (hs *headerSorter) Sort() { + sort.Sort(hs) +} + +// Len is an additional function for function SignHeader. +func (hs *headerSorter) Len() int { + return len(hs.Vals) +} + +// Less is an additional function for function SignHeader. +func (hs *headerSorter) Less(i, j int) bool { + return bytes.Compare([]byte(hs.Keys[i]), []byte(hs.Keys[j])) < 0 +} + +// Swap is an additional function for function SignHeader. +func (hs *headerSorter) Swap(i, j int) { + hs.Vals[i], hs.Vals[j] = hs.Vals[j], hs.Vals[i] + hs.Keys[i], hs.Keys[j] = hs.Keys[j], hs.Keys[i] +} diff --git a/pkg/common/db/s3/s3.go b/pkg/common/db/s3/s3.go new file mode 100644 index 000000000..4f1571b1e --- /dev/null +++ b/pkg/common/db/s3/s3.go @@ -0,0 +1,134 @@ +package s3 + +import ( + "context" + "net/http" + "net/url" + "time" +) + +type PartLimit struct { + MinPartSize int64 `json:"minPartSize"` + MaxPartSize int64 `json:"maxPartSize"` + MaxNumSize int `json:"maxNumSize"` +} + +type InitiateMultipartUploadResult struct { + Bucket string `json:"bucket"` + Key string `json:"key"` + UploadID string `json:"uploadID"` +} + +type MultipartUploadRequest struct { + UploadID string `json:"uploadId"` + Bucket string `json:"bucket"` + Key string `json:"key"` + Method string `json:"method"` + URL string `json:"url"` + Query url.Values `json:"query"` + Header http.Header `json:"header"` + PartKey string `json:"partKey"` + PartSize int64 `json:"partSize"` + FirstPart int `json:"firstPart"` +} + +type Part struct { + PartNumber int `json:"partNumber"` + ETag string `json:"etag"` +} + +type CompleteMultipartUploadResult struct { + Location string `json:"location"` + Bucket string `json:"bucket"` + Key string `json:"key"` + ETag string `json:"etag"` +} + +type SignResult struct { + Parts []SignPart `json:"parts"` +} + +type ObjectInfo struct { + ETag string `json:"etag"` + Key string `json:"name"` + Size int64 `json:"size"` + LastModified time.Time `json:"lastModified"` +} + +type CopyObjectInfo struct { + Key string `json:"name"` + ETag string `json:"etag"` +} + +type SignPart struct { + PartNumber int `json:"partNumber"` + URL string `json:"url"` + Query url.Values `json:"query"` + Header http.Header `json:"header"` +} + +type AuthSignResult struct { + URL string `json:"url"` + Query url.Values `json:"query"` + Header http.Header `json:"header"` + Parts []SignPart `json:"parts"` +} + +type InitiateUpload struct { + UploadID string `json:"uploadId"` + Bucket string `json:"bucket"` + Key string `json:"key"` + Method string `json:"method"` + URL string `json:"url"` + Query url.Values `json:"query"` + Header http.Header `json:"header"` + PartKey string `json:"partKey"` + PartSize int64 `json:"partSize"` + FirstPart int `json:"firstPart"` +} + +type UploadedPart struct { + PartNumber int `json:"partNumber"` + LastModified time.Time `json:"lastModified"` + ETag string `json:"etag"` + Size int64 `json:"size"` +} + +type ListUploadedPartsResult struct { + Key string `xml:"Key"` + UploadID string `xml:"UploadId"` + NextPartNumberMarker int `xml:"NextPartNumberMarker"` + MaxParts int `xml:"MaxParts"` + UploadedParts []UploadedPart `xml:"Part"` +} + +type AccessURLOption struct { + ContentType string `json:"contentType"` + ContentDisposition string `json:"contentDisposition"` +} + +type Interface interface { + Engine() string + PartLimit() *PartLimit + + InitiateMultipartUpload(ctx context.Context, name string) (*InitiateMultipartUploadResult, error) + CompleteMultipartUpload(ctx context.Context, uploadID string, name string, parts []Part) (*CompleteMultipartUploadResult, error) + + PartSize(ctx context.Context, size int64) (int64, error) + AuthSign(ctx context.Context, uploadID string, name string, expire time.Duration, partNumbers []int) (*AuthSignResult, error) + + PresignedPutObject(ctx context.Context, name string, expire time.Duration) (string, error) + + DeleteObject(ctx context.Context, name string) error + + CopyObject(ctx context.Context, src string, dst string) (*CopyObjectInfo, error) + + StatObject(ctx context.Context, name string) (*ObjectInfo, error) + + IsNotFound(err error) bool + + AbortMultipartUpload(ctx context.Context, uploadID string, name string) error + ListUploadedParts(ctx context.Context, uploadID string, name string, partNumberMarker int, maxParts int) (*ListUploadedPartsResult, error) + + AccessURL(ctx context.Context, name string, expire time.Duration, opt *AccessURLOption) (string, error) +} diff --git a/pkg/common/db/table/relation/conversation.go b/pkg/common/db/table/relation/conversation.go index 5d6d061b1..6fd260583 100644 --- a/pkg/common/db/table/relation/conversation.go +++ b/pkg/common/db/table/relation/conversation.go @@ -1,40 +1,33 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package relation -import "context" +import ( + "context" + "time" +) const ( conversationModelTableName = "conversations" ) type ConversationModel struct { - OwnerUserID string `gorm:"column:owner_user_id;primary_key;type:char(128)" json:"OwnerUserID"` - ConversationID string `gorm:"column:conversation_id;primary_key;type:char(128)" json:"conversationID"` - ConversationType int32 `gorm:"column:conversation_type" json:"conversationType"` - UserID string `gorm:"column:user_id;type:char(64)" json:"userID"` - GroupID string `gorm:"column:group_id;type:char(128)" json:"groupID"` - RecvMsgOpt int32 `gorm:"column:recv_msg_opt" json:"recvMsgOpt"` - IsPinned bool `gorm:"column:is_pinned" json:"isPinned"` - IsPrivateChat bool `gorm:"column:is_private_chat" json:"isPrivateChat"` - BurnDuration int32 `gorm:"column:burn_duration;default:30" json:"burnDuration"` - GroupAtType int32 `gorm:"column:group_at_type" json:"groupAtType"` - AttachedInfo string `gorm:"column:attached_info;type:varchar(1024)" json:"attachedInfo"` - Ex string `gorm:"column:ex;type:varchar(1024)" json:"ex"` - MaxSeq int64 `gorm:"column:max_seq" json:"maxSeq"` - MinSeq int64 `gorm:"column:min_seq" json:"minSeq"` + OwnerUserID string `gorm:"column:owner_user_id;primary_key;type:char(128)" json:"OwnerUserID"` + ConversationID string `gorm:"column:conversation_id;primary_key;type:char(128)" json:"conversationID"` + ConversationType int32 `gorm:"column:conversation_type" json:"conversationType"` + UserID string `gorm:"column:user_id;type:char(64)" json:"userID"` + GroupID string `gorm:"column:group_id;type:char(128)" json:"groupID"` + RecvMsgOpt int32 `gorm:"column:recv_msg_opt" json:"recvMsgOpt"` + IsPinned bool `gorm:"column:is_pinned" json:"isPinned"` + IsPrivateChat bool `gorm:"column:is_private_chat" json:"isPrivateChat"` + BurnDuration int32 `gorm:"column:burn_duration;default:30" json:"burnDuration"` + GroupAtType int32 `gorm:"column:group_at_type" json:"groupAtType"` + AttachedInfo string `gorm:"column:attached_info;type:varchar(1024)" json:"attachedInfo"` + Ex string `gorm:"column:ex;type:varchar(1024)" json:"ex"` + MaxSeq int64 `gorm:"column:max_seq" json:"maxSeq"` + MinSeq int64 `gorm:"column:min_seq" json:"minSeq"` + CreateTime time.Time `gorm:"column:create_time;index:create_time;autoCreateTime"` + IsMsgDestruct bool `gorm:"column:is_msg_destruct;default:false"` + MsgDestructTime int64 `gorm:"column:msg_destruct_time;default:604800"` + LatestMsgDestructTime time.Time `gorm:"column:latest_msg_destruct_time;autoCreateTime"` } func (ConversationModel) TableName() string { @@ -44,26 +37,13 @@ func (ConversationModel) TableName() string { type ConversationModelInterface interface { Create(ctx context.Context, conversations []*ConversationModel) (err error) Delete(ctx context.Context, groupIDs []string) (err error) - UpdateByMap( - ctx context.Context, - userIDs []string, - conversationID string, - args map[string]interface{}, - ) (rows int64, err error) + UpdateByMap(ctx context.Context, userIDs []string, conversationID string, args map[string]interface{}) (rows int64, err error) Update(ctx context.Context, conversation *ConversationModel) (err error) - Find( - ctx context.Context, - ownerUserID string, - conversationIDs []string, - ) (conversations []*ConversationModel, err error) + Find(ctx context.Context, ownerUserID string, conversationIDs []string) (conversations []*ConversationModel, err error) FindUserID(ctx context.Context, userIDs []string, conversationIDs []string) ([]string, error) FindUserIDAllConversationID(ctx context.Context, userID string) ([]string, error) Take(ctx context.Context, userID, conversationID string) (conversation *ConversationModel, err error) - FindConversationID( - ctx context.Context, - userID string, - conversationIDs []string, - ) (existConversationID []string, err error) + FindConversationID(ctx context.Context, userID string, conversationIDs []string) (existConversationID []string, err error) FindUserIDAllConversations(ctx context.Context, userID string) (conversations []*ConversationModel, err error) FindRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) ([]string, error) GetUserRecvMsgOpt(ctx context.Context, ownerUserID, conversationID string) (opt int, err error) @@ -71,5 +51,6 @@ type ConversationModelInterface interface { GetAllConversationIDs(ctx context.Context) ([]string, error) GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (hashReadSeqs map[string]int64, err error) GetConversationsByConversationID(ctx context.Context, conversationIDs []string) ([]*ConversationModel, error) + GetConversationIDsNeedDestruct(ctx context.Context) ([]*ConversationModel, error) NewTx(tx any) ConversationModelInterface } diff --git a/pkg/common/db/table/relation/group.go b/pkg/common/db/table/relation/group.go index be0afbcc3..2bafb53ec 100644 --- a/pkg/common/db/table/relation/group.go +++ b/pkg/common/db/table/relation/group.go @@ -58,4 +58,8 @@ type GroupModelInterface interface { pageNumber, showNumber int32, ) (total uint32, groups []*GroupModel, err error) GetGroupIDsByGroupType(ctx context.Context, groupType int) (groupIDs []string, err error) + // 获取群总数 + CountTotal(ctx context.Context, before *time.Time) (count int64, err error) + // 获取范围内群增量 + CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) } diff --git a/pkg/common/db/table/relation/object.go b/pkg/common/db/table/relation/object.go new file mode 100644 index 000000000..eb58c308d --- /dev/null +++ b/pkg/common/db/table/relation/object.go @@ -0,0 +1,31 @@ +package relation + +import ( + "context" + "time" +) + +const ( + ObjectInfoModelTableName = "object" +) + +type ObjectModel struct { + Name string `gorm:"column:name;primary_key"` + UserID string `gorm:"column:user_id"` + Hash string `gorm:"column:hash"` + Key string `gorm:"column:key"` + Size int64 `gorm:"column:size"` + ContentType string `gorm:"column:content_type"` + Cause string `gorm:"column:cause"` + CreateTime time.Time `gorm:"column:create_time"` +} + +func (ObjectModel) TableName() string { + return ObjectInfoModelTableName +} + +type ObjectInfoModelInterface interface { + NewTx(tx any) ObjectInfoModelInterface + SetObject(ctx context.Context, obj *ObjectModel) error + Take(ctx context.Context, name string) (*ObjectModel, error) +} diff --git a/pkg/common/db/table/relation/object_hash.go b/pkg/common/db/table/relation/object_hash.go deleted file mode 100644 index 231f21a81..000000000 --- a/pkg/common/db/table/relation/object_hash.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package relation - -import ( - "context" - "time" -) - -const ( - ObjectHashModelTableName = "object_hash" -) - -type ObjectHashModel struct { - Hash string `gorm:"column:hash;primary_key;size:32"` - Engine string `gorm:"column:engine;primary_key;size:16"` - Size int64 `gorm:"column:size"` - Bucket string `gorm:"column:bucket"` - Name string `gorm:"column:name"` - CreateTime time.Time `gorm:"column:create_time"` -} - -func (ObjectHashModel) TableName() string { - return ObjectHashModelTableName -} - -type ObjectHashModelInterface interface { - NewTx(tx any) ObjectHashModelInterface - Take(ctx context.Context, hash string, engine string) (*ObjectHashModel, error) - Create(ctx context.Context, h []*ObjectHashModel) error - DeleteNoCitation(ctx context.Context, engine string, num int) (list []*ObjectHashModel, err error) -} diff --git a/pkg/common/db/table/relation/object_info.go b/pkg/common/db/table/relation/object_info.go deleted file mode 100644 index e7b7fb57c..000000000 --- a/pkg/common/db/table/relation/object_info.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package relation - -import ( - "context" - "time" -) - -const ( - ObjectInfoModelTableName = "object_info" -) - -type ObjectInfoModel struct { - Name string `gorm:"column:name;primary_key"` - Hash string `gorm:"column:hash"` - ContentType string `gorm:"column:content_type"` - ValidTime *time.Time `gorm:"column:valid_time"` - CreateTime time.Time `gorm:"column:create_time"` -} - -func (ObjectInfoModel) TableName() string { - return ObjectInfoModelTableName -} - -type ObjectInfoModelInterface interface { - NewTx(tx any) ObjectInfoModelInterface - SetObject(ctx context.Context, obj *ObjectInfoModel) error - Take(ctx context.Context, name string) (*ObjectInfoModel, error) - DeleteExpiration(ctx context.Context, expiration time.Time) error -} diff --git a/pkg/common/db/table/relation/object_put.go b/pkg/common/db/table/relation/object_put.go deleted file mode 100644 index 49b37826c..000000000 --- a/pkg/common/db/table/relation/object_put.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package relation - -import ( - "context" - "time" -) - -const ( - ObjectPutModelTableName = "object_put" -) - -type ObjectPutModel struct { - PutID string `gorm:"column:put_id;primary_key"` - Hash string `gorm:"column:hash"` - Path string `gorm:"column:path"` - Name string `gorm:"column:name"` - ContentType string `gorm:"column:content_type"` - ObjectSize int64 `gorm:"column:object_size"` - FragmentSize int64 `gorm:"column:fragment_size"` - PutURLsHash string `gorm:"column:put_urls_hash"` - ValidTime *time.Time `gorm:"column:valid_time"` - EffectiveTime time.Time `gorm:"column:effective_time"` - CreateTime time.Time `gorm:"column:create_time"` -} - -func (ObjectPutModel) TableName() string { - return ObjectPutModelTableName -} - -type ObjectPutModelInterface interface { - NewTx(tx any) ObjectPutModelInterface - Create(ctx context.Context, m []*ObjectPutModel) error - Take(ctx context.Context, putID string) (*ObjectPutModel, error) - SetCompleted(ctx context.Context, putID string) error - FindExpirationPut(ctx context.Context, expirationTime time.Time, num int) ([]*ObjectPutModel, error) - DelPut(ctx context.Context, ids []string) error -} diff --git a/pkg/common/db/table/relation/user.go b/pkg/common/db/table/relation/user.go index 7d2bf0cf5..f44a610ad 100644 --- a/pkg/common/db/table/relation/user.go +++ b/pkg/common/db/table/relation/user.go @@ -28,7 +28,7 @@ type UserModel struct { Nickname string `gorm:"column:name;size:255"` FaceURL string `gorm:"column:face_url;size:255"` Ex string `gorm:"column:ex;size:1024"` - CreateTime time.Time `gorm:"column:create_time;index:create_time; autoCreateTime"` + CreateTime time.Time `gorm:"column:create_time;index:create_time;autoCreateTime"` AppMangerLevel int32 `gorm:"column:app_manger_level;default:18"` GlobalRecvMsgOpt int32 `gorm:"column:global_recv_msg_opt"` } @@ -66,7 +66,7 @@ type UserModelInterface interface { GetAllUserID(ctx context.Context) (userIDs []string, err error) GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) (opt int, err error) // 获取用户总数 - CountTotal(ctx context.Context) (count int64, err error) + CountTotal(ctx context.Context, before *time.Time) (count int64, err error) // 获取范围内用户增量 CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) } diff --git a/pkg/common/db/table/unrelation/extend_msg_set.go b/pkg/common/db/table/unrelation/extend_msg_set.go deleted file mode 100644 index 472daf5fa..000000000 --- a/pkg/common/db/table/unrelation/extend_msg_set.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package unrelation - -import ( - "context" - "strconv" - "strings" - - "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" -) - -const ( - CExtendMsgSet = "extend_msgs" - - ExtendMsgMaxNum = 100 -) - -type ExtendMsgSetModel struct { - ConversationID string `bson:"source_id" json:"conversationID"` - SessionType int32 `bson:"session_type" json:"sessionType"` - ExtendMsgs map[string]ExtendMsgModel `bson:"extend_msgs" json:"extendMsgs"` - ExtendMsgNum int32 `bson:"extend_msg_num" json:"extendMsgNum"` - CreateTime int64 `bson:"create_time" json:"createTime"` // this block's create time - MaxMsgUpdateTime int64 `bson:"max_msg_update_time" json:"maxMsgUpdateTime"` // index find msg -} - -type KeyValueModel struct { - TypeKey string `bson:"type_key" json:"typeKey"` - Value string `bson:"value" json:"value"` - LatestUpdateTime int64 `bson:"latest_update_time" json:"latestUpdateTime"` -} - -type ExtendMsgModel struct { - ReactionExtensionList map[string]KeyValueModel `bson:"reaction_extension_list" json:"reactionExtensionList"` - ClientMsgID string `bson:"client_msg_id" json:"clientMsgID"` - MsgFirstModifyTime int64 `bson:"msg_first_modify_time" json:"msgFirstModifyTime"` // this extendMsg create time - AttachedInfo string `bson:"attached_info" json:"attachedInfo"` - Ex string `bson:"ex" json:"ex"` -} - -type ExtendMsgSetModelInterface interface { - CreateExtendMsgSet(ctx context.Context, set *ExtendMsgSetModel) error - GetAllExtendMsgSet( - ctx context.Context, - conversationID string, - opts *GetAllExtendMsgSetOpts, - ) (sets []*ExtendMsgSetModel, err error) - GetExtendMsgSet( - ctx context.Context, - conversationID string, - sessionType int32, - maxMsgUpdateTime int64, - ) (*ExtendMsgSetModel, error) - InsertExtendMsg(ctx context.Context, conversationID string, sessionType int32, msg *ExtendMsgModel) error - InsertOrUpdateReactionExtendMsgSet( - ctx context.Context, - conversationID string, - sessionType int32, - clientMsgID string, - msgFirstModifyTime int64, - reactionExtensionList map[string]*KeyValueModel, - ) error - DeleteReactionExtendMsgSet( - ctx context.Context, - conversationID string, - sessionType int32, - clientMsgID string, - msgFirstModifyTime int64, - reactionExtensionList map[string]*KeyValueModel, - ) error - TakeExtendMsg( - ctx context.Context, - conversationID string, - sessionType int32, - clientMsgID string, - maxMsgUpdateTime int64, - ) (extendMsg *ExtendMsgModel, err error) -} - -func (ExtendMsgSetModel) TableName() string { - return CExtendMsgSet -} - -func (ExtendMsgSetModel) GetExtendMsgMaxNum() int32 { - return ExtendMsgMaxNum -} - -func (ExtendMsgSetModel) GetConversationID(ID string, index int32) string { - return ID + ":" + strconv.Itoa(int(index)) -} - -func (e *ExtendMsgSetModel) SplitConversationIDAndGetIndex() int32 { - l := strings.Split(e.ConversationID, ":") - index, _ := strconv.Atoi(l[len(l)-1]) - return int32(index) -} - -type GetAllExtendMsgSetOpts struct { - ExcludeExtendMsgs bool -} - -func (ExtendMsgSetModel) Pb2Model(reactionExtensionList map[string]*sdkws.KeyValue) map[string]*KeyValueModel { - r := make(map[string]*KeyValueModel) - for key, value := range reactionExtensionList { - r[key] = &KeyValueModel{ - TypeKey: value.TypeKey, - Value: value.Value, - LatestUpdateTime: value.LatestUpdateTime, - } - } - return r -} diff --git a/pkg/common/db/table/unrelation/msg.go b/pkg/common/db/table/unrelation/msg.go index 57f9b4cca..e34bd374b 100644 --- a/pkg/common/db/table/unrelation/msg.go +++ b/pkg/common/db/table/unrelation/msg.go @@ -17,6 +17,7 @@ package unrelation import ( "context" "strconv" + "time" "go.mongodb.org/mongo-driver/mongo" @@ -36,7 +37,6 @@ type MsgDocModel struct { } type RevokeModel struct { - ID string `bson:"id"` Role int32 `bson:"role"` UserID string `bson:"user_id"` Nickname string `bson:"nickname"` @@ -83,6 +83,16 @@ type MsgInfoModel struct { IsRead bool `bson:"is_read"` } +type UserCount struct { + UserID string `bson:"user_id"` + Count int64 `bson:"count"` +} + +type GroupCount struct { + GroupID string `bson:"group_id"` + Count int64 `bson:"count"` +} + type MsgDocModelInterface interface { PushMsgsToDoc(ctx context.Context, docID string, msgsToMongo []MsgInfoModel) error Create(ctx context.Context, model *MsgDocModel) error @@ -98,6 +108,8 @@ type MsgDocModelInterface interface { GetMsgDocModelByIndex(ctx context.Context, conversationID string, index, sort int64) (*MsgDocModel, error) DeleteMsgsInOneDocByIndex(ctx context.Context, docID string, indexes []int) error MarkSingleChatMsgsAsRead(ctx context.Context, userID string, docID string, indexes []int64) error + RangeUserSendCount(ctx context.Context, start time.Time, end time.Time, group bool, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, users []*UserCount, dateCount map[string]int64, err error) + RangeGroupSendCount(ctx context.Context, start time.Time, end time.Time, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, groups []*GroupCount, dateCount map[string]int64, err error) } func (MsgDocModel) TableName() string { diff --git a/pkg/common/db/unrelation/extend_msg.go b/pkg/common/db/unrelation/extend_msg.go deleted file mode 100644 index 6f09247bc..000000000 --- a/pkg/common/db/unrelation/extend_msg.go +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package unrelation - -import ( - "context" - "errors" - "fmt" - - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/primitive" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" - - unRelationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation" - "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" -) - -type ExtendMsgSetMongoDriver struct { - mgoDB *mongo.Database - ExtendMsgSetCollection *mongo.Collection -} - -func NewExtendMsgSetMongoDriver(mgoDB *mongo.Database) unRelationTb.ExtendMsgSetModelInterface { - return &ExtendMsgSetMongoDriver{mgoDB: mgoDB, ExtendMsgSetCollection: mgoDB.Collection(unRelationTb.CExtendMsgSet)} -} - -func (e *ExtendMsgSetMongoDriver) CreateExtendMsgSet(ctx context.Context, set *unRelationTb.ExtendMsgSetModel) error { - _, err := e.ExtendMsgSetCollection.InsertOne(ctx, set) - return err -} - -func (e *ExtendMsgSetMongoDriver) GetAllExtendMsgSet( - ctx context.Context, - ID string, - opts *unRelationTb.GetAllExtendMsgSetOpts, -) (sets []*unRelationTb.ExtendMsgSetModel, err error) { - regex := fmt.Sprintf("^%s", ID) - var findOpts *options.FindOptions - if opts != nil { - if opts.ExcludeExtendMsgs { - findOpts = &options.FindOptions{} - findOpts.SetProjection(bson.M{"extend_msgs": 0}) - } - } - cursor, err := e.ExtendMsgSetCollection.Find(ctx, bson.M{"doc_id": primitive.Regex{Pattern: regex}}, findOpts) - if err != nil { - return nil, utils.Wrap(err, "") - } - err = cursor.All(ctx, &sets) - if err != nil { - return nil, utils.Wrap(err, fmt.Sprintf("cursor is %s", cursor.Current.String())) - } - return sets, nil -} - -func (e *ExtendMsgSetMongoDriver) GetExtendMsgSet( - ctx context.Context, - conversationID string, - sessionType int32, - maxMsgUpdateTime int64, -) (*unRelationTb.ExtendMsgSetModel, error) { - var err error - findOpts := options.Find(). - SetLimit(1). - SetSkip(0). - SetSort(bson.M{"source_id": -1}). - SetProjection(bson.M{"extend_msgs": 0}) - // update newest - find := bson.M{ - "source_id": primitive.Regex{Pattern: fmt.Sprintf("^%s", conversationID)}, - "session_type": sessionType, - } - if maxMsgUpdateTime > 0 { - find["max_msg_update_time"] = maxMsgUpdateTime - } - result, err := e.ExtendMsgSetCollection.Find(ctx, find, findOpts) - if err != nil { - return nil, utils.Wrap(err, "") - } - var setList []unRelationTb.ExtendMsgSetModel - if err := result.All(ctx, &setList); err != nil { - return nil, utils.Wrap(err, "") - } - if len(setList) == 0 { - return nil, nil - } - return &setList[0], nil -} - -// first modify msg -func (e *ExtendMsgSetMongoDriver) InsertExtendMsg( - ctx context.Context, - conversationID string, - sessionType int32, - msg *unRelationTb.ExtendMsgModel, -) error { - set, err := e.GetExtendMsgSet(ctx, conversationID, sessionType, 0) - if err != nil { - return utils.Wrap(err, "") - } - if set == nil || set.ExtendMsgNum >= set.GetExtendMsgMaxNum() { - var index int32 - if set != nil { - index = set.SplitConversationIDAndGetIndex() - } - err = e.CreateExtendMsgSet(ctx, &unRelationTb.ExtendMsgSetModel{ - ConversationID: set.GetConversationID(conversationID, index), - SessionType: sessionType, - ExtendMsgs: map[string]unRelationTb.ExtendMsgModel{msg.ClientMsgID: *msg}, - ExtendMsgNum: 1, - CreateTime: msg.MsgFirstModifyTime, - MaxMsgUpdateTime: msg.MsgFirstModifyTime, - }) - } else { - _, err = e.ExtendMsgSetCollection.UpdateOne(ctx, bson.M{"conversation_id": set.ConversationID, "session_type": sessionType}, bson.M{"$set": bson.M{"max_msg_update_time": msg.MsgFirstModifyTime, "$inc": bson.M{"extend_msg_num": 1}, fmt.Sprintf("extend_msgs.%s", msg.ClientMsgID): msg}}) - } - return utils.Wrap(err, "") -} - -// insert or update -func (e *ExtendMsgSetMongoDriver) InsertOrUpdateReactionExtendMsgSet( - ctx context.Context, - conversationID string, - sessionType int32, - clientMsgID string, - msgFirstModifyTime int64, - reactionExtensionList map[string]*unRelationTb.KeyValueModel, -) error { - var updateBson = bson.M{} - for _, v := range reactionExtensionList { - updateBson[fmt.Sprintf("extend_msgs.%s.%s", clientMsgID, v.TypeKey)] = v - } - upsert := true - opt := &options.UpdateOptions{ - Upsert: &upsert, - } - set, err := e.GetExtendMsgSet(ctx, conversationID, sessionType, msgFirstModifyTime) - if err != nil { - return utils.Wrap(err, "") - } - if set == nil { - return fmt.Errorf("conversationID %s has no set", conversationID) - } - _, err = e.ExtendMsgSetCollection.UpdateOne( - ctx, - bson.M{"source_id": set.ConversationID, "session_type": sessionType}, - bson.M{"$set": updateBson}, - opt, - ) - return utils.Wrap(err, "") -} - -// delete TypeKey -func (e *ExtendMsgSetMongoDriver) DeleteReactionExtendMsgSet( - ctx context.Context, - conversationID string, - sessionType int32, - clientMsgID string, - msgFirstModifyTime int64, - reactionExtensionList map[string]*unRelationTb.KeyValueModel, -) error { - var updateBson = bson.M{} - for _, v := range reactionExtensionList { - updateBson[fmt.Sprintf("extend_msgs.%s.%s", clientMsgID, v.TypeKey)] = "" - } - set, err := e.GetExtendMsgSet(ctx, conversationID, sessionType, msgFirstModifyTime) - if err != nil { - return utils.Wrap(err, "") - } - if set == nil { - return fmt.Errorf("conversationID %s has no set", conversationID) - } - _, err = e.ExtendMsgSetCollection.UpdateOne( - ctx, - bson.M{"source_id": set.ConversationID, "session_type": sessionType}, - bson.M{"$unset": updateBson}, - ) - return err -} - -func (e *ExtendMsgSetMongoDriver) TakeExtendMsg( - ctx context.Context, - conversationID string, - sessionType int32, - clientMsgID string, - maxMsgUpdateTime int64, -) (extendMsg *unRelationTb.ExtendMsgModel, err error) { - findOpts := options.Find(). - SetLimit(1). - SetSkip(0). - SetSort(bson.M{"source_id": -1}). - SetProjection(bson.M{fmt.Sprintf("extend_msgs.%s", clientMsgID): 1}) - regex := fmt.Sprintf("^%s", conversationID) - result, err := e.ExtendMsgSetCollection.Find( - ctx, - bson.M{ - "source_id": primitive.Regex{Pattern: regex}, - "session_type": sessionType, - "max_msg_update_time": bson.M{"$lte": maxMsgUpdateTime}, - }, - findOpts, - ) - if err != nil { - return nil, utils.Wrap(err, "") - } - var setList []unRelationTb.ExtendMsgSetModel - if err := result.All(ctx, &setList); err != nil { - return nil, utils.Wrap(err, "") - } - if len(setList) == 0 { - return nil, utils.Wrap(errors.New("GetExtendMsg failed, len(setList) == 0"), "") - } - if v, ok := setList[0].ExtendMsgs[clientMsgID]; ok { - return &v, nil - } - return nil, fmt.Errorf("cant find client msg id: %s", clientMsgID) -} diff --git a/pkg/common/db/unrelation/mongo.go b/pkg/common/db/unrelation/mongo.go index 51b9e4b7e..fd13bb2aa 100644 --- a/pkg/common/db/unrelation/mongo.go +++ b/pkg/common/db/unrelation/mongo.go @@ -94,10 +94,6 @@ func (m *Mongo) CreateSuperGroupIndex() error { return nil } -func (m *Mongo) CreateExtendMsgSetIndex() error { - return m.createMongoIndex(unrelation.CExtendMsgSet, true, "-create_time", "work_moment_id") -} - func (m *Mongo) createMongoIndex(collection string, isUnique bool, keys ...string) error { db := m.db.Database(config.Config.Mongo.Database).Collection(collection) opts := options.CreateIndexes().SetMaxTime(10 * time.Second) diff --git a/pkg/common/db/unrelation/msg.go b/pkg/common/db/unrelation/msg.go index cecbf2301..6c6d76216 100644 --- a/pkg/common/db/unrelation/msg.go +++ b/pkg/common/db/unrelation/msg.go @@ -19,6 +19,7 @@ import ( "encoding/json" "errors" "fmt" + "time" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" @@ -302,7 +303,7 @@ func (m *MsgMongoDriver) GetMsgBySeqIndexIn1Doc( } if msg.Revoke != nil { revokeContent := sdkws.MessageRevokedContent{ - RevokerID: msg.Revoke.ID, + RevokerID: msg.Revoke.UserID, RevokerRole: msg.Revoke.Role, ClientMsgID: msg.Msg.ClientMsgID, RevokerNickname: msg.Revoke.Nickname, @@ -368,3 +369,683 @@ func (m *MsgMongoDriver) MarkSingleChatMsgsAsRead( _, err := m.MsgCollection.BulkWrite(ctx, updates) return err } + +// RangeUserSendCount +// db.msg.aggregate([ +// +// { +// $match: { +// "msgs.msg.send_time": { +// "$gte": 0, +// "$lt": 1788122092317 +// } +// } +// }, +// { +// "$addFields": { +// "msgs": { +// "$filter": { +// "input": "$msgs", +// "as": "item", +// "cond": { +// "$and": [ +// { +// $gte: ["$$item.msg.send_time", 0] +// }, +// { +// $lt: ["$$item.msg.send_time", 1788122092317] +// } +// ] +// } +// } +// } +// } +// }, +// { +// "$project": { +// "_id": 0, +// +// }, +// +// }, +// { +// "$project": { +// "result": { +// "$map": { +// "input": "$msgs", +// "as": "item", +// "in": { +// user_id: "$$item.msg.send_id", +// send_date: { +// $dateToString: { +// format: "%Y-%m-%d", +// date: { +// $toDate: "$$item.msg.send_time" +// } +// } +// } +// } +// } +// } +// }, +// +// }, +// { +// "$unwind": "$result" +// }, +// { +// "$group": { +// _id: "$result.send_date", +// count: { +// $sum: 1 +// }, +// original: { +// $push: "$$ROOT" +// } +// } +// }, +// { +// "$addFields": { +// "dates": "$$ROOT" +// } +// }, +// { +// "$project": { +// "_id": 0, +// "count": 0, +// "dates.original": 0, +// +// }, +// +// }, +// { +// "$group": { +// _id: null, +// count: { +// $sum: 1 +// }, +// dates: { +// $push: "$dates" +// }, +// original: { +// $push: "$original" +// }, +// +// } +// }, +// { +// "$unwind": "$original" +// }, +// { +// "$unwind": "$original" +// }, +// { +// "$group": { +// _id: "$original.result.user_id", +// count: { +// $sum: 1 +// }, +// original: { +// $push: "$dates" +// }, +// +// } +// }, +// { +// "$addFields": { +// "dates": { +// $arrayElemAt: ["$original", 0] +// } +// } +// }, +// { +// "$project": { +// original: 0 +// } +// }, +// { +// $sort: { +// count: - 1 +// } +// }, +// { +// "$group": { +// _id: null, +// user_count: { +// $sum: 1 +// }, +// users: { +// $push: "$$ROOT" +// }, +// +// } +// }, +// { +// "$addFields": { +// "dates": { +// $arrayElemAt: ["$users", 0] +// } +// } +// }, +// { +// "$addFields": { +// "dates": "$dates.dates" +// } +// }, +// { +// "$project": { +// _id: 0, +// "users.dates": 0, +// +// } +// }, +// { +// "$addFields": { +// "msg_count": { +// $sum: "$users.count" +// } +// } +// }, +// { +// "$addFields": { +// users: { +// $slice: ["$users", 0, 10] +// } +// } +// } +// +// ]) +func (m *MsgMongoDriver) RangeUserSendCount(ctx context.Context, start time.Time, end time.Time, group bool, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, users []*table.UserCount, dateCount map[string]int64, err error) { + var sort int + if ase { + sort = 1 + } else { + sort = -1 + } + type Result struct { + MsgCount int64 `bson:"msg_count"` + UserCount int64 `bson:"user_count"` + Users []struct { + UserID string `bson:"_id"` + Count int64 `bson:"count"` + } `bson:"users"` + Dates []struct { + Date string `bson:"_id"` + Count int64 `bson:"count"` + } `bson:"dates"` + } + or := bson.A{ + bson.M{ + "doc_id": bson.M{ + "$regex": "^si_", + "$options": "i", + }, + }, + } + if group { + or = append(or, + bson.M{ + "doc_id": bson.M{ + "$regex": "^g_", + "$options": "i", + }, + }, + bson.M{ + "doc_id": bson.M{ + "$regex": "^sg_", + "$options": "i", + }, + }, + ) + } + pipeline := bson.A{ + bson.M{ + "$match": bson.M{ + "$and": bson.A{ + bson.M{ + "msgs.msg.send_time": bson.M{ + "$gte": start.UnixMilli(), + "$lt": end.UnixMilli(), + }, + }, + bson.M{ + "$or": or, + }, + }, + }, + }, + bson.M{ + "$addFields": bson.M{ + "msgs": bson.M{ + "$filter": bson.M{ + "input": "$msgs", + "as": "item", + "cond": bson.M{ + "$and": bson.A{ + bson.M{ + "$gte": bson.A{ + "$$item.msg.send_time", start.UnixMilli(), + }, + }, + bson.M{ + "$lt": bson.A{ + "$$item.msg.send_time", end.UnixMilli(), + }, + }, + }, + }, + }, + }, + }, + }, + bson.M{ + "$project": bson.M{ + "_id": 0, + }, + }, + bson.M{ + "$project": bson.M{ + "result": bson.M{ + "$map": bson.M{ + "input": "$msgs", + "as": "item", + "in": bson.M{ + "user_id": "$$item.msg.send_id", + "send_date": bson.M{ + "$dateToString": bson.M{ + "format": "%Y-%m-%d", + "date": bson.M{ + "$toDate": "$$item.msg.send_time", // 毫秒时间戳 + }, + }, + }, + }, + }, + }, + }, + }, + bson.M{ + "$unwind": "$result", + }, + bson.M{ + "$group": bson.M{ + "_id": "$result.send_date", + "count": bson.M{ + "$sum": 1, + }, + "original": bson.M{ + "$push": "$$ROOT", + }, + }, + }, + bson.M{ + "$addFields": bson.M{ + "dates": "$$ROOT", + }, + }, + bson.M{ + "$project": bson.M{ + "_id": 0, + "count": 0, + "dates.original": 0, + }, + }, + bson.M{ + "$group": bson.M{ + "_id": nil, + "count": bson.M{ + "$sum": 1, + }, + "dates": bson.M{ + "$push": "$dates", + }, + "original": bson.M{ + "$push": "$original", + }, + }, + }, + bson.M{ + "$unwind": "$original", + }, + bson.M{ + "$unwind": "$original", + }, + bson.M{ + "$group": bson.M{ + "_id": "$original.result.user_id", + "count": bson.M{ + "$sum": 1, + }, + "original": bson.M{ + "$push": "$dates", + }, + }, + }, + bson.M{ + "$addFields": bson.M{ + "dates": bson.M{ + "$arrayElemAt": bson.A{"$original", 0}, + }, + }, + }, + bson.M{ + "$project": bson.M{ + "original": 0, + }, + }, + bson.M{ + "$sort": bson.M{ + "count": sort, + }, + }, + bson.M{ + "$group": bson.M{ + "_id": nil, + "user_count": bson.M{ + "$sum": 1, + }, + "users": bson.M{ + "$push": "$$ROOT", + }, + }, + }, + bson.M{ + "$addFields": bson.M{ + "dates": bson.M{ + "$arrayElemAt": bson.A{"$users", 0}, + }, + }, + }, + bson.M{ + "$addFields": bson.M{ + "dates": "$dates.dates", + }, + }, + bson.M{ + "$project": bson.M{ + "_id": 0, + "users.dates": 0, + }, + }, + bson.M{ + "$addFields": bson.M{ + "msg_count": bson.M{ + "$sum": "$users.count", + }, + }, + }, + bson.M{ + "$addFields": bson.M{ + "users": bson.M{ + "$slice": bson.A{"$users", pageNumber - 1, showNumber}, + }, + }, + }, + } + cur, err := m.MsgCollection.Aggregate(ctx, pipeline, options.Aggregate().SetAllowDiskUse(true)) + if err != nil { + return 0, 0, nil, nil, errs.Wrap(err) + } + defer cur.Close(ctx) + var result []Result + if err := cur.All(ctx, &result); err != nil { + return 0, 0, nil, nil, errs.Wrap(err) + } + if len(result) == 0 { + return 0, 0, nil, nil, errs.Wrap(err) + } + users = make([]*table.UserCount, len(result[0].Users)) + for i, r := range result[0].Users { + users[i] = &table.UserCount{ + UserID: r.UserID, + Count: r.Count, + } + } + dateCount = make(map[string]int64) + for _, r := range result[0].Dates { + dateCount[r.Date] = r.Count + } + return result[0].MsgCount, result[0].UserCount, users, dateCount, nil +} + +func (m *MsgMongoDriver) RangeGroupSendCount(ctx context.Context, start time.Time, end time.Time, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, groups []*table.GroupCount, dateCount map[string]int64, err error) { + var sort int + if ase { + sort = 1 + } else { + sort = -1 + } + type Result struct { + MsgCount int64 `bson:"msg_count"` + UserCount int64 `bson:"user_count"` + Groups []struct { + GroupID string `bson:"_id"` + Count int64 `bson:"count"` + } `bson:"groups"` + Dates []struct { + Date string `bson:"_id"` + Count int64 `bson:"count"` + } `bson:"dates"` + } + pipeline := bson.A{ + bson.M{ + "$match": bson.M{ + "$and": bson.A{ + bson.M{ + "msgs.msg.send_time": bson.M{ + "$gte": start.UnixMilli(), + "$lt": end.UnixMilli(), + }, + }, + bson.M{ + "$or": bson.A{ + bson.M{ + "doc_id": bson.M{ + "$regex": "^g_", + "$options": "i", + }, + }, + bson.M{ + "doc_id": bson.M{ + "$regex": "^sg_", + "$options": "i", + }, + }, + }, + }, + }, + }, + }, + bson.M{ + "$addFields": bson.M{ + "msgs": bson.M{ + "$filter": bson.M{ + "input": "$msgs", + "as": "item", + "cond": bson.M{ + "$and": bson.A{ + bson.M{ + "$gte": bson.A{ + "$$item.msg.send_time", start.UnixMilli(), + }, + }, + bson.M{ + "$lt": bson.A{ + "$$item.msg.send_time", end.UnixMilli(), + }, + }, + }, + }, + }, + }, + }, + }, + bson.M{ + "$project": bson.M{ + "_id": 0, + }, + }, + bson.M{ + "$project": bson.M{ + "result": bson.M{ + "$map": bson.M{ + "input": "$msgs", + "as": "item", + "in": bson.M{ + "group_id": "$$item.msg.group_id", + "send_date": bson.M{ + "$dateToString": bson.M{ + "format": "%Y-%m-%d", + "date": bson.M{ + "$toDate": "$$item.msg.send_time", // 毫秒时间戳 + }, + }, + }, + }, + }, + }, + }, + }, + bson.M{ + "$unwind": "$result", + }, + bson.M{ + "$group": bson.M{ + "_id": "$result.send_date", + "count": bson.M{ + "$sum": 1, + }, + "original": bson.M{ + "$push": "$$ROOT", + }, + }, + }, + bson.M{ + "$addFields": bson.M{ + "dates": "$$ROOT", + }, + }, + bson.M{ + "$project": bson.M{ + "_id": 0, + "count": 0, + "dates.original": 0, + }, + }, + bson.M{ + "$group": bson.M{ + "_id": nil, + "count": bson.M{ + "$sum": 1, + }, + "dates": bson.M{ + "$push": "$dates", + }, + "original": bson.M{ + "$push": "$original", + }, + }, + }, + bson.M{ + "$unwind": "$original", + }, + bson.M{ + "$unwind": "$original", + }, + bson.M{ + "$group": bson.M{ + "_id": "$original.result.group_id", + "count": bson.M{ + "$sum": 1, + }, + "original": bson.M{ + "$push": "$dates", + }, + }, + }, + bson.M{ + "$addFields": bson.M{ + "dates": bson.M{ + "$arrayElemAt": bson.A{"$original", 0}, + }, + }, + }, + bson.M{ + "$project": bson.M{ + "original": 0, + }, + }, + bson.M{ + "$sort": bson.M{ + "count": sort, + }, + }, + bson.M{ + "$group": bson.M{ + "_id": nil, + "user_count": bson.M{ + "$sum": 1, + }, + "groups": bson.M{ + "$push": "$$ROOT", + }, + }, + }, + bson.M{ + "$addFields": bson.M{ + "dates": bson.M{ + "$arrayElemAt": bson.A{"$groups", 0}, + }, + }, + }, + bson.M{ + "$addFields": bson.M{ + "dates": "$dates.dates", + }, + }, + bson.M{ + "$project": bson.M{ + "_id": 0, + "groups.dates": 0, + }, + }, + bson.M{ + "$addFields": bson.M{ + "msg_count": bson.M{ + "$sum": "$groups.count", + }, + }, + }, + bson.M{ + "$addFields": bson.M{ + "groups": bson.M{ + "$slice": bson.A{"$groups", pageNumber - 1, showNumber}, + }, + }, + }, + } + cur, err := m.MsgCollection.Aggregate(ctx, pipeline, options.Aggregate().SetAllowDiskUse(true)) + if err != nil { + return 0, 0, nil, nil, errs.Wrap(err) + } + defer cur.Close(ctx) + var result []Result + if err := cur.All(ctx, &result); err != nil { + return 0, 0, nil, nil, errs.Wrap(err) + } + if len(result) == 0 { + return 0, 0, nil, nil, errs.Wrap(err) + } + groups = make([]*table.GroupCount, len(result[0].Groups)) + for i, r := range result[0].Groups { + groups[i] = &table.GroupCount{ + GroupID: r.GroupID, + Count: r.Count, + } + } + dateCount = make(map[string]int64) + for _, r := range result[0].Dates { + dateCount[r.Date] = r.Count + } + return result[0].MsgCount, result[0].UserCount, groups, dateCount, nil +} diff --git a/pkg/common/log/sql_logger.go b/pkg/common/log/sql_logger.go index 40b483474..9e9bb1be6 100644 --- a/pkg/common/log/sql_logger.go +++ b/pkg/common/log/sql_logger.go @@ -1,17 +1,3 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package log import ( @@ -31,11 +17,7 @@ type SqlLogger struct { SlowThreshold time.Duration } -func NewSqlLogger( - logLevel gormLogger.LogLevel, - ignoreRecordNotFoundError bool, - slowThreshold time.Duration, -) *SqlLogger { +func NewSqlLogger(logLevel gormLogger.LogLevel, ignoreRecordNotFoundError bool, slowThreshold time.Duration) *SqlLogger { return &SqlLogger{ LogLevel: logLevel, IgnoreRecordNotFoundError: ignoreRecordNotFoundError, @@ -70,17 +52,7 @@ func (l *SqlLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql s case err != nil && l.LogLevel >= gormLogger.Error && (!errors.Is(err, gorm.ErrRecordNotFound) || !l.IgnoreRecordNotFoundError): sql, rows := fc() if rows == -1 { - ZError( - ctx, - "sql exec detail", - err, - "gorm", - gormUtils.FileWithLineNum(), - "elapsed time", - fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), - "sql", - sql, - ) + ZError(ctx, "sql exec detail", err, "gorm", gormUtils.FileWithLineNum(), "elapsed time", fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), "sql", sql) } else { ZError(ctx, "sql exec detail", err, "gorm", gormUtils.FileWithLineNum(), "elapsed time", fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), "rows", rows, "sql", sql) } @@ -88,36 +60,14 @@ func (l *SqlLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql s sql, rows := fc() slowLog := fmt.Sprintf("SLOW SQL >= %v", l.SlowThreshold) if rows == -1 { - ZWarn( - ctx, - "sql exec detail", - nil, - "gorm", - gormUtils.FileWithLineNum(), - nil, - "slow sql", - slowLog, - "elapsed time", - fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), - "sql", - sql, - ) + ZWarn(ctx, "sql exec detail", nil, "gorm", gormUtils.FileWithLineNum(), "slow sql", slowLog, "elapsed time", fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), "sql", sql) } else { - ZWarn(ctx, "sql exec detail", nil, "gorm", gormUtils.FileWithLineNum(), nil, "slow sql", slowLog, "elapsed time", fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), "rows", rows, "sql", sql) + ZWarn(ctx, "sql exec detail", nil, "gorm", gormUtils.FileWithLineNum(), "slow sql", slowLog, "elapsed time", fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), "rows", rows, "sql", sql) } case l.LogLevel == gormLogger.Info: sql, rows := fc() if rows == -1 { - ZDebug( - ctx, - "sql exec detail", - "gorm", - gormUtils.FileWithLineNum(), - "elapsed time", - fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), - "sql", - sql, - ) + ZDebug(ctx, "sql exec detail", "gorm", gormUtils.FileWithLineNum(), "elapsed time", fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), "sql", sql) } else { ZDebug(ctx, "sql exec detail", "gorm", gormUtils.FileWithLineNum(), "elapsed time", fmt.Sprintf("%f(ms)", float64(elapsed.Nanoseconds())/1e6), "rows", rows, "sql", sql) } diff --git a/pkg/common/mw/rpc_client_interceptor.go b/pkg/common/mw/rpc_client_interceptor.go index 40933810c..ebfcda99d 100644 --- a/pkg/common/mw/rpc_client_interceptor.go +++ b/pkg/common/mw/rpc_client_interceptor.go @@ -31,7 +31,7 @@ import ( ) func GrpcClient() grpc.DialOption { - return grpc.WithUnaryInterceptor(RpcClientInterceptor) + return grpc.WithChainUnaryInterceptor(RpcClientInterceptor) } func RpcClientInterceptor( diff --git a/pkg/common/mw/rpc_server_interceptor.go b/pkg/common/mw/rpc_server_interceptor.go index 01eb77b89..da99275fd 100644 --- a/pkg/common/mw/rpc_server_interceptor.go +++ b/pkg/common/mw/rpc_server_interceptor.go @@ -52,7 +52,6 @@ func RpcServerInterceptor( handler grpc.UnaryHandler, ) (resp interface{}, err error) { log.ZDebug(ctx, "rpc server req", "req", rpcString(req)) - //defer func() { // if r := recover(); r != nil { // log.ZError(ctx, "rpc panic", nil, "FullMethod", info.FullMethod, "type:", fmt.Sprintf("%T", r), "panic:", r) @@ -177,12 +176,13 @@ func RpcServerInterceptor( } details, err := grpcStatus.WithDetails(errInfo) if err != nil { - panic(err) + log.ZWarn(ctx, "rpc server resp WithDetails error", err, "funcName", funcName) + return nil, errs.Wrap(err) } log.ZWarn(ctx, "rpc server resp", err, "funcName", funcName) return nil, details.Err() } func GrpcServer() grpc.ServerOption { - return grpc.UnaryInterceptor(RpcServerInterceptor) + return grpc.ChainUnaryInterceptor(RpcServerInterceptor) } diff --git a/pkg/common/tokenverify/jwt_token.go b/pkg/common/tokenverify/jwt_token.go index 30e9ac9d1..68fee2602 100644 --- a/pkg/common/tokenverify/jwt_token.go +++ b/pkg/common/tokenverify/jwt_token.go @@ -84,7 +84,7 @@ func CheckAccessV3(ctx context.Context, ownerUserID string) (err error) { if opUserID == ownerUserID { return nil } - return errs.ErrIdentity.Wrap(utils.GetSelfFuncName()) + return errs.ErrNoPermission.Wrap(utils.GetSelfFuncName()) } func IsAppManagerUid(ctx context.Context) bool { @@ -95,7 +95,7 @@ func CheckAdmin(ctx context.Context) error { if utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID) { return nil } - return errs.ErrIdentity.Wrap(fmt.Sprintf("user %s is not admin userID", mcontext.GetOpUserID(ctx))) + return errs.ErrNoPermission.Wrap(fmt.Sprintf("user %s is not admin userID", mcontext.GetOpUserID(ctx))) } func ParseRedisInterfaceToken(redisToken interface{}) (*Claims, error) { diff --git a/pkg/discoveryregistry/discovery_register.go b/pkg/discoveryregistry/discovery_register.go index 50e720f97..4ebabe7a8 100644 --- a/pkg/discoveryregistry/discovery_register.go +++ b/pkg/discoveryregistry/discovery_register.go @@ -18,7 +18,6 @@ import ( "context" "google.golang.org/grpc" - "google.golang.org/grpc/resolver" ) type Conn interface { @@ -27,7 +26,7 @@ type Conn interface { AddOption(opts ...grpc.DialOption) CloseConn(conn grpc.ClientConnInterface) // do not use this method for call rpc - GetClientLocalConns() map[string][]resolver.Address + GetClientLocalConns() map[string][]grpc.ClientConnInterface } type SvcDiscoveryRegistry interface { diff --git a/pkg/discoveryregistry/zookeeper/discover.go b/pkg/discoveryregistry/zookeeper/discover.go index 3966d315c..a9848ee32 100644 --- a/pkg/discoveryregistry/zookeeper/discover.go +++ b/pkg/discoveryregistry/zookeeper/discover.go @@ -1,17 +1,3 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package zookeeper import ( @@ -20,9 +6,9 @@ import ( "io" "strings" - "github.com/pkg/errors" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" + "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" + "github.com/pkg/errors" "github.com/go-zookeeper/zk" "google.golang.org/grpc" @@ -76,64 +62,44 @@ func (s *ZkClient) GetConnsRemote(serviceName string) (conns []resolver.Address, } return nil, errors.Wrap(err, "get children error") } - log.ZDebug(context.Background(), "get conns from remote", "conn", string(data)) + log.ZDebug(context.Background(), "get addrs from remote", "conn", string(data)) conns = append(conns, resolver.Address{Addr: string(data), ServerName: serviceName}) } } return conns, nil } -func (s *ZkClient) GetConns( - ctx context.Context, - serviceName string, - opts ...grpc.DialOption, -) ([]grpc.ClientConnInterface, error) { +func (s *ZkClient) GetConns(ctx context.Context, serviceName string, opts ...grpc.DialOption) ([]grpc.ClientConnInterface, error) { s.logger.Printf("get conns from client, serviceName: %s", serviceName) - s.lock.Lock() opts = append(s.options, opts...) + s.lock.Lock() + defer s.lock.Unlock() conns := s.localConns[serviceName] if len(conns) == 0 { var err error s.logger.Printf("get conns from zk remote, serviceName: %s", serviceName) - conns, err = s.GetConnsRemote(serviceName) + addrs, err := s.GetConnsRemote(serviceName) if err != nil { - s.lock.Unlock() return nil, err } - if len(conns) == 0 { - return nil, fmt.Errorf( - "no conn for service %s, grpc server may not exist, local conn is %v, please check zookeeper server %v, path: %s", - serviceName, - s.localConns, - s.zkServers, - s.zkRoot, - ) + if len(addrs) == 0 { + return nil, fmt.Errorf("no conn for service %s, grpc server may not exist, local conn is %v, please check zookeeper server %v, path: %s", serviceName, s.localConns, s.zkServers, s.zkRoot) } - s.localConns[serviceName] = conns - } - s.lock.Unlock() - var ret []grpc.ClientConnInterface - s.logger.Printf("get conns from zk success, serviceName: %s", serviceName) - for _, conn := range conns { - cc, err := grpc.DialContext(ctx, conn.Addr, append(s.options, opts...)...) - if err != nil { - return nil, errors.Wrap(err, fmt.Sprintf("conns dialContext error, conn: %s", conn.Addr)) + for _, addr := range addrs { + cc, err := grpc.DialContext(ctx, addr.Addr, append(s.options, opts...)...) + if err != nil { + log.ZError(context.Background(), "dialContext failed", err, "addr", addr.Addr, "opts", append(s.options, opts...)) + return nil, errs.Wrap(err) + } + conns = append(conns, cc) } - ret = append(ret, cc) + s.localConns[serviceName] = conns } - s.logger.Printf("dial ctx success, serviceName: %s", serviceName) - return ret, nil + return conns, nil } -func (s *ZkClient) GetConn( - ctx context.Context, - serviceName string, - opts ...grpc.DialOption, -) (grpc.ClientConnInterface, error) { - newOpts := append( - s.options, - grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, s.balancerName)), - ) +func (s *ZkClient) GetConn(ctx context.Context, serviceName string, opts ...grpc.DialOption) (grpc.ClientConnInterface, error) { + newOpts := append(s.options, grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, s.balancerName))) s.logger.Printf("get conn from client, serviceName: %s", serviceName) return grpc.DialContext(ctx, fmt.Sprintf("%s:///%s", s.scheme, serviceName), append(newOpts, opts...)...) } diff --git a/pkg/discoveryregistry/zookeeper/register.go b/pkg/discoveryregistry/zookeeper/register.go index 1531980bb..fec72538d 100644 --- a/pkg/discoveryregistry/zookeeper/register.go +++ b/pkg/discoveryregistry/zookeeper/register.go @@ -19,7 +19,6 @@ import ( "github.com/go-zookeeper/zk" "google.golang.org/grpc" - "google.golang.org/grpc/resolver" ) func (s *ZkClient) CreateRpcRootNodes(serviceNames []string) error { @@ -61,7 +60,7 @@ func (s *ZkClient) UnRegister() error { } time.Sleep(time.Second) s.node = "" - s.localConns = make(map[string][]resolver.Address) + s.localConns = make(map[string][]grpc.ClientConnInterface) s.resolvers = make(map[string]*Resolver) return nil } diff --git a/pkg/discoveryregistry/zookeeper/zk.go b/pkg/discoveryregistry/zookeeper/zk.go index b9b68c528..a3b02b12e 100644 --- a/pkg/discoveryregistry/zookeeper/zk.go +++ b/pkg/discoveryregistry/zookeeper/zk.go @@ -51,8 +51,9 @@ type ZkClient struct { lock sync.Locker options []grpc.DialOption - resolvers map[string]*Resolver - localConns map[string][]resolver.Address + resolvers map[string]*Resolver + localConns map[string][]grpc.ClientConnInterface + balancerName string logger Logger @@ -103,7 +104,7 @@ func NewClient(zkServers []string, zkRoot string, options ...ZkOption) (*ZkClien zkRoot: "/", scheme: zkRoot, timeout: timeout, - localConns: make(map[string][]resolver.Address), + localConns: make(map[string][]grpc.ClientConnInterface), resolvers: make(map[string]*Resolver), lock: &sync.Mutex{}, } @@ -216,6 +217,6 @@ func (s *ZkClient) AddOption(opts ...grpc.DialOption) { s.options = append(s.options, opts...) } -func (s *ZkClient) GetClientLocalConns() map[string][]resolver.Address { +func (s *ZkClient) GetClientLocalConns() map[string][]grpc.ClientConnInterface { return s.localConns } diff --git a/pkg/errs/code.go b/pkg/errs/code.go index b153e4e40..3cd66611d 100644 --- a/pkg/errs/code.go +++ b/pkg/errs/code.go @@ -36,17 +36,12 @@ const ( // 通用错误码 const ( - NoError = 0 //无错误 - DatabaseError = 90002 //redis/mysql等db错误 - NetworkError = 90004 //网络错误 - IdentityError = 90008 // 身份错误 非管理员token,且token中userID与请求userID不一致 - GRPCConnIsNil = 90006 //grpc连接空 - DefaultOtherError = 90006 //其他错误 - DataError = 90007 //数据错误 - ConfigError = 90009 - CallbackError = 80000 - RelationshipAlreadyError = 92001 //已经是好友关系(或者黑名单) - NotRelationshipYetError = 92002 //不是好友关系(或者黑名单) + NoError = 0 //无错误 + DatabaseError = 90002 //redis/mysql等db错误 + NetworkError = 90004 //网络错误 + DataError = 90007 //数据错误 + + CallbackError = 80000 //通用错误码 ServerInternalError = 500 //服务器内部错误 @@ -57,50 +52,41 @@ const ( // 账号错误码 UserIDNotFoundError = 1101 //UserID不存在 或未注册 - UserIDExisted = 1102 //UserID已存在 - RegisteredAlreadyError = 1103 //用户已经注册过了 + RegisteredAlreadyError = 1102 //用户已经注册过了 // 群组错误码 - GroupIDNotFoundError = 1201 //GroupID不存在 - GroupIDExisted = 1202 //GroupID已存在 - OnlyOneOwnerError = 1203 //只能有一个群主 - InGroupAlreadyError = 1204 //已在群组中 - NotInGroupYetError = 1205 //不在群组中 - DismissedAlreadyError = 1206 //群组已经解散 - OwnerNotAllowedQuitError = 1207 //群主不能退群 - GroupTypeNotSupport = 1208 - GroupNoOwner = 1209 - GroupRequestHandled = 1210 + GroupIDNotFoundError = 1201 //GroupID不存在 + GroupIDExisted = 1202 //GroupID已存在 + NotInGroupYetError = 1203 //不在群组中 + DismissedAlreadyError = 1204 //群组已经解散 + GroupTypeNotSupport = 1205 + GroupRequestHandled = 1206 // 关系链错误码 - CanNotAddYourselfError = 1301 //不能添加自己为好友 - BlockedByPeer = 1302 //被对方拉黑 - NotPeersFriend = 1303 //不是对方的好友 + CanNotAddYourselfError = 1301 //不能添加自己为好友 + BlockedByPeer = 1302 //被对方拉黑 + NotPeersFriend = 1303 //不是对方的好友 + RelationshipAlreadyError = 1304 //已经是好友关系 // 消息错误码 MessageHasReadDisable = 1401 MutedInGroup = 1402 //群成员被禁言 MutedGroup = 1403 //群被禁言 - UserNotRecvMsg = 1404 //用户设置了不接收消息 - MsgAlreadyRevoke = 1405 //消息已撤回 + MsgAlreadyRevoke = 1404 //消息已撤回 // token错误码 - TokenExpiredError = 1501 - TokenInvalidError = 1502 - TokenMalformedError = 1503 - TokenNotValidYetError = 1504 - TokenUnknownError = 1505 - TokenKickedError = 1506 - TokenDifferentPlatformIDError = 1507 - TokenDifferentUserIDError = 1508 - TokenNotExistError = 1509 + TokenExpiredError = 1501 + TokenInvalidError = 1502 + TokenMalformedError = 1503 + TokenNotValidYetError = 1504 + TokenUnknownError = 1505 + TokenKickedError = 1506 + TokenNotExistError = 1507 // 长连接网关错误码 ConnOverMaxNumLimit = 1601 ConnArgsErr = 1602 - ConnUpdateErr = 1603 // S3错误码 - FileUploadedCompleteError = 1701 // 文件已上传 - FileUploadedExpiredError = 1702 // 上传过期 + FileUploadedExpiredError = 1701 // 上传过期 ) diff --git a/pkg/errs/predefine.go b/pkg/errs/predefine.go index 4890a0996..16525420c 100644 --- a/pkg/errs/predefine.go +++ b/pkg/errs/predefine.go @@ -16,66 +16,49 @@ package errs var ( ErrArgs = NewCodeError(ArgsError, "ArgsError") + ErrNoPermission = NewCodeError(NoPermissionError, "NoPermissionError") ErrDatabase = NewCodeError(DatabaseError, "DatabaseError") ErrInternalServer = NewCodeError(ServerInternalError, "ServerInternalError") ErrNetwork = NewCodeError(NetworkError, "NetworkError") - ErrNoPermission = NewCodeError(NoPermissionError, "NoPermissionError") - ErrIdentity = NewCodeError(IdentityError, "IdentityError") ErrCallback = NewCodeError(CallbackError, "CallbackError") ErrCallbackContinue = NewCodeError(CallbackError, "ErrCallbackContinue") ErrUserIDNotFound = NewCodeError(UserIDNotFoundError, "UserIDNotFoundError") ErrGroupIDNotFound = NewCodeError(GroupIDNotFoundError, "GroupIDNotFoundError") ErrGroupIDExisted = NewCodeError(GroupIDExisted, "GroupIDExisted") - ErrUserIDExisted = NewCodeError(UserIDExisted, "UserIDExisted") ErrRecordNotFound = NewCodeError(RecordNotFoundError, "RecordNotFoundError") - ErrRelationshipAlready = NewCodeError(RelationshipAlreadyError, "RelationshipAlreadyError") - ErrNotRelationshipYet = NewCodeError(NotRelationshipYetError, "NotRelationshipYetError") - ErrCanNotAddYourself = NewCodeError(CanNotAddYourselfError, "CanNotAddYourselfError") - - ErrOnlyOneOwner = NewCodeError(OnlyOneOwnerError, "OnlyOneOwnerError") - ErrInGroupAlready = NewCodeError(InGroupAlreadyError, "InGroupAlreadyError") ErrNotInGroupYet = NewCodeError(NotInGroupYetError, "NotInGroupYetError") ErrDismissedAlready = NewCodeError(DismissedAlreadyError, "DismissedAlreadyError") - ErrOwnerNotAllowedQuit = NewCodeError(OwnerNotAllowedQuitError, "OwnerNotAllowedQuitError") ErrRegisteredAlready = NewCodeError(RegisteredAlreadyError, "RegisteredAlreadyError") ErrGroupTypeNotSupport = NewCodeError(GroupTypeNotSupport, "") - ErrGroupNoOwner = NewCodeError(GroupNoOwner, "ErrGroupNoOwner") + ErrGroupRequestHandled = NewCodeError(GroupRequestHandled, "GroupRequestHandled") - ErrDefaultOther = NewCodeError(DefaultOtherError, "DefaultOtherError") - ErrData = NewCodeError(DataError, "DataError") - ErrTokenExpired = NewCodeError(TokenExpiredError, "TokenExpiredError") - ErrTokenInvalid = NewCodeError(TokenInvalidError, "TokenInvalidError") // - ErrTokenMalformed = NewCodeError(TokenMalformedError, "TokenMalformedError") //格式错误 - ErrTokenNotValidYet = NewCodeError(TokenNotValidYetError, "TokenNotValidYetError") //还未生效 - ErrTokenUnknown = NewCodeError(TokenUnknownError, "TokenUnknownError") //未知错误 - ErrTokenKicked = NewCodeError(TokenKickedError, "TokenKickedError") - ErrTokenNotExist = NewCodeError(TokenNotExistError, "TokenNotExistError") //在redis中不存在 - ErrTokenDifferentPlatformID = NewCodeError(TokenDifferentPlatformIDError, "TokenDifferentPlatformIDError") - ErrTokenDifferentUserID = NewCodeError(TokenDifferentUserIDError, "TokenDifferentUserIDError") - ErrDuplicateKey = NewCodeError(DuplicateKeyError, "DuplicateKeyError") + ErrData = NewCodeError(DataError, "DataError") + ErrTokenExpired = NewCodeError(TokenExpiredError, "TokenExpiredError") + ErrTokenInvalid = NewCodeError(TokenInvalidError, "TokenInvalidError") // + ErrTokenMalformed = NewCodeError(TokenMalformedError, "TokenMalformedError") //格式错误 + ErrTokenNotValidYet = NewCodeError(TokenNotValidYetError, "TokenNotValidYetError") //还未生效 + ErrTokenUnknown = NewCodeError(TokenUnknownError, "TokenUnknownError") //未知错误 + ErrTokenKicked = NewCodeError(TokenKickedError, "TokenKickedError") + ErrTokenNotExist = NewCodeError(TokenNotExistError, "TokenNotExistError") //在redis中不存在 + ErrDuplicateKey = NewCodeError(DuplicateKeyError, "DuplicateKeyError") ErrMessageHasReadDisable = NewCodeError(MessageHasReadDisable, "MessageHasReadDisable") - ErrBlockedByPeer = NewCodeError(BlockedByPeer, "BlockedByPeer") - //不是对方的好友 - ErrNotPeersFriend = NewCodeError(NotPeersFriend, "NotPeersFriend") + ErrCanNotAddYourself = NewCodeError(CanNotAddYourselfError, "CanNotAddYourselfError") + ErrBlockedByPeer = NewCodeError(BlockedByPeer, "BlockedByPeer") + ErrNotPeersFriend = NewCodeError(NotPeersFriend, "NotPeersFriend") + ErrRelationshipAlready = NewCodeError(RelationshipAlreadyError, "RelationshipAlreadyError") ErrMutedInGroup = NewCodeError(MutedInGroup, "MutedInGroup") ErrMutedGroup = NewCodeError(MutedGroup, "MutedGroup") - ErrUserNotRecvMsg = NewCodeError(UserNotRecvMsg, "UserNotRecvMsg") ErrMsgAlreadyRevoke = NewCodeError(MsgAlreadyRevoke, "MsgAlreadyRevoke") ErrConnOverMaxNumLimit = NewCodeError(ConnOverMaxNumLimit, "ConnOverMaxNumLimit") - ErrConnArgsErr = NewCodeError(ConnArgsErr, "args err, need token, sendID, platformID") - ErrConnUpdateErr = NewCodeError(ConnArgsErr, "upgrade http conn err") - - ErrConfig = NewCodeError(ConfigError, "ConfigError") + ErrConnArgsErr = NewCodeError(ConnArgsErr, "args err, need token, sendID, platformID") - ErrFileUploadedComplete = NewCodeError(FileUploadedCompleteError, "FileUploadedComplete") - ErrFileUploadedExpired = NewCodeError(FileUploadedExpiredError, "FileUploadedExpiredError") - ErrGroupRequestHandled = NewCodeError(GroupRequestHandled, "GroupRequestHandled") + ErrFileUploadedExpired = NewCodeError(FileUploadedExpiredError, "FileUploadedExpiredError") ) diff --git a/pkg/errs/relation.go b/pkg/errs/relation.go index 76578f4bb..0a7ad4997 100644 --- a/pkg/errs/relation.go +++ b/pkg/errs/relation.go @@ -19,7 +19,6 @@ var Relation = &relation{m: make(map[int]map[int]struct{})} func init() { Relation.Add(RecordNotFoundError, UserIDNotFoundError) Relation.Add(RecordNotFoundError, GroupIDNotFoundError) - Relation.Add(DuplicateKeyError, UserIDExisted) Relation.Add(DuplicateKeyError, GroupIDExisted) } diff --git a/pkg/proto/conversation/conversation.pb.go b/pkg/proto/conversation/conversation.pb.go index debced9b9..7ec352420 100644 --- a/pkg/proto/conversation/conversation.pb.go +++ b/pkg/proto/conversation/conversation.pb.go @@ -32,20 +32,23 @@ type Conversation struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - OwnerUserID string `protobuf:"bytes,1,opt,name=ownerUserID,proto3" json:"ownerUserID"` - ConversationID string `protobuf:"bytes,2,opt,name=conversationID,proto3" json:"conversationID"` - RecvMsgOpt int32 `protobuf:"varint,3,opt,name=recvMsgOpt,proto3" json:"recvMsgOpt"` - ConversationType int32 `protobuf:"varint,4,opt,name=conversationType,proto3" json:"conversationType"` - UserID string `protobuf:"bytes,5,opt,name=userID,proto3" json:"userID"` - GroupID string `protobuf:"bytes,6,opt,name=groupID,proto3" json:"groupID"` - IsPinned bool `protobuf:"varint,7,opt,name=isPinned,proto3" json:"isPinned"` - AttachedInfo string `protobuf:"bytes,8,opt,name=attachedInfo,proto3" json:"attachedInfo"` - IsPrivateChat bool `protobuf:"varint,9,opt,name=isPrivateChat,proto3" json:"isPrivateChat"` - GroupAtType int32 `protobuf:"varint,10,opt,name=groupAtType,proto3" json:"groupAtType"` - Ex string `protobuf:"bytes,11,opt,name=ex,proto3" json:"ex"` - BurnDuration int32 `protobuf:"varint,12,opt,name=burnDuration,proto3" json:"burnDuration"` - MinSeq int64 `protobuf:"varint,13,opt,name=minSeq,proto3" json:"minSeq"` - MaxSeq int64 `protobuf:"varint,14,opt,name=maxSeq,proto3" json:"maxSeq"` + OwnerUserID string `protobuf:"bytes,1,opt,name=ownerUserID,proto3" json:"ownerUserID"` + ConversationID string `protobuf:"bytes,2,opt,name=conversationID,proto3" json:"conversationID"` + RecvMsgOpt int32 `protobuf:"varint,3,opt,name=recvMsgOpt,proto3" json:"recvMsgOpt"` + ConversationType int32 `protobuf:"varint,4,opt,name=conversationType,proto3" json:"conversationType"` + UserID string `protobuf:"bytes,5,opt,name=userID,proto3" json:"userID"` + GroupID string `protobuf:"bytes,6,opt,name=groupID,proto3" json:"groupID"` + IsPinned bool `protobuf:"varint,7,opt,name=isPinned,proto3" json:"isPinned"` + AttachedInfo string `protobuf:"bytes,8,opt,name=attachedInfo,proto3" json:"attachedInfo"` + IsPrivateChat bool `protobuf:"varint,9,opt,name=isPrivateChat,proto3" json:"isPrivateChat"` + GroupAtType int32 `protobuf:"varint,10,opt,name=groupAtType,proto3" json:"groupAtType"` + Ex string `protobuf:"bytes,11,opt,name=ex,proto3" json:"ex"` + BurnDuration int32 `protobuf:"varint,12,opt,name=burnDuration,proto3" json:"burnDuration"` + MinSeq int64 `protobuf:"varint,13,opt,name=minSeq,proto3" json:"minSeq"` + MaxSeq int64 `protobuf:"varint,14,opt,name=maxSeq,proto3" json:"maxSeq"` + MsgDestructTime int64 `protobuf:"varint,15,opt,name=msgDestructTime,proto3" json:"msgDestructTime"` + LatestMsgDestructTime int64 `protobuf:"varint,16,opt,name=latestMsgDestructTime,proto3" json:"latestMsgDestructTime"` + IsMsgDestruct bool `protobuf:"varint,17,opt,name=isMsgDestruct,proto3" json:"isMsgDestruct"` } func (x *Conversation) Reset() { @@ -178,6 +181,27 @@ func (x *Conversation) GetMaxSeq() int64 { return 0 } +func (x *Conversation) GetMsgDestructTime() int64 { + if x != nil { + return x.MsgDestructTime + } + return 0 +} + +func (x *Conversation) GetLatestMsgDestructTime() int64 { + if x != nil { + return x.LatestMsgDestructTime + } + return 0 +} + +func (x *Conversation) GetIsMsgDestruct() bool { + if x != nil { + return x.IsMsgDestruct + } + return false +} + type ConversationReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -196,6 +220,8 @@ type ConversationReq struct { MinSeq *wrapperspb.Int64Value `protobuf:"bytes,11,opt,name=minSeq,proto3" json:"minSeq"` MaxSeq *wrapperspb.Int64Value `protobuf:"bytes,12,opt,name=maxSeq,proto3" json:"maxSeq"` GroupAtType *wrapperspb.Int32Value `protobuf:"bytes,13,opt,name=groupAtType,proto3" json:"groupAtType"` + MsgDestructTime *wrapperspb.Int64Value `protobuf:"bytes,14,opt,name=msgDestructTime,proto3" json:"msgDestructTime"` + IsMsgDestruct *wrapperspb.BoolValue `protobuf:"bytes,15,opt,name=isMsgDestruct,proto3" json:"isMsgDestruct"` } func (x *ConversationReq) Reset() { @@ -321,107 +347,20 @@ func (x *ConversationReq) GetGroupAtType() *wrapperspb.Int32Value { return nil } -type ModifyConversationFieldReq struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - UserIDList []string `protobuf:"bytes,1,rep,name=userIDList,proto3" json:"userIDList"` - FieldType int32 `protobuf:"varint,2,opt,name=FieldType,proto3" json:"FieldType"` - Conversation *Conversation `protobuf:"bytes,3,opt,name=conversation,proto3" json:"conversation"` -} - -func (x *ModifyConversationFieldReq) Reset() { - *x = ModifyConversationFieldReq{} - if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ModifyConversationFieldReq) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ModifyConversationFieldReq) ProtoMessage() {} - -func (x *ModifyConversationFieldReq) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ModifyConversationFieldReq.ProtoReflect.Descriptor instead. -func (*ModifyConversationFieldReq) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{2} -} - -func (x *ModifyConversationFieldReq) GetUserIDList() []string { +func (x *ConversationReq) GetMsgDestructTime() *wrapperspb.Int64Value { if x != nil { - return x.UserIDList + return x.MsgDestructTime } return nil } -func (x *ModifyConversationFieldReq) GetFieldType() int32 { - if x != nil { - return x.FieldType - } - return 0 -} - -func (x *ModifyConversationFieldReq) GetConversation() *Conversation { +func (x *ConversationReq) GetIsMsgDestruct() *wrapperspb.BoolValue { if x != nil { - return x.Conversation + return x.IsMsgDestruct } return nil } -type ModifyConversationFieldResp struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *ModifyConversationFieldResp) Reset() { - *x = ModifyConversationFieldResp{} - if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ModifyConversationFieldResp) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ModifyConversationFieldResp) ProtoMessage() {} - -func (x *ModifyConversationFieldResp) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ModifyConversationFieldResp.ProtoReflect.Descriptor instead. -func (*ModifyConversationFieldResp) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{3} -} - type SetConversationReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -433,7 +372,7 @@ type SetConversationReq struct { func (x *SetConversationReq) Reset() { *x = SetConversationReq{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[4] + mi := &file_conversation_conversation_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -446,7 +385,7 @@ func (x *SetConversationReq) String() string { func (*SetConversationReq) ProtoMessage() {} func (x *SetConversationReq) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[4] + mi := &file_conversation_conversation_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -459,7 +398,7 @@ func (x *SetConversationReq) ProtoReflect() protoreflect.Message { // Deprecated: Use SetConversationReq.ProtoReflect.Descriptor instead. func (*SetConversationReq) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{4} + return file_conversation_conversation_proto_rawDescGZIP(), []int{2} } func (x *SetConversationReq) GetConversation() *Conversation { @@ -478,7 +417,7 @@ type SetConversationResp struct { func (x *SetConversationResp) Reset() { *x = SetConversationResp{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[5] + mi := &file_conversation_conversation_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -491,7 +430,7 @@ func (x *SetConversationResp) String() string { func (*SetConversationResp) ProtoMessage() {} func (x *SetConversationResp) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[5] + mi := &file_conversation_conversation_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -504,108 +443,7 @@ func (x *SetConversationResp) ProtoReflect() protoreflect.Message { // Deprecated: Use SetConversationResp.ProtoReflect.Descriptor instead. func (*SetConversationResp) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{5} -} - -type SetRecvMsgOptReq struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - OwnerUserID string `protobuf:"bytes,1,opt,name=ownerUserID,proto3" json:"ownerUserID"` - ConversationID string `protobuf:"bytes,2,opt,name=conversationID,proto3" json:"conversationID"` - RecvMsgOpt int32 `protobuf:"varint,3,opt,name=recvMsgOpt,proto3" json:"recvMsgOpt"` -} - -func (x *SetRecvMsgOptReq) Reset() { - *x = SetRecvMsgOptReq{} - if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SetRecvMsgOptReq) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SetRecvMsgOptReq) ProtoMessage() {} - -func (x *SetRecvMsgOptReq) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SetRecvMsgOptReq.ProtoReflect.Descriptor instead. -func (*SetRecvMsgOptReq) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{6} -} - -func (x *SetRecvMsgOptReq) GetOwnerUserID() string { - if x != nil { - return x.OwnerUserID - } - return "" -} - -func (x *SetRecvMsgOptReq) GetConversationID() string { - if x != nil { - return x.ConversationID - } - return "" -} - -func (x *SetRecvMsgOptReq) GetRecvMsgOpt() int32 { - if x != nil { - return x.RecvMsgOpt - } - return 0 -} - -type SetRecvMsgOptResp struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *SetRecvMsgOptResp) Reset() { - *x = SetRecvMsgOptResp{} - if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SetRecvMsgOptResp) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SetRecvMsgOptResp) ProtoMessage() {} - -func (x *SetRecvMsgOptResp) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SetRecvMsgOptResp.ProtoReflect.Descriptor instead. -func (*SetRecvMsgOptResp) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{7} + return file_conversation_conversation_proto_rawDescGZIP(), []int{3} } type GetConversationReq struct { @@ -620,7 +458,7 @@ type GetConversationReq struct { func (x *GetConversationReq) Reset() { *x = GetConversationReq{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[8] + mi := &file_conversation_conversation_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -633,7 +471,7 @@ func (x *GetConversationReq) String() string { func (*GetConversationReq) ProtoMessage() {} func (x *GetConversationReq) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[8] + mi := &file_conversation_conversation_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -646,7 +484,7 @@ func (x *GetConversationReq) ProtoReflect() protoreflect.Message { // Deprecated: Use GetConversationReq.ProtoReflect.Descriptor instead. func (*GetConversationReq) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{8} + return file_conversation_conversation_proto_rawDescGZIP(), []int{4} } func (x *GetConversationReq) GetConversationID() string { @@ -674,7 +512,7 @@ type GetConversationResp struct { func (x *GetConversationResp) Reset() { *x = GetConversationResp{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[9] + mi := &file_conversation_conversation_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -687,7 +525,7 @@ func (x *GetConversationResp) String() string { func (*GetConversationResp) ProtoMessage() {} func (x *GetConversationResp) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[9] + mi := &file_conversation_conversation_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -700,7 +538,7 @@ func (x *GetConversationResp) ProtoReflect() protoreflect.Message { // Deprecated: Use GetConversationResp.ProtoReflect.Descriptor instead. func (*GetConversationResp) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{9} + return file_conversation_conversation_proto_rawDescGZIP(), []int{5} } func (x *GetConversationResp) GetConversation() *Conversation { @@ -722,7 +560,7 @@ type GetConversationsReq struct { func (x *GetConversationsReq) Reset() { *x = GetConversationsReq{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[10] + mi := &file_conversation_conversation_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -735,7 +573,7 @@ func (x *GetConversationsReq) String() string { func (*GetConversationsReq) ProtoMessage() {} func (x *GetConversationsReq) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[10] + mi := &file_conversation_conversation_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -748,7 +586,7 @@ func (x *GetConversationsReq) ProtoReflect() protoreflect.Message { // Deprecated: Use GetConversationsReq.ProtoReflect.Descriptor instead. func (*GetConversationsReq) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{10} + return file_conversation_conversation_proto_rawDescGZIP(), []int{6} } func (x *GetConversationsReq) GetOwnerUserID() string { @@ -776,7 +614,7 @@ type GetConversationsResp struct { func (x *GetConversationsResp) Reset() { *x = GetConversationsResp{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[11] + mi := &file_conversation_conversation_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -789,7 +627,7 @@ func (x *GetConversationsResp) String() string { func (*GetConversationsResp) ProtoMessage() {} func (x *GetConversationsResp) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[11] + mi := &file_conversation_conversation_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -802,7 +640,7 @@ func (x *GetConversationsResp) ProtoReflect() protoreflect.Message { // Deprecated: Use GetConversationsResp.ProtoReflect.Descriptor instead. func (*GetConversationsResp) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{11} + return file_conversation_conversation_proto_rawDescGZIP(), []int{7} } func (x *GetConversationsResp) GetConversations() []*Conversation { @@ -823,7 +661,7 @@ type GetAllConversationsReq struct { func (x *GetAllConversationsReq) Reset() { *x = GetAllConversationsReq{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[12] + mi := &file_conversation_conversation_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -836,7 +674,7 @@ func (x *GetAllConversationsReq) String() string { func (*GetAllConversationsReq) ProtoMessage() {} func (x *GetAllConversationsReq) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[12] + mi := &file_conversation_conversation_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -849,7 +687,7 @@ func (x *GetAllConversationsReq) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAllConversationsReq.ProtoReflect.Descriptor instead. func (*GetAllConversationsReq) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{12} + return file_conversation_conversation_proto_rawDescGZIP(), []int{8} } func (x *GetAllConversationsReq) GetOwnerUserID() string { @@ -870,7 +708,7 @@ type GetAllConversationsResp struct { func (x *GetAllConversationsResp) Reset() { *x = GetAllConversationsResp{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[13] + mi := &file_conversation_conversation_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -883,7 +721,7 @@ func (x *GetAllConversationsResp) String() string { func (*GetAllConversationsResp) ProtoMessage() {} func (x *GetAllConversationsResp) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[13] + mi := &file_conversation_conversation_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -896,7 +734,7 @@ func (x *GetAllConversationsResp) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAllConversationsResp.ProtoReflect.Descriptor instead. func (*GetAllConversationsResp) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{13} + return file_conversation_conversation_proto_rawDescGZIP(), []int{9} } func (x *GetAllConversationsResp) GetConversations() []*Conversation { @@ -906,99 +744,6 @@ func (x *GetAllConversationsResp) GetConversations() []*Conversation { return nil } -type BatchSetConversationsReq struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Conversations []*Conversation `protobuf:"bytes,1,rep,name=conversations,proto3" json:"conversations"` - OwnerUserID string `protobuf:"bytes,2,opt,name=ownerUserID,proto3" json:"ownerUserID"` -} - -func (x *BatchSetConversationsReq) Reset() { - *x = BatchSetConversationsReq{} - if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BatchSetConversationsReq) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BatchSetConversationsReq) ProtoMessage() {} - -func (x *BatchSetConversationsReq) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[14] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BatchSetConversationsReq.ProtoReflect.Descriptor instead. -func (*BatchSetConversationsReq) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{14} -} - -func (x *BatchSetConversationsReq) GetConversations() []*Conversation { - if x != nil { - return x.Conversations - } - return nil -} - -func (x *BatchSetConversationsReq) GetOwnerUserID() string { - if x != nil { - return x.OwnerUserID - } - return "" -} - -type BatchSetConversationsResp struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *BatchSetConversationsResp) Reset() { - *x = BatchSetConversationsResp{} - if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BatchSetConversationsResp) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BatchSetConversationsResp) ProtoMessage() {} - -func (x *BatchSetConversationsResp) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[15] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BatchSetConversationsResp.ProtoReflect.Descriptor instead. -func (*BatchSetConversationsResp) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{15} -} - type GetRecvMsgNotNotifyUserIDsReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1010,7 +755,7 @@ type GetRecvMsgNotNotifyUserIDsReq struct { func (x *GetRecvMsgNotNotifyUserIDsReq) Reset() { *x = GetRecvMsgNotNotifyUserIDsReq{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[16] + mi := &file_conversation_conversation_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1023,7 +768,7 @@ func (x *GetRecvMsgNotNotifyUserIDsReq) String() string { func (*GetRecvMsgNotNotifyUserIDsReq) ProtoMessage() {} func (x *GetRecvMsgNotNotifyUserIDsReq) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[16] + mi := &file_conversation_conversation_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1036,7 +781,7 @@ func (x *GetRecvMsgNotNotifyUserIDsReq) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRecvMsgNotNotifyUserIDsReq.ProtoReflect.Descriptor instead. func (*GetRecvMsgNotNotifyUserIDsReq) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{16} + return file_conversation_conversation_proto_rawDescGZIP(), []int{10} } func (x *GetRecvMsgNotNotifyUserIDsReq) GetGroupID() string { @@ -1057,7 +802,7 @@ type GetRecvMsgNotNotifyUserIDsResp struct { func (x *GetRecvMsgNotNotifyUserIDsResp) Reset() { *x = GetRecvMsgNotNotifyUserIDsResp{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[17] + mi := &file_conversation_conversation_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1070,7 +815,7 @@ func (x *GetRecvMsgNotNotifyUserIDsResp) String() string { func (*GetRecvMsgNotNotifyUserIDsResp) ProtoMessage() {} func (x *GetRecvMsgNotNotifyUserIDsResp) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[17] + mi := &file_conversation_conversation_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1083,7 +828,7 @@ func (x *GetRecvMsgNotNotifyUserIDsResp) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRecvMsgNotNotifyUserIDsResp.ProtoReflect.Descriptor instead. func (*GetRecvMsgNotNotifyUserIDsResp) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{17} + return file_conversation_conversation_proto_rawDescGZIP(), []int{11} } func (x *GetRecvMsgNotNotifyUserIDsResp) GetUserIDs() []string { @@ -1105,7 +850,7 @@ type CreateSingleChatConversationsReq struct { func (x *CreateSingleChatConversationsReq) Reset() { *x = CreateSingleChatConversationsReq{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[18] + mi := &file_conversation_conversation_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1118,7 +863,7 @@ func (x *CreateSingleChatConversationsReq) String() string { func (*CreateSingleChatConversationsReq) ProtoMessage() {} func (x *CreateSingleChatConversationsReq) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[18] + mi := &file_conversation_conversation_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1131,7 +876,7 @@ func (x *CreateSingleChatConversationsReq) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateSingleChatConversationsReq.ProtoReflect.Descriptor instead. func (*CreateSingleChatConversationsReq) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{18} + return file_conversation_conversation_proto_rawDescGZIP(), []int{12} } func (x *CreateSingleChatConversationsReq) GetRecvID() string { @@ -1157,7 +902,7 @@ type CreateSingleChatConversationsResp struct { func (x *CreateSingleChatConversationsResp) Reset() { *x = CreateSingleChatConversationsResp{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[19] + mi := &file_conversation_conversation_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1170,7 +915,7 @@ func (x *CreateSingleChatConversationsResp) String() string { func (*CreateSingleChatConversationsResp) ProtoMessage() {} func (x *CreateSingleChatConversationsResp) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[19] + mi := &file_conversation_conversation_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1183,7 +928,7 @@ func (x *CreateSingleChatConversationsResp) ProtoReflect() protoreflect.Message // Deprecated: Use CreateSingleChatConversationsResp.ProtoReflect.Descriptor instead. func (*CreateSingleChatConversationsResp) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{19} + return file_conversation_conversation_proto_rawDescGZIP(), []int{13} } type CreateGroupChatConversationsReq struct { @@ -1198,7 +943,7 @@ type CreateGroupChatConversationsReq struct { func (x *CreateGroupChatConversationsReq) Reset() { *x = CreateGroupChatConversationsReq{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[20] + mi := &file_conversation_conversation_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1211,7 +956,7 @@ func (x *CreateGroupChatConversationsReq) String() string { func (*CreateGroupChatConversationsReq) ProtoMessage() {} func (x *CreateGroupChatConversationsReq) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[20] + mi := &file_conversation_conversation_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1224,7 +969,7 @@ func (x *CreateGroupChatConversationsReq) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateGroupChatConversationsReq.ProtoReflect.Descriptor instead. func (*CreateGroupChatConversationsReq) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{20} + return file_conversation_conversation_proto_rawDescGZIP(), []int{14} } func (x *CreateGroupChatConversationsReq) GetUserIDs() []string { @@ -1250,7 +995,7 @@ type CreateGroupChatConversationsResp struct { func (x *CreateGroupChatConversationsResp) Reset() { *x = CreateGroupChatConversationsResp{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[21] + mi := &file_conversation_conversation_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1263,7 +1008,7 @@ func (x *CreateGroupChatConversationsResp) String() string { func (*CreateGroupChatConversationsResp) ProtoMessage() {} func (x *CreateGroupChatConversationsResp) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[21] + mi := &file_conversation_conversation_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1276,7 +1021,7 @@ func (x *CreateGroupChatConversationsResp) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateGroupChatConversationsResp.ProtoReflect.Descriptor instead. func (*CreateGroupChatConversationsResp) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{21} + return file_conversation_conversation_proto_rawDescGZIP(), []int{15} } type SetConversationMaxSeqReq struct { @@ -1292,7 +1037,7 @@ type SetConversationMaxSeqReq struct { func (x *SetConversationMaxSeqReq) Reset() { *x = SetConversationMaxSeqReq{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[22] + mi := &file_conversation_conversation_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1305,7 +1050,7 @@ func (x *SetConversationMaxSeqReq) String() string { func (*SetConversationMaxSeqReq) ProtoMessage() {} func (x *SetConversationMaxSeqReq) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[22] + mi := &file_conversation_conversation_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1318,7 +1063,7 @@ func (x *SetConversationMaxSeqReq) ProtoReflect() protoreflect.Message { // Deprecated: Use SetConversationMaxSeqReq.ProtoReflect.Descriptor instead. func (*SetConversationMaxSeqReq) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{22} + return file_conversation_conversation_proto_rawDescGZIP(), []int{16} } func (x *SetConversationMaxSeqReq) GetConversationID() string { @@ -1351,7 +1096,7 @@ type SetConversationMaxSeqResp struct { func (x *SetConversationMaxSeqResp) Reset() { *x = SetConversationMaxSeqResp{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[23] + mi := &file_conversation_conversation_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1364,7 +1109,7 @@ func (x *SetConversationMaxSeqResp) String() string { func (*SetConversationMaxSeqResp) ProtoMessage() {} func (x *SetConversationMaxSeqResp) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[23] + mi := &file_conversation_conversation_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1377,7 +1122,7 @@ func (x *SetConversationMaxSeqResp) ProtoReflect() protoreflect.Message { // Deprecated: Use SetConversationMaxSeqResp.ProtoReflect.Descriptor instead. func (*SetConversationMaxSeqResp) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{23} + return file_conversation_conversation_proto_rawDescGZIP(), []int{17} } type GetConversationIDsReq struct { @@ -1391,7 +1136,7 @@ type GetConversationIDsReq struct { func (x *GetConversationIDsReq) Reset() { *x = GetConversationIDsReq{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[24] + mi := &file_conversation_conversation_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1404,7 +1149,7 @@ func (x *GetConversationIDsReq) String() string { func (*GetConversationIDsReq) ProtoMessage() {} func (x *GetConversationIDsReq) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[24] + mi := &file_conversation_conversation_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1417,7 +1162,7 @@ func (x *GetConversationIDsReq) ProtoReflect() protoreflect.Message { // Deprecated: Use GetConversationIDsReq.ProtoReflect.Descriptor instead. func (*GetConversationIDsReq) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{24} + return file_conversation_conversation_proto_rawDescGZIP(), []int{18} } func (x *GetConversationIDsReq) GetUserID() string { @@ -1438,7 +1183,7 @@ type GetConversationIDsResp struct { func (x *GetConversationIDsResp) Reset() { *x = GetConversationIDsResp{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[25] + mi := &file_conversation_conversation_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1451,7 +1196,7 @@ func (x *GetConversationIDsResp) String() string { func (*GetConversationIDsResp) ProtoMessage() {} func (x *GetConversationIDsResp) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[25] + mi := &file_conversation_conversation_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1464,7 +1209,7 @@ func (x *GetConversationIDsResp) ProtoReflect() protoreflect.Message { // Deprecated: Use GetConversationIDsResp.ProtoReflect.Descriptor instead. func (*GetConversationIDsResp) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{25} + return file_conversation_conversation_proto_rawDescGZIP(), []int{19} } func (x *GetConversationIDsResp) GetConversationIDs() []string { @@ -1486,7 +1231,7 @@ type SetConversationsReq struct { func (x *SetConversationsReq) Reset() { *x = SetConversationsReq{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[26] + mi := &file_conversation_conversation_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1499,7 +1244,7 @@ func (x *SetConversationsReq) String() string { func (*SetConversationsReq) ProtoMessage() {} func (x *SetConversationsReq) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[26] + mi := &file_conversation_conversation_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1512,7 +1257,7 @@ func (x *SetConversationsReq) ProtoReflect() protoreflect.Message { // Deprecated: Use SetConversationsReq.ProtoReflect.Descriptor instead. func (*SetConversationsReq) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{26} + return file_conversation_conversation_proto_rawDescGZIP(), []int{20} } func (x *SetConversationsReq) GetUserIDs() []string { @@ -1538,7 +1283,7 @@ type SetConversationsResp struct { func (x *SetConversationsResp) Reset() { *x = SetConversationsResp{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[27] + mi := &file_conversation_conversation_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1551,7 +1296,7 @@ func (x *SetConversationsResp) String() string { func (*SetConversationsResp) ProtoMessage() {} func (x *SetConversationsResp) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[27] + mi := &file_conversation_conversation_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1564,7 +1309,7 @@ func (x *SetConversationsResp) ProtoReflect() protoreflect.Message { // Deprecated: Use SetConversationsResp.ProtoReflect.Descriptor instead. func (*SetConversationsResp) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{27} + return file_conversation_conversation_proto_rawDescGZIP(), []int{21} } type GetUserConversationIDsHashReq struct { @@ -1578,7 +1323,7 @@ type GetUserConversationIDsHashReq struct { func (x *GetUserConversationIDsHashReq) Reset() { *x = GetUserConversationIDsHashReq{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[28] + mi := &file_conversation_conversation_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1591,7 +1336,7 @@ func (x *GetUserConversationIDsHashReq) String() string { func (*GetUserConversationIDsHashReq) ProtoMessage() {} func (x *GetUserConversationIDsHashReq) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[28] + mi := &file_conversation_conversation_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1604,7 +1349,7 @@ func (x *GetUserConversationIDsHashReq) ProtoReflect() protoreflect.Message { // Deprecated: Use GetUserConversationIDsHashReq.ProtoReflect.Descriptor instead. func (*GetUserConversationIDsHashReq) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{28} + return file_conversation_conversation_proto_rawDescGZIP(), []int{22} } func (x *GetUserConversationIDsHashReq) GetOwnerUserID() string { @@ -1625,7 +1370,7 @@ type GetUserConversationIDsHashResp struct { func (x *GetUserConversationIDsHashResp) Reset() { *x = GetUserConversationIDsHashResp{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[29] + mi := &file_conversation_conversation_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1638,7 +1383,7 @@ func (x *GetUserConversationIDsHashResp) String() string { func (*GetUserConversationIDsHashResp) ProtoMessage() {} func (x *GetUserConversationIDsHashResp) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[29] + mi := &file_conversation_conversation_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1651,7 +1396,7 @@ func (x *GetUserConversationIDsHashResp) ProtoReflect() protoreflect.Message { // Deprecated: Use GetUserConversationIDsHashResp.ProtoReflect.Descriptor instead. func (*GetUserConversationIDsHashResp) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{29} + return file_conversation_conversation_proto_rawDescGZIP(), []int{23} } func (x *GetUserConversationIDsHashResp) GetHash() uint64 { @@ -1672,7 +1417,7 @@ type GetConversationsByConversationIDReq struct { func (x *GetConversationsByConversationIDReq) Reset() { *x = GetConversationsByConversationIDReq{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[30] + mi := &file_conversation_conversation_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1685,7 +1430,7 @@ func (x *GetConversationsByConversationIDReq) String() string { func (*GetConversationsByConversationIDReq) ProtoMessage() {} func (x *GetConversationsByConversationIDReq) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[30] + mi := &file_conversation_conversation_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1698,7 +1443,7 @@ func (x *GetConversationsByConversationIDReq) ProtoReflect() protoreflect.Messag // Deprecated: Use GetConversationsByConversationIDReq.ProtoReflect.Descriptor instead. func (*GetConversationsByConversationIDReq) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{30} + return file_conversation_conversation_proto_rawDescGZIP(), []int{24} } func (x *GetConversationsByConversationIDReq) GetConversationIDs() []string { @@ -1719,7 +1464,7 @@ type GetConversationsByConversationIDResp struct { func (x *GetConversationsByConversationIDResp) Reset() { *x = GetConversationsByConversationIDResp{} if protoimpl.UnsafeEnabled { - mi := &file_conversation_conversation_proto_msgTypes[31] + mi := &file_conversation_conversation_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1732,7 +1477,7 @@ func (x *GetConversationsByConversationIDResp) String() string { func (*GetConversationsByConversationIDResp) ProtoMessage() {} func (x *GetConversationsByConversationIDResp) ProtoReflect() protoreflect.Message { - mi := &file_conversation_conversation_proto_msgTypes[31] + mi := &file_conversation_conversation_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1745,7 +1490,7 @@ func (x *GetConversationsByConversationIDResp) ProtoReflect() protoreflect.Messa // Deprecated: Use GetConversationsByConversationIDResp.ProtoReflect.Descriptor instead. func (*GetConversationsByConversationIDResp) Descriptor() ([]byte, []int) { - return file_conversation_conversation_proto_rawDescGZIP(), []int{31} + return file_conversation_conversation_proto_rawDescGZIP(), []int{25} } func (x *GetConversationsByConversationIDResp) GetConversations() []*Conversation { @@ -1763,7 +1508,7 @@ var file_conversation_conversation_proto_rawDesc = []byte{ 0x6f, 0x12, 0x19, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x1b, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x70, 0x62, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, - 0x73, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc2, 0x03, 0x0a, 0x0c, 0x43, 0x6f, + 0x73, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc8, 0x04, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x26, 0x0a, 0x0e, @@ -1791,328 +1536,290 @@ var file_conversation_conversation_proto_rawDesc = []byte{ 0x52, 0x0c, 0x62, 0x75, 0x72, 0x6e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x69, 0x6e, 0x53, 0x65, 0x71, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6d, 0x69, 0x6e, 0x53, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x78, 0x53, 0x65, 0x71, - 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x22, 0xde, - 0x05, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x18, - 0x0a, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x12, 0x41, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x76, - 0x4d, 0x73, 0x67, 0x4f, 0x70, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x4f, - 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x0a, 0x72, 0x65, 0x63, 0x76, 0x4d, 0x73, 0x67, 0x4f, 0x70, 0x74, 0x12, 0x3c, 0x0a, 0x08, 0x69, - 0x73, 0x50, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, - 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x08, 0x69, 0x73, 0x50, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x12, 0x46, 0x0a, 0x0c, 0x61, 0x74, 0x74, - 0x61, 0x63, 0x68, 0x65, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x22, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x49, 0x6e, 0x66, - 0x6f, 0x12, 0x46, 0x0a, 0x0d, 0x69, 0x73, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x43, 0x68, - 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, - 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0d, 0x69, 0x73, 0x50, 0x72, - 0x69, 0x76, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x74, 0x12, 0x32, 0x0a, 0x02, 0x65, 0x78, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, - 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x02, 0x65, 0x78, 0x12, 0x45, 0x0a, - 0x0c, 0x62, 0x75, 0x72, 0x6e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x33, - 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x62, 0x75, 0x72, 0x6e, 0x44, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x06, 0x6d, 0x69, 0x6e, 0x53, 0x65, 0x71, 0x18, 0x0b, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, - 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x6d, 0x69, 0x6e, 0x53, 0x65, 0x71, 0x12, - 0x39, 0x0a, 0x06, 0x6d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x12, 0x43, 0x0a, 0x0b, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x41, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x0b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, - 0xa7, 0x01, 0x0a, 0x1a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, - 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x12, 0x1e, - 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1c, - 0x0a, 0x09, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x09, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x4b, 0x0a, 0x0c, - 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, - 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x63, 0x6f, 0x6e, - 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x1d, 0x0a, 0x1b, 0x4d, 0x6f, 0x64, - 0x69, 0x66, 0x79, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, - 0x69, 0x65, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x22, 0x61, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x43, - 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x12, 0x4b, - 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x63, - 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x15, 0x0a, 0x13, 0x53, - 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x22, 0x7c, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x52, 0x65, 0x63, 0x76, 0x4d, 0x73, 0x67, - 0x4f, 0x70, 0x74, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, - 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x77, 0x6e, - 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x12, 0x28, + 0x0a, 0x0f, 0x6d, 0x73, 0x67, 0x44, 0x65, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x54, 0x69, 0x6d, + 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x6d, 0x73, 0x67, 0x44, 0x65, 0x73, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x34, 0x0a, 0x15, 0x6c, 0x61, 0x74, 0x65, + 0x73, 0x74, 0x4d, 0x73, 0x67, 0x44, 0x65, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x54, 0x69, 0x6d, + 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x4d, + 0x73, 0x67, 0x44, 0x65, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x24, + 0x0a, 0x0d, 0x69, 0x73, 0x4d, 0x73, 0x67, 0x44, 0x65, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, + 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x69, 0x73, 0x4d, 0x73, 0x67, 0x44, 0x65, 0x73, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x22, 0xf3, 0x06, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, - 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x76, 0x4d, 0x73, 0x67, 0x4f, 0x70, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x72, 0x65, 0x63, 0x76, 0x4d, 0x73, 0x67, 0x4f, 0x70, 0x74, - 0x22, 0x13, 0x0a, 0x11, 0x53, 0x65, 0x74, 0x52, 0x65, 0x63, 0x76, 0x4d, 0x73, 0x67, 0x4f, 0x70, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x22, 0x5e, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, 0x63, - 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, - 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, - 0x73, 0x65, 0x72, 0x49, 0x44, 0x22, 0x62, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x4b, 0x0a, 0x0c, - 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, - 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x63, 0x6f, 0x6e, - 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x61, 0x0a, 0x13, 0x47, 0x65, 0x74, - 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, - 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, - 0x49, 0x44, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, - 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x22, 0x65, 0x0a, 0x14, - 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x12, 0x4d, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x4f, 0x70, - 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, - 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x22, 0x3a, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x6f, 0x6e, - 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, - 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x22, - 0x68, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x4d, 0x0a, 0x0d, 0x63, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x27, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8b, 0x01, 0x0a, 0x18, 0x42, 0x61, - 0x74, 0x63, 0x68, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x12, 0x4d, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, - 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, - 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, - 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, - 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x73, - 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x77, 0x6e, 0x65, - 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x22, 0x1b, 0x0a, 0x19, 0x42, 0x61, 0x74, 0x63, 0x68, - 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x22, 0x39, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x76, 0x4d, - 0x73, 0x67, 0x4e, 0x6f, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, - 0x44, 0x73, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x22, - 0x3a, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x76, 0x4d, 0x73, 0x67, 0x4e, 0x6f, 0x74, - 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x73, 0x22, 0x52, 0x0a, 0x20, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x74, 0x43, - 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x12, - 0x16, 0x0a, 0x06, 0x72, 0x65, 0x63, 0x76, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x72, 0x65, 0x63, 0x76, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x49, - 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x49, 0x44, 0x22, - 0x23, 0x0a, 0x21, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x43, - 0x68, 0x61, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x22, 0x55, 0x0a, 0x1f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x43, 0x68, 0x61, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, - 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, - 0x73, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x22, 0x22, 0x0a, 0x20, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x68, 0x61, 0x74, 0x43, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, - 0x7c, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, 0x63, - 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, - 0x49, 0x44, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, - 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x22, 0x1b, 0x0a, - 0x19, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, 0x73, 0x70, 0x22, 0x2f, 0x0a, 0x15, 0x47, 0x65, - 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, - 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x22, 0x42, 0x0a, 0x16, 0x47, - 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, - 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x22, - 0x7f, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x73, - 0x12, 0x4e, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, + 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, + 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, + 0x65, 0x72, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x12, 0x41, + 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x76, 0x4d, 0x73, 0x67, 0x4f, 0x70, 0x74, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x72, 0x65, 0x63, 0x76, 0x4d, 0x73, 0x67, 0x4f, 0x70, + 0x74, 0x12, 0x3c, 0x0a, 0x08, 0x69, 0x73, 0x50, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x69, 0x73, 0x50, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x12, + 0x46, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x61, 0x63, + 0x68, 0x65, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x46, 0x0a, 0x0d, 0x69, 0x73, 0x50, 0x72, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, + 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x0d, 0x69, 0x73, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x74, 0x12, + 0x32, 0x0a, 0x02, 0x65, 0x78, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x4f, 0x70, + 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x02, 0x65, 0x78, 0x12, 0x45, 0x0a, 0x0c, 0x62, 0x75, 0x72, 0x6e, 0x44, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x6e, + 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x62, 0x75, + 0x72, 0x6e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x06, 0x6d, 0x69, + 0x6e, 0x53, 0x65, 0x71, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x4f, 0x70, 0x65, + 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x6d, + 0x69, 0x6e, 0x53, 0x65, 0x71, 0x12, 0x39, 0x0a, 0x06, 0x6d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, + 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x53, 0x65, 0x71, + 0x12, 0x43, 0x0a, 0x0b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, + 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, + 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x41, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x4b, 0x0a, 0x0f, 0x6d, 0x73, 0x67, 0x44, 0x65, 0x73, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, + 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x52, 0x0f, 0x6d, 0x73, 0x67, 0x44, 0x65, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x54, 0x69, + 0x6d, 0x65, 0x12, 0x46, 0x0a, 0x0d, 0x69, 0x73, 0x4d, 0x73, 0x67, 0x44, 0x65, 0x73, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x4f, 0x70, 0x65, 0x6e, + 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0d, 0x69, 0x73, 0x4d, + 0x73, 0x67, 0x44, 0x65, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x22, 0x61, 0x0a, 0x12, 0x53, 0x65, + 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x12, 0x4b, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x71, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x22, 0x16, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x41, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x55, - 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, - 0x44, 0x73, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x77, 0x6e, - 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x22, 0x34, 0x0a, 0x1e, 0x47, - 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x12, 0x12, 0x0a, - 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x68, 0x61, 0x73, - 0x68, 0x22, 0x4f, 0x0a, 0x23, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x79, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, - 0x44, 0x73, 0x22, 0x75, 0x0a, 0x24, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x79, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x12, 0x4d, 0x0a, 0x0d, 0x63, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0c, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x15, 0x0a, + 0x13, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x22, 0x5e, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, + 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, + 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x73, + 0x65, 0x72, 0x49, 0x44, 0x22, 0x62, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, + 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x4b, 0x0a, 0x0c, 0x63, + 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x32, 0xdb, 0x0f, 0x0a, 0x0c, 0x63, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x88, 0x01, 0x0a, 0x17, 0x4d, - 0x6f, 0x64, 0x69, 0x66, 0x79, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x35, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x36, 0x2e, - 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, - 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, - 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x65, 0x6c, - 0x64, 0x52, 0x65, 0x73, 0x70, 0x12, 0x70, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, - 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x2e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x7c, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x41, 0x6c, - 0x6c, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x31, - 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, - 0x6c, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, - 0x71, 0x1a, 0x32, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x61, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x43, + 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x12, + 0x20, 0x0a, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, + 0x44, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x49, 0x44, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x22, 0x65, 0x0a, 0x14, 0x47, + 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x12, 0x4d, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x4f, 0x70, 0x65, + 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x22, 0x3a, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0b, + 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x22, 0x68, + 0x0a, 0x17, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x4d, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x27, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x76, 0x65, + 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x39, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x52, + 0x65, 0x63, 0x76, 0x4d, 0x73, 0x67, 0x4e, 0x6f, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, + 0x73, 0x65, 0x72, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x49, 0x44, 0x22, 0x3a, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x76, 0x4d, 0x73, + 0x67, 0x4e, 0x6f, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x73, 0x22, + 0x52, 0x0a, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x43, + 0x68, 0x61, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x63, 0x76, 0x49, 0x44, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x63, 0x76, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x73, + 0x65, 0x6e, 0x64, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x6e, + 0x64, 0x49, 0x44, 0x22, 0x23, 0x0a, 0x21, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x6e, + 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x55, 0x0a, 0x1f, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x68, 0x61, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, + 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x75, + 0x73, 0x65, 0x72, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, + 0x65, 0x72, 0x49, 0x44, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x22, + 0x22, 0x0a, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x68, + 0x61, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x22, 0x7c, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, 0x71, 0x12, + 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, + 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x77, + 0x6e, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x78, + 0x53, 0x65, 0x71, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x53, 0x65, + 0x71, 0x22, 0x1b, 0x0a, 0x19, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, 0x73, 0x70, 0x22, 0x2f, + 0x0a, 0x15, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, + 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x22, + 0x42, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x44, 0x73, 0x22, 0x7f, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x73, + 0x65, 0x72, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, + 0x72, 0x49, 0x44, 0x73, 0x12, 0x4e, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x4f, 0x70, 0x65, + 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x16, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, + 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x41, 0x0a, 0x1d, + 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, + 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x22, + 0x34, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x73, + 0x70, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x04, 0x68, 0x61, 0x73, 0x68, 0x22, 0x4f, 0x0a, 0x23, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x79, 0x43, 0x6f, 0x6e, 0x76, 0x65, + 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x12, 0x28, 0x0a, 0x0f, + 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x22, 0x75, 0x0a, 0x24, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x79, 0x43, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x12, 0x4d, + 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, + 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x32, 0xdf, 0x0c, + 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x70, + 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x2d, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, - 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, - 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x2f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, - 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x82, 0x01, 0x0a, 0x15, 0x42, - 0x61, 0x74, 0x63, 0x68, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x33, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x34, 0x2e, 0x4f, 0x70, 0x65, 0x6e, - 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x53, 0x65, 0x74, 0x43, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, - 0x70, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x2d, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, - 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x1a, 0x2e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, - 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, - 0x70, 0x12, 0x6a, 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x52, 0x65, 0x63, 0x76, 0x4d, 0x73, 0x67, 0x4f, - 0x70, 0x74, 0x12, 0x2b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, - 0x65, 0x74, 0x52, 0x65, 0x63, 0x76, 0x4d, 0x73, 0x67, 0x4f, 0x70, 0x74, 0x52, 0x65, 0x71, 0x1a, - 0x2c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, - 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x52, - 0x65, 0x63, 0x76, 0x4d, 0x73, 0x67, 0x4f, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x91, 0x01, - 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x76, 0x4d, 0x73, 0x67, 0x4e, 0x6f, 0x74, 0x4e, - 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x73, 0x12, 0x38, 0x2e, 0x4f, - 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x76, - 0x4d, 0x73, 0x67, 0x4e, 0x6f, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x73, 0x65, 0x72, - 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x39, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, + 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x1a, 0x2e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, + 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x12, 0x7c, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x32, 0x2e, 0x4f, 0x70, 0x65, + 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x73, + 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x2e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, + 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, + 0x65, 0x71, 0x1a, 0x2f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, + 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x12, 0x70, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x76, 0x4d, 0x73, 0x67, 0x4e, 0x6f, 0x74, - 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x12, 0x9a, 0x01, 0x0a, 0x1d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x6e, 0x67, - 0x6c, 0x65, 0x43, 0x68, 0x61, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x3b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x74, - 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, - 0x1a, 0x3c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, - 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x74, 0x43, 0x6f, 0x6e, - 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x97, - 0x01, 0x0a, 0x1c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x68, - 0x61, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, - 0x3a, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, - 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x68, 0x61, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, - 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x3b, 0x2e, 0x4f, 0x70, + 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x2e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x91, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, + 0x76, 0x4d, 0x73, 0x67, 0x4e, 0x6f, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x73, 0x65, + 0x72, 0x49, 0x44, 0x73, 0x12, 0x38, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x76, 0x4d, 0x73, 0x67, 0x4e, 0x6f, 0x74, 0x4e, 0x6f, + 0x74, 0x69, 0x66, 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x39, + 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, + 0x63, 0x76, 0x4d, 0x73, 0x67, 0x4e, 0x6f, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x73, + 0x65, 0x72, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x9a, 0x01, 0x0a, 0x1d, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x74, 0x43, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, - 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x43, 0x68, 0x61, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x82, 0x01, 0x0a, 0x15, 0x53, 0x65, 0x74, - 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, - 0x65, 0x71, 0x12, 0x33, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, - 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, - 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, 0x71, 0x1a, 0x34, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, + 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, + 0x6e, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x3c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, + 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x69, 0x6e, 0x67, 0x6c, + 0x65, 0x43, 0x68, 0x61, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x97, 0x01, 0x0a, 0x1c, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x68, 0x61, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3a, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, 0x73, 0x70, 0x12, 0x79, 0x0a, - 0x12, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x49, 0x44, 0x73, 0x12, 0x30, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, + 0x68, 0x61, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x71, 0x1a, 0x3b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, - 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, - 0x44, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x73, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x43, - 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2e, 0x2e, 0x4f, - 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x2f, 0x2e, 0x4f, - 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x91, 0x01, - 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x48, 0x61, 0x73, 0x68, 0x12, 0x38, 0x2e, 0x4f, - 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, - 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x48, - 0x61, 0x73, 0x68, 0x52, 0x65, 0x71, 0x1a, 0x39, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, - 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x73, - 0x70, 0x12, 0xa3, 0x01, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x79, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x3e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x42, 0x79, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x1a, 0x3f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x42, 0x79, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x44, 0x4b, 0x2f, - 0x4f, 0x70, 0x65, 0x6e, 0x2d, 0x49, 0x4d, 0x2d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x70, - 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x68, 0x61, 0x74, 0x43, + 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x12, 0x82, 0x01, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x12, 0x33, 0x2e, 0x4f, 0x70, 0x65, + 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, 0x71, 0x1a, + 0x34, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, + 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x43, + 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, + 0x71, 0x52, 0x65, 0x73, 0x70, 0x12, 0x79, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x12, 0x30, 0x2e, 0x4f, 0x70, + 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, + 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, + 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x31, 0x2e, + 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x12, 0x73, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x52, 0x65, 0x71, 0x1a, 0x2f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x91, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, + 0x72, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, + 0x48, 0x61, 0x73, 0x68, 0x12, 0x38, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x71, 0x1a, 0x39, + 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, + 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, + 0x73, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x12, 0xa3, 0x01, 0x0a, 0x20, 0x47, 0x65, + 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x79, + 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x3e, + 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x79, 0x43, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x1a, 0x3f, + 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x79, 0x43, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x42, + 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x70, + 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x44, 0x4b, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x2d, 0x49, 0x4d, 0x2d, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2127,95 +1834,83 @@ func file_conversation_conversation_proto_rawDescGZIP() []byte { return file_conversation_conversation_proto_rawDescData } -var file_conversation_conversation_proto_msgTypes = make([]protoimpl.MessageInfo, 32) +var file_conversation_conversation_proto_msgTypes = make([]protoimpl.MessageInfo, 26) var file_conversation_conversation_proto_goTypes = []interface{}{ (*Conversation)(nil), // 0: OpenIMServer.conversation.Conversation (*ConversationReq)(nil), // 1: OpenIMServer.conversation.ConversationReq - (*ModifyConversationFieldReq)(nil), // 2: OpenIMServer.conversation.ModifyConversationFieldReq - (*ModifyConversationFieldResp)(nil), // 3: OpenIMServer.conversation.ModifyConversationFieldResp - (*SetConversationReq)(nil), // 4: OpenIMServer.conversation.SetConversationReq - (*SetConversationResp)(nil), // 5: OpenIMServer.conversation.SetConversationResp - (*SetRecvMsgOptReq)(nil), // 6: OpenIMServer.conversation.SetRecvMsgOptReq - (*SetRecvMsgOptResp)(nil), // 7: OpenIMServer.conversation.SetRecvMsgOptResp - (*GetConversationReq)(nil), // 8: OpenIMServer.conversation.GetConversationReq - (*GetConversationResp)(nil), // 9: OpenIMServer.conversation.GetConversationResp - (*GetConversationsReq)(nil), // 10: OpenIMServer.conversation.GetConversationsReq - (*GetConversationsResp)(nil), // 11: OpenIMServer.conversation.GetConversationsResp - (*GetAllConversationsReq)(nil), // 12: OpenIMServer.conversation.GetAllConversationsReq - (*GetAllConversationsResp)(nil), // 13: OpenIMServer.conversation.GetAllConversationsResp - (*BatchSetConversationsReq)(nil), // 14: OpenIMServer.conversation.BatchSetConversationsReq - (*BatchSetConversationsResp)(nil), // 15: OpenIMServer.conversation.BatchSetConversationsResp - (*GetRecvMsgNotNotifyUserIDsReq)(nil), // 16: OpenIMServer.conversation.GetRecvMsgNotNotifyUserIDsReq - (*GetRecvMsgNotNotifyUserIDsResp)(nil), // 17: OpenIMServer.conversation.GetRecvMsgNotNotifyUserIDsResp - (*CreateSingleChatConversationsReq)(nil), // 18: OpenIMServer.conversation.CreateSingleChatConversationsReq - (*CreateSingleChatConversationsResp)(nil), // 19: OpenIMServer.conversation.CreateSingleChatConversationsResp - (*CreateGroupChatConversationsReq)(nil), // 20: OpenIMServer.conversation.CreateGroupChatConversationsReq - (*CreateGroupChatConversationsResp)(nil), // 21: OpenIMServer.conversation.CreateGroupChatConversationsResp - (*SetConversationMaxSeqReq)(nil), // 22: OpenIMServer.conversation.SetConversationMaxSeqReq - (*SetConversationMaxSeqResp)(nil), // 23: OpenIMServer.conversation.SetConversationMaxSeqResp - (*GetConversationIDsReq)(nil), // 24: OpenIMServer.conversation.GetConversationIDsReq - (*GetConversationIDsResp)(nil), // 25: OpenIMServer.conversation.GetConversationIDsResp - (*SetConversationsReq)(nil), // 26: OpenIMServer.conversation.SetConversationsReq - (*SetConversationsResp)(nil), // 27: OpenIMServer.conversation.SetConversationsResp - (*GetUserConversationIDsHashReq)(nil), // 28: OpenIMServer.conversation.GetUserConversationIDsHashReq - (*GetUserConversationIDsHashResp)(nil), // 29: OpenIMServer.conversation.GetUserConversationIDsHashResp - (*GetConversationsByConversationIDReq)(nil), // 30: OpenIMServer.conversation.GetConversationsByConversationIDReq - (*GetConversationsByConversationIDResp)(nil), // 31: OpenIMServer.conversation.GetConversationsByConversationIDResp - (*wrapperspb.Int32Value)(nil), // 32: OpenIMServer.protobuf.Int32Value - (*wrapperspb.BoolValue)(nil), // 33: OpenIMServer.protobuf.BoolValue - (*wrapperspb.StringValue)(nil), // 34: OpenIMServer.protobuf.StringValue - (*wrapperspb.Int64Value)(nil), // 35: OpenIMServer.protobuf.Int64Value + (*SetConversationReq)(nil), // 2: OpenIMServer.conversation.SetConversationReq + (*SetConversationResp)(nil), // 3: OpenIMServer.conversation.SetConversationResp + (*GetConversationReq)(nil), // 4: OpenIMServer.conversation.GetConversationReq + (*GetConversationResp)(nil), // 5: OpenIMServer.conversation.GetConversationResp + (*GetConversationsReq)(nil), // 6: OpenIMServer.conversation.GetConversationsReq + (*GetConversationsResp)(nil), // 7: OpenIMServer.conversation.GetConversationsResp + (*GetAllConversationsReq)(nil), // 8: OpenIMServer.conversation.GetAllConversationsReq + (*GetAllConversationsResp)(nil), // 9: OpenIMServer.conversation.GetAllConversationsResp + (*GetRecvMsgNotNotifyUserIDsReq)(nil), // 10: OpenIMServer.conversation.GetRecvMsgNotNotifyUserIDsReq + (*GetRecvMsgNotNotifyUserIDsResp)(nil), // 11: OpenIMServer.conversation.GetRecvMsgNotNotifyUserIDsResp + (*CreateSingleChatConversationsReq)(nil), // 12: OpenIMServer.conversation.CreateSingleChatConversationsReq + (*CreateSingleChatConversationsResp)(nil), // 13: OpenIMServer.conversation.CreateSingleChatConversationsResp + (*CreateGroupChatConversationsReq)(nil), // 14: OpenIMServer.conversation.CreateGroupChatConversationsReq + (*CreateGroupChatConversationsResp)(nil), // 15: OpenIMServer.conversation.CreateGroupChatConversationsResp + (*SetConversationMaxSeqReq)(nil), // 16: OpenIMServer.conversation.SetConversationMaxSeqReq + (*SetConversationMaxSeqResp)(nil), // 17: OpenIMServer.conversation.SetConversationMaxSeqResp + (*GetConversationIDsReq)(nil), // 18: OpenIMServer.conversation.GetConversationIDsReq + (*GetConversationIDsResp)(nil), // 19: OpenIMServer.conversation.GetConversationIDsResp + (*SetConversationsReq)(nil), // 20: OpenIMServer.conversation.SetConversationsReq + (*SetConversationsResp)(nil), // 21: OpenIMServer.conversation.SetConversationsResp + (*GetUserConversationIDsHashReq)(nil), // 22: OpenIMServer.conversation.GetUserConversationIDsHashReq + (*GetUserConversationIDsHashResp)(nil), // 23: OpenIMServer.conversation.GetUserConversationIDsHashResp + (*GetConversationsByConversationIDReq)(nil), // 24: OpenIMServer.conversation.GetConversationsByConversationIDReq + (*GetConversationsByConversationIDResp)(nil), // 25: OpenIMServer.conversation.GetConversationsByConversationIDResp + (*wrapperspb.Int32Value)(nil), // 26: OpenIMServer.protobuf.Int32Value + (*wrapperspb.BoolValue)(nil), // 27: OpenIMServer.protobuf.BoolValue + (*wrapperspb.StringValue)(nil), // 28: OpenIMServer.protobuf.StringValue + (*wrapperspb.Int64Value)(nil), // 29: OpenIMServer.protobuf.Int64Value } var file_conversation_conversation_proto_depIdxs = []int32{ - 32, // 0: OpenIMServer.conversation.ConversationReq.recvMsgOpt:type_name -> OpenIMServer.protobuf.Int32Value - 33, // 1: OpenIMServer.conversation.ConversationReq.isPinned:type_name -> OpenIMServer.protobuf.BoolValue - 34, // 2: OpenIMServer.conversation.ConversationReq.attachedInfo:type_name -> OpenIMServer.protobuf.StringValue - 33, // 3: OpenIMServer.conversation.ConversationReq.isPrivateChat:type_name -> OpenIMServer.protobuf.BoolValue - 34, // 4: OpenIMServer.conversation.ConversationReq.ex:type_name -> OpenIMServer.protobuf.StringValue - 32, // 5: OpenIMServer.conversation.ConversationReq.burnDuration:type_name -> OpenIMServer.protobuf.Int32Value - 35, // 6: OpenIMServer.conversation.ConversationReq.minSeq:type_name -> OpenIMServer.protobuf.Int64Value - 35, // 7: OpenIMServer.conversation.ConversationReq.maxSeq:type_name -> OpenIMServer.protobuf.Int64Value - 32, // 8: OpenIMServer.conversation.ConversationReq.groupAtType:type_name -> OpenIMServer.protobuf.Int32Value - 0, // 9: OpenIMServer.conversation.ModifyConversationFieldReq.conversation:type_name -> OpenIMServer.conversation.Conversation - 0, // 10: OpenIMServer.conversation.SetConversationReq.conversation:type_name -> OpenIMServer.conversation.Conversation - 0, // 11: OpenIMServer.conversation.GetConversationResp.conversation:type_name -> OpenIMServer.conversation.Conversation - 0, // 12: OpenIMServer.conversation.GetConversationsResp.conversations:type_name -> OpenIMServer.conversation.Conversation - 0, // 13: OpenIMServer.conversation.GetAllConversationsResp.conversations:type_name -> OpenIMServer.conversation.Conversation - 0, // 14: OpenIMServer.conversation.BatchSetConversationsReq.conversations:type_name -> OpenIMServer.conversation.Conversation + 26, // 0: OpenIMServer.conversation.ConversationReq.recvMsgOpt:type_name -> OpenIMServer.protobuf.Int32Value + 27, // 1: OpenIMServer.conversation.ConversationReq.isPinned:type_name -> OpenIMServer.protobuf.BoolValue + 28, // 2: OpenIMServer.conversation.ConversationReq.attachedInfo:type_name -> OpenIMServer.protobuf.StringValue + 27, // 3: OpenIMServer.conversation.ConversationReq.isPrivateChat:type_name -> OpenIMServer.protobuf.BoolValue + 28, // 4: OpenIMServer.conversation.ConversationReq.ex:type_name -> OpenIMServer.protobuf.StringValue + 26, // 5: OpenIMServer.conversation.ConversationReq.burnDuration:type_name -> OpenIMServer.protobuf.Int32Value + 29, // 6: OpenIMServer.conversation.ConversationReq.minSeq:type_name -> OpenIMServer.protobuf.Int64Value + 29, // 7: OpenIMServer.conversation.ConversationReq.maxSeq:type_name -> OpenIMServer.protobuf.Int64Value + 26, // 8: OpenIMServer.conversation.ConversationReq.groupAtType:type_name -> OpenIMServer.protobuf.Int32Value + 29, // 9: OpenIMServer.conversation.ConversationReq.msgDestructTime:type_name -> OpenIMServer.protobuf.Int64Value + 27, // 10: OpenIMServer.conversation.ConversationReq.isMsgDestruct:type_name -> OpenIMServer.protobuf.BoolValue + 0, // 11: OpenIMServer.conversation.SetConversationReq.conversation:type_name -> OpenIMServer.conversation.Conversation + 0, // 12: OpenIMServer.conversation.GetConversationResp.conversation:type_name -> OpenIMServer.conversation.Conversation + 0, // 13: OpenIMServer.conversation.GetConversationsResp.conversations:type_name -> OpenIMServer.conversation.Conversation + 0, // 14: OpenIMServer.conversation.GetAllConversationsResp.conversations:type_name -> OpenIMServer.conversation.Conversation 1, // 15: OpenIMServer.conversation.SetConversationsReq.conversation:type_name -> OpenIMServer.conversation.ConversationReq 0, // 16: OpenIMServer.conversation.GetConversationsByConversationIDResp.conversations:type_name -> OpenIMServer.conversation.Conversation - 2, // 17: OpenIMServer.conversation.conversation.ModifyConversationField:input_type -> OpenIMServer.conversation.ModifyConversationFieldReq - 8, // 18: OpenIMServer.conversation.conversation.GetConversation:input_type -> OpenIMServer.conversation.GetConversationReq - 12, // 19: OpenIMServer.conversation.conversation.GetAllConversations:input_type -> OpenIMServer.conversation.GetAllConversationsReq - 10, // 20: OpenIMServer.conversation.conversation.GetConversations:input_type -> OpenIMServer.conversation.GetConversationsReq - 14, // 21: OpenIMServer.conversation.conversation.BatchSetConversations:input_type -> OpenIMServer.conversation.BatchSetConversationsReq - 4, // 22: OpenIMServer.conversation.conversation.SetConversation:input_type -> OpenIMServer.conversation.SetConversationReq - 6, // 23: OpenIMServer.conversation.conversation.SetRecvMsgOpt:input_type -> OpenIMServer.conversation.SetRecvMsgOptReq - 16, // 24: OpenIMServer.conversation.conversation.GetRecvMsgNotNotifyUserIDs:input_type -> OpenIMServer.conversation.GetRecvMsgNotNotifyUserIDsReq - 18, // 25: OpenIMServer.conversation.conversation.CreateSingleChatConversations:input_type -> OpenIMServer.conversation.CreateSingleChatConversationsReq - 20, // 26: OpenIMServer.conversation.conversation.CreateGroupChatConversations:input_type -> OpenIMServer.conversation.CreateGroupChatConversationsReq - 22, // 27: OpenIMServer.conversation.conversation.SetConversationMaxSeq:input_type -> OpenIMServer.conversation.SetConversationMaxSeqReq - 24, // 28: OpenIMServer.conversation.conversation.GetConversationIDs:input_type -> OpenIMServer.conversation.GetConversationIDsReq - 26, // 29: OpenIMServer.conversation.conversation.SetConversations:input_type -> OpenIMServer.conversation.SetConversationsReq - 28, // 30: OpenIMServer.conversation.conversation.GetUserConversationIDsHash:input_type -> OpenIMServer.conversation.GetUserConversationIDsHashReq - 30, // 31: OpenIMServer.conversation.conversation.GetConversationsByConversationID:input_type -> OpenIMServer.conversation.GetConversationsByConversationIDReq - 3, // 32: OpenIMServer.conversation.conversation.ModifyConversationField:output_type -> OpenIMServer.conversation.ModifyConversationFieldResp - 9, // 33: OpenIMServer.conversation.conversation.GetConversation:output_type -> OpenIMServer.conversation.GetConversationResp - 13, // 34: OpenIMServer.conversation.conversation.GetAllConversations:output_type -> OpenIMServer.conversation.GetAllConversationsResp - 11, // 35: OpenIMServer.conversation.conversation.GetConversations:output_type -> OpenIMServer.conversation.GetConversationsResp - 15, // 36: OpenIMServer.conversation.conversation.BatchSetConversations:output_type -> OpenIMServer.conversation.BatchSetConversationsResp - 5, // 37: OpenIMServer.conversation.conversation.SetConversation:output_type -> OpenIMServer.conversation.SetConversationResp - 7, // 38: OpenIMServer.conversation.conversation.SetRecvMsgOpt:output_type -> OpenIMServer.conversation.SetRecvMsgOptResp - 17, // 39: OpenIMServer.conversation.conversation.GetRecvMsgNotNotifyUserIDs:output_type -> OpenIMServer.conversation.GetRecvMsgNotNotifyUserIDsResp - 19, // 40: OpenIMServer.conversation.conversation.CreateSingleChatConversations:output_type -> OpenIMServer.conversation.CreateSingleChatConversationsResp - 21, // 41: OpenIMServer.conversation.conversation.CreateGroupChatConversations:output_type -> OpenIMServer.conversation.CreateGroupChatConversationsResp - 23, // 42: OpenIMServer.conversation.conversation.SetConversationMaxSeq:output_type -> OpenIMServer.conversation.SetConversationMaxSeqResp - 25, // 43: OpenIMServer.conversation.conversation.GetConversationIDs:output_type -> OpenIMServer.conversation.GetConversationIDsResp - 27, // 44: OpenIMServer.conversation.conversation.SetConversations:output_type -> OpenIMServer.conversation.SetConversationsResp - 29, // 45: OpenIMServer.conversation.conversation.GetUserConversationIDsHash:output_type -> OpenIMServer.conversation.GetUserConversationIDsHashResp - 31, // 46: OpenIMServer.conversation.conversation.GetConversationsByConversationID:output_type -> OpenIMServer.conversation.GetConversationsByConversationIDResp - 32, // [32:47] is the sub-list for method output_type - 17, // [17:32] is the sub-list for method input_type + 4, // 17: OpenIMServer.conversation.conversation.GetConversation:input_type -> OpenIMServer.conversation.GetConversationReq + 8, // 18: OpenIMServer.conversation.conversation.GetAllConversations:input_type -> OpenIMServer.conversation.GetAllConversationsReq + 6, // 19: OpenIMServer.conversation.conversation.GetConversations:input_type -> OpenIMServer.conversation.GetConversationsReq + 2, // 20: OpenIMServer.conversation.conversation.SetConversation:input_type -> OpenIMServer.conversation.SetConversationReq + 10, // 21: OpenIMServer.conversation.conversation.GetRecvMsgNotNotifyUserIDs:input_type -> OpenIMServer.conversation.GetRecvMsgNotNotifyUserIDsReq + 12, // 22: OpenIMServer.conversation.conversation.CreateSingleChatConversations:input_type -> OpenIMServer.conversation.CreateSingleChatConversationsReq + 14, // 23: OpenIMServer.conversation.conversation.CreateGroupChatConversations:input_type -> OpenIMServer.conversation.CreateGroupChatConversationsReq + 16, // 24: OpenIMServer.conversation.conversation.SetConversationMaxSeq:input_type -> OpenIMServer.conversation.SetConversationMaxSeqReq + 18, // 25: OpenIMServer.conversation.conversation.GetConversationIDs:input_type -> OpenIMServer.conversation.GetConversationIDsReq + 20, // 26: OpenIMServer.conversation.conversation.SetConversations:input_type -> OpenIMServer.conversation.SetConversationsReq + 22, // 27: OpenIMServer.conversation.conversation.GetUserConversationIDsHash:input_type -> OpenIMServer.conversation.GetUserConversationIDsHashReq + 24, // 28: OpenIMServer.conversation.conversation.GetConversationsByConversationID:input_type -> OpenIMServer.conversation.GetConversationsByConversationIDReq + 5, // 29: OpenIMServer.conversation.conversation.GetConversation:output_type -> OpenIMServer.conversation.GetConversationResp + 9, // 30: OpenIMServer.conversation.conversation.GetAllConversations:output_type -> OpenIMServer.conversation.GetAllConversationsResp + 7, // 31: OpenIMServer.conversation.conversation.GetConversations:output_type -> OpenIMServer.conversation.GetConversationsResp + 3, // 32: OpenIMServer.conversation.conversation.SetConversation:output_type -> OpenIMServer.conversation.SetConversationResp + 11, // 33: OpenIMServer.conversation.conversation.GetRecvMsgNotNotifyUserIDs:output_type -> OpenIMServer.conversation.GetRecvMsgNotNotifyUserIDsResp + 13, // 34: OpenIMServer.conversation.conversation.CreateSingleChatConversations:output_type -> OpenIMServer.conversation.CreateSingleChatConversationsResp + 15, // 35: OpenIMServer.conversation.conversation.CreateGroupChatConversations:output_type -> OpenIMServer.conversation.CreateGroupChatConversationsResp + 17, // 36: OpenIMServer.conversation.conversation.SetConversationMaxSeq:output_type -> OpenIMServer.conversation.SetConversationMaxSeqResp + 19, // 37: OpenIMServer.conversation.conversation.GetConversationIDs:output_type -> OpenIMServer.conversation.GetConversationIDsResp + 21, // 38: OpenIMServer.conversation.conversation.SetConversations:output_type -> OpenIMServer.conversation.SetConversationsResp + 23, // 39: OpenIMServer.conversation.conversation.GetUserConversationIDsHash:output_type -> OpenIMServer.conversation.GetUserConversationIDsHashResp + 25, // 40: OpenIMServer.conversation.conversation.GetConversationsByConversationID:output_type -> OpenIMServer.conversation.GetConversationsByConversationIDResp + 29, // [29:41] is the sub-list for method output_type + 17, // [17:29] is the sub-list for method input_type 17, // [17:17] is the sub-list for extension type_name 17, // [17:17] is the sub-list for extension extendee 0, // [0:17] is the sub-list for field type_name @@ -2252,30 +1947,6 @@ func file_conversation_conversation_proto_init() { } } file_conversation_conversation_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ModifyConversationFieldReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_conversation_conversation_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ModifyConversationFieldResp); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_conversation_conversation_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SetConversationReq); i { case 0: return &v.state @@ -2287,7 +1958,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SetConversationResp); i { case 0: return &v.state @@ -2299,31 +1970,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetRecvMsgOptReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_conversation_conversation_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetRecvMsgOptResp); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_conversation_conversation_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetConversationReq); i { case 0: return &v.state @@ -2335,7 +1982,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetConversationResp); i { case 0: return &v.state @@ -2347,7 +1994,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetConversationsReq); i { case 0: return &v.state @@ -2359,7 +2006,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetConversationsResp); i { case 0: return &v.state @@ -2371,7 +2018,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetAllConversationsReq); i { case 0: return &v.state @@ -2383,7 +2030,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetAllConversationsResp); i { case 0: return &v.state @@ -2395,31 +2042,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BatchSetConversationsReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_conversation_conversation_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BatchSetConversationsResp); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_conversation_conversation_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetRecvMsgNotNotifyUserIDsReq); i { case 0: return &v.state @@ -2431,7 +2054,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetRecvMsgNotNotifyUserIDsResp); i { case 0: return &v.state @@ -2443,7 +2066,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateSingleChatConversationsReq); i { case 0: return &v.state @@ -2455,7 +2078,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateSingleChatConversationsResp); i { case 0: return &v.state @@ -2467,7 +2090,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateGroupChatConversationsReq); i { case 0: return &v.state @@ -2479,7 +2102,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateGroupChatConversationsResp); i { case 0: return &v.state @@ -2491,7 +2114,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SetConversationMaxSeqReq); i { case 0: return &v.state @@ -2503,7 +2126,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SetConversationMaxSeqResp); i { case 0: return &v.state @@ -2515,7 +2138,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetConversationIDsReq); i { case 0: return &v.state @@ -2527,7 +2150,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetConversationIDsResp); i { case 0: return &v.state @@ -2539,7 +2162,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SetConversationsReq); i { case 0: return &v.state @@ -2551,7 +2174,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SetConversationsResp); i { case 0: return &v.state @@ -2563,7 +2186,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetUserConversationIDsHashReq); i { case 0: return &v.state @@ -2575,7 +2198,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetUserConversationIDsHashResp); i { case 0: return &v.state @@ -2587,7 +2210,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetConversationsByConversationIDReq); i { case 0: return &v.state @@ -2599,7 +2222,7 @@ func file_conversation_conversation_proto_init() { return nil } } - file_conversation_conversation_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + file_conversation_conversation_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetConversationsByConversationIDResp); i { case 0: return &v.state @@ -2618,7 +2241,7 @@ func file_conversation_conversation_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_conversation_conversation_proto_rawDesc, NumEnums: 0, - NumMessages: 32, + NumMessages: 26, NumExtensions: 0, NumServices: 1, }, @@ -2644,13 +2267,10 @@ const _ = grpc.SupportPackageIsVersion6 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type ConversationClient interface { - ModifyConversationField(ctx context.Context, in *ModifyConversationFieldReq, opts ...grpc.CallOption) (*ModifyConversationFieldResp, error) GetConversation(ctx context.Context, in *GetConversationReq, opts ...grpc.CallOption) (*GetConversationResp, error) GetAllConversations(ctx context.Context, in *GetAllConversationsReq, opts ...grpc.CallOption) (*GetAllConversationsResp, error) GetConversations(ctx context.Context, in *GetConversationsReq, opts ...grpc.CallOption) (*GetConversationsResp, error) - BatchSetConversations(ctx context.Context, in *BatchSetConversationsReq, opts ...grpc.CallOption) (*BatchSetConversationsResp, error) SetConversation(ctx context.Context, in *SetConversationReq, opts ...grpc.CallOption) (*SetConversationResp, error) - SetRecvMsgOpt(ctx context.Context, in *SetRecvMsgOptReq, opts ...grpc.CallOption) (*SetRecvMsgOptResp, error) GetRecvMsgNotNotifyUserIDs(ctx context.Context, in *GetRecvMsgNotNotifyUserIDsReq, opts ...grpc.CallOption) (*GetRecvMsgNotNotifyUserIDsResp, error) CreateSingleChatConversations(ctx context.Context, in *CreateSingleChatConversationsReq, opts ...grpc.CallOption) (*CreateSingleChatConversationsResp, error) CreateGroupChatConversations(ctx context.Context, in *CreateGroupChatConversationsReq, opts ...grpc.CallOption) (*CreateGroupChatConversationsResp, error) @@ -2669,15 +2289,6 @@ func NewConversationClient(cc grpc.ClientConnInterface) ConversationClient { return &conversationClient{cc} } -func (c *conversationClient) ModifyConversationField(ctx context.Context, in *ModifyConversationFieldReq, opts ...grpc.CallOption) (*ModifyConversationFieldResp, error) { - out := new(ModifyConversationFieldResp) - err := c.cc.Invoke(ctx, "/OpenIMServer.conversation.conversation/ModifyConversationField", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *conversationClient) GetConversation(ctx context.Context, in *GetConversationReq, opts ...grpc.CallOption) (*GetConversationResp, error) { out := new(GetConversationResp) err := c.cc.Invoke(ctx, "/OpenIMServer.conversation.conversation/GetConversation", in, out, opts...) @@ -2705,15 +2316,6 @@ func (c *conversationClient) GetConversations(ctx context.Context, in *GetConver return out, nil } -func (c *conversationClient) BatchSetConversations(ctx context.Context, in *BatchSetConversationsReq, opts ...grpc.CallOption) (*BatchSetConversationsResp, error) { - out := new(BatchSetConversationsResp) - err := c.cc.Invoke(ctx, "/OpenIMServer.conversation.conversation/BatchSetConversations", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *conversationClient) SetConversation(ctx context.Context, in *SetConversationReq, opts ...grpc.CallOption) (*SetConversationResp, error) { out := new(SetConversationResp) err := c.cc.Invoke(ctx, "/OpenIMServer.conversation.conversation/SetConversation", in, out, opts...) @@ -2723,15 +2325,6 @@ func (c *conversationClient) SetConversation(ctx context.Context, in *SetConvers return out, nil } -func (c *conversationClient) SetRecvMsgOpt(ctx context.Context, in *SetRecvMsgOptReq, opts ...grpc.CallOption) (*SetRecvMsgOptResp, error) { - out := new(SetRecvMsgOptResp) - err := c.cc.Invoke(ctx, "/OpenIMServer.conversation.conversation/SetRecvMsgOpt", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *conversationClient) GetRecvMsgNotNotifyUserIDs(ctx context.Context, in *GetRecvMsgNotNotifyUserIDsReq, opts ...grpc.CallOption) (*GetRecvMsgNotNotifyUserIDsResp, error) { out := new(GetRecvMsgNotNotifyUserIDsResp) err := c.cc.Invoke(ctx, "/OpenIMServer.conversation.conversation/GetRecvMsgNotNotifyUserIDs", in, out, opts...) @@ -2806,13 +2399,10 @@ func (c *conversationClient) GetConversationsByConversationID(ctx context.Contex // ConversationServer is the server API for Conversation service. type ConversationServer interface { - ModifyConversationField(context.Context, *ModifyConversationFieldReq) (*ModifyConversationFieldResp, error) GetConversation(context.Context, *GetConversationReq) (*GetConversationResp, error) GetAllConversations(context.Context, *GetAllConversationsReq) (*GetAllConversationsResp, error) GetConversations(context.Context, *GetConversationsReq) (*GetConversationsResp, error) - BatchSetConversations(context.Context, *BatchSetConversationsReq) (*BatchSetConversationsResp, error) SetConversation(context.Context, *SetConversationReq) (*SetConversationResp, error) - SetRecvMsgOpt(context.Context, *SetRecvMsgOptReq) (*SetRecvMsgOptResp, error) GetRecvMsgNotNotifyUserIDs(context.Context, *GetRecvMsgNotNotifyUserIDsReq) (*GetRecvMsgNotNotifyUserIDsResp, error) CreateSingleChatConversations(context.Context, *CreateSingleChatConversationsReq) (*CreateSingleChatConversationsResp, error) CreateGroupChatConversations(context.Context, *CreateGroupChatConversationsReq) (*CreateGroupChatConversationsResp, error) @@ -2827,9 +2417,6 @@ type ConversationServer interface { type UnimplementedConversationServer struct { } -func (*UnimplementedConversationServer) ModifyConversationField(context.Context, *ModifyConversationFieldReq) (*ModifyConversationFieldResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method ModifyConversationField not implemented") -} func (*UnimplementedConversationServer) GetConversation(context.Context, *GetConversationReq) (*GetConversationResp, error) { return nil, status.Errorf(codes.Unimplemented, "method GetConversation not implemented") } @@ -2839,15 +2426,9 @@ func (*UnimplementedConversationServer) GetAllConversations(context.Context, *Ge func (*UnimplementedConversationServer) GetConversations(context.Context, *GetConversationsReq) (*GetConversationsResp, error) { return nil, status.Errorf(codes.Unimplemented, "method GetConversations not implemented") } -func (*UnimplementedConversationServer) BatchSetConversations(context.Context, *BatchSetConversationsReq) (*BatchSetConversationsResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method BatchSetConversations not implemented") -} func (*UnimplementedConversationServer) SetConversation(context.Context, *SetConversationReq) (*SetConversationResp, error) { return nil, status.Errorf(codes.Unimplemented, "method SetConversation not implemented") } -func (*UnimplementedConversationServer) SetRecvMsgOpt(context.Context, *SetRecvMsgOptReq) (*SetRecvMsgOptResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method SetRecvMsgOpt not implemented") -} func (*UnimplementedConversationServer) GetRecvMsgNotNotifyUserIDs(context.Context, *GetRecvMsgNotNotifyUserIDsReq) (*GetRecvMsgNotNotifyUserIDsResp, error) { return nil, status.Errorf(codes.Unimplemented, "method GetRecvMsgNotNotifyUserIDs not implemented") } @@ -2877,24 +2458,6 @@ func RegisterConversationServer(s *grpc.Server, srv ConversationServer) { s.RegisterService(&_Conversation_serviceDesc, srv) } -func _Conversation_ModifyConversationField_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ModifyConversationFieldReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ConversationServer).ModifyConversationField(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/OpenIMServer.conversation.conversation/ModifyConversationField", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ConversationServer).ModifyConversationField(ctx, req.(*ModifyConversationFieldReq)) - } - return interceptor(ctx, in, info, handler) -} - func _Conversation_GetConversation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetConversationReq) if err := dec(in); err != nil { @@ -2949,24 +2512,6 @@ func _Conversation_GetConversations_Handler(srv interface{}, ctx context.Context return interceptor(ctx, in, info, handler) } -func _Conversation_BatchSetConversations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(BatchSetConversationsReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ConversationServer).BatchSetConversations(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/OpenIMServer.conversation.conversation/BatchSetConversations", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ConversationServer).BatchSetConversations(ctx, req.(*BatchSetConversationsReq)) - } - return interceptor(ctx, in, info, handler) -} - func _Conversation_SetConversation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(SetConversationReq) if err := dec(in); err != nil { @@ -2985,24 +2530,6 @@ func _Conversation_SetConversation_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } -func _Conversation_SetRecvMsgOpt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SetRecvMsgOptReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ConversationServer).SetRecvMsgOpt(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/OpenIMServer.conversation.conversation/SetRecvMsgOpt", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ConversationServer).SetRecvMsgOpt(ctx, req.(*SetRecvMsgOptReq)) - } - return interceptor(ctx, in, info, handler) -} - func _Conversation_GetRecvMsgNotNotifyUserIDs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetRecvMsgNotNotifyUserIDsReq) if err := dec(in); err != nil { @@ -3151,10 +2678,6 @@ var _Conversation_serviceDesc = grpc.ServiceDesc{ ServiceName: "OpenIMServer.conversation.conversation", HandlerType: (*ConversationServer)(nil), Methods: []grpc.MethodDesc{ - { - MethodName: "ModifyConversationField", - Handler: _Conversation_ModifyConversationField_Handler, - }, { MethodName: "GetConversation", Handler: _Conversation_GetConversation_Handler, @@ -3167,18 +2690,10 @@ var _Conversation_serviceDesc = grpc.ServiceDesc{ MethodName: "GetConversations", Handler: _Conversation_GetConversations_Handler, }, - { - MethodName: "BatchSetConversations", - Handler: _Conversation_BatchSetConversations_Handler, - }, { MethodName: "SetConversation", Handler: _Conversation_SetConversation_Handler, }, - { - MethodName: "SetRecvMsgOpt", - Handler: _Conversation_SetRecvMsgOpt_Handler, - }, { MethodName: "GetRecvMsgNotNotifyUserIDs", Handler: _Conversation_GetRecvMsgNotNotifyUserIDs_Handler, diff --git a/pkg/proto/conversation/conversation.proto b/pkg/proto/conversation/conversation.proto index b08366af7..0a271d525 100644 --- a/pkg/proto/conversation/conversation.proto +++ b/pkg/proto/conversation/conversation.proto @@ -32,6 +32,9 @@ message Conversation{ int32 burnDuration = 12; int64 minSeq = 13; int64 maxSeq = 14; + int64 msgDestructTime = 15; + int64 latestMsgDestructTime = 16; + bool isMsgDestruct = 17; } message ConversationReq{ @@ -48,15 +51,8 @@ message ConversationReq{ OpenIMServer.protobuf.Int64Value minSeq = 11; OpenIMServer.protobuf.Int64Value maxSeq = 12; OpenIMServer.protobuf.Int32Value groupAtType = 13; -} - -message ModifyConversationFieldReq{ - repeated string userIDList = 1; - int32 FieldType = 2; - Conversation conversation = 3; -} - -message ModifyConversationFieldResp{ + OpenIMServer.protobuf.Int64Value msgDestructTime = 14; + OpenIMServer.protobuf.BoolValue isMsgDestruct = 15; } message SetConversationReq{ @@ -66,15 +62,6 @@ message SetConversationReq{ message SetConversationResp{ } -message SetRecvMsgOptReq { - string ownerUserID = 1; - string conversationID = 2; - int32 recvMsgOpt = 3; -} - -message SetRecvMsgOptResp { -} - message GetConversationReq{ string conversationID = 1; string ownerUserID = 2; @@ -101,13 +88,6 @@ message GetAllConversationsResp{ repeated Conversation conversations = 2; } -message BatchSetConversationsReq{ - repeated Conversation conversations = 1; - string ownerUserID = 2; -} - -message BatchSetConversationsResp{ -} message GetRecvMsgNotNotifyUserIDsReq { string groupID = 1; @@ -177,13 +157,10 @@ message GetConversationsByConversationIDResp { } service conversation { - rpc ModifyConversationField(ModifyConversationFieldReq)returns(ModifyConversationFieldResp); rpc GetConversation(GetConversationReq)returns(GetConversationResp); rpc GetAllConversations(GetAllConversationsReq)returns(GetAllConversationsResp); rpc GetConversations(GetConversationsReq)returns(GetConversationsResp); - rpc BatchSetConversations(BatchSetConversationsReq)returns(BatchSetConversationsResp); rpc SetConversation(SetConversationReq)returns(SetConversationResp); - rpc SetRecvMsgOpt(SetRecvMsgOptReq)returns(SetRecvMsgOptResp); rpc GetRecvMsgNotNotifyUserIDs(GetRecvMsgNotNotifyUserIDsReq) returns (GetRecvMsgNotNotifyUserIDsResp); rpc CreateSingleChatConversations(CreateSingleChatConversationsReq) returns (CreateSingleChatConversationsResp); rpc CreateGroupChatConversations(CreateGroupChatConversationsReq) returns (CreateGroupChatConversationsResp); diff --git a/pkg/proto/group/group.pb.go b/pkg/proto/group/group.pb.go index f54e54a9e..9541496e2 100644 --- a/pkg/proto/group/group.pb.go +++ b/pkg/proto/group/group.pb.go @@ -3397,6 +3397,124 @@ func (x *GetGroupMemberCacheResp) GetMember() *sdkws.GroupMemberFullInfo { return nil } +type GroupCreateCountReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Start int64 `protobuf:"varint,1,opt,name=start,proto3" json:"start"` + End int64 `protobuf:"varint,2,opt,name=end,proto3" json:"end"` +} + +func (x *GroupCreateCountReq) Reset() { + *x = GroupCreateCountReq{} + if protoimpl.UnsafeEnabled { + mi := &file_group_group_proto_msgTypes[66] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupCreateCountReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupCreateCountReq) ProtoMessage() {} + +func (x *GroupCreateCountReq) ProtoReflect() protoreflect.Message { + mi := &file_group_group_proto_msgTypes[66] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GroupCreateCountReq.ProtoReflect.Descriptor instead. +func (*GroupCreateCountReq) Descriptor() ([]byte, []int) { + return file_group_group_proto_rawDescGZIP(), []int{66} +} + +func (x *GroupCreateCountReq) GetStart() int64 { + if x != nil { + return x.Start + } + return 0 +} + +func (x *GroupCreateCountReq) GetEnd() int64 { + if x != nil { + return x.End + } + return 0 +} + +type GroupCreateCountResp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Total int64 `protobuf:"varint,1,opt,name=total,proto3" json:"total"` + Before int64 `protobuf:"varint,2,opt,name=before,proto3" json:"before"` + Count map[string]int64 `protobuf:"bytes,3,rep,name=count,proto3" json:"count" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` +} + +func (x *GroupCreateCountResp) Reset() { + *x = GroupCreateCountResp{} + if protoimpl.UnsafeEnabled { + mi := &file_group_group_proto_msgTypes[67] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupCreateCountResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupCreateCountResp) ProtoMessage() {} + +func (x *GroupCreateCountResp) ProtoReflect() protoreflect.Message { + mi := &file_group_group_proto_msgTypes[67] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GroupCreateCountResp.ProtoReflect.Descriptor instead. +func (*GroupCreateCountResp) Descriptor() ([]byte, []int) { + return file_group_group_proto_rawDescGZIP(), []int{67} +} + +func (x *GroupCreateCountResp) GetTotal() int64 { + if x != nil { + return x.Total + } + return 0 +} + +func (x *GroupCreateCountResp) GetBefore() int64 { + if x != nil { + return x.Before + } + return 0 +} + +func (x *GroupCreateCountResp) GetCount() map[string]int64 { + if x != nil { + return x.Count + } + return nil +} + var File_group_group_proto protoreflect.FileDescriptor var file_group_group_proto_rawDesc = []byte{ @@ -3765,210 +3883,233 @@ var file_group_group_proto_rawDesc = []byte{ 0x27, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x46, 0x75, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, - 0x32, 0xf4, 0x18, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x56, 0x0a, 0x0b, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x22, 0x2e, 0x4f, 0x70, 0x65, 0x6e, - 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x23, 0x2e, - 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, - 0x75, 0x70, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, - 0x73, 0x70, 0x12, 0x50, 0x0a, 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, - 0x20, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, - 0x71, 0x1a, 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x52, 0x65, 0x73, 0x70, 0x12, 0x50, 0x0a, 0x09, 0x71, 0x75, 0x69, 0x74, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x12, 0x20, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x51, 0x75, 0x69, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x52, 0x65, 0x71, 0x1a, 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x51, 0x75, 0x69, 0x74, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x12, 0x5c, 0x0a, 0x0d, 0x67, 0x65, 0x74, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x24, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x1a, 0x25, 0x2e, - 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, - 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x73, 0x70, 0x12, 0x59, 0x0a, 0x0c, 0x73, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x23, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x53, 0x65, 0x74, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x1a, 0x24, 0x2e, 0x4f, 0x70, 0x65, 0x6e, - 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x53, - 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x12, - 0x7a, 0x0a, 0x17, 0x67, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x70, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2e, 0x2e, 0x4f, 0x70, 0x65, - 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, - 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x2f, 0x2e, 0x4f, 0x70, 0x65, + 0x22, 0x3d, 0x0a, 0x13, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, + 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, + 0xc9, 0x01, 0x0a, 0x14, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x16, + 0x0a, 0x06, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, + 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x12, 0x49, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x2e, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x1a, 0x38, 0x0a, 0x0a, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0xdb, 0x19, 0x0a, 0x05, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x56, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x12, 0x22, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x23, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, + 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x12, 0x50, 0x0a, + 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x20, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, - 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x80, 0x01, 0x0a, 0x19, - 0x67, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x30, 0x2e, 0x4f, 0x70, 0x65, 0x6e, - 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, - 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x31, 0x2e, 0x4f, 0x70, - 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x41, 0x70, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x6b, - 0x0a, 0x12, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4f, - 0x77, 0x6e, 0x65, 0x72, 0x12, 0x29, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, - 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, - 0x2a, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, + 0x4a, 0x6f, 0x69, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x21, 0x2e, 0x4f, + 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x12, + 0x50, 0x0a, 0x09, 0x71, 0x75, 0x69, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x20, 0x2e, 0x4f, + 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x2e, 0x51, 0x75, 0x69, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x21, + 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x2e, 0x51, 0x75, 0x69, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, + 0x70, 0x12, 0x5c, 0x0a, 0x0d, 0x67, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x49, 0x6e, + 0x66, 0x6f, 0x12, 0x24, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x1a, 0x25, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, + 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, + 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x12, + 0x59, 0x0a, 0x0c, 0x73, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x23, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x53, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x65, 0x71, 0x1a, 0x24, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x53, 0x65, 0x74, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x12, 0x7a, 0x0a, 0x17, 0x67, 0x65, + 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x2f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x80, 0x01, 0x0a, 0x19, 0x67, 0x65, 0x74, 0x55, 0x73, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x4c, 0x69, 0x73, 0x74, 0x12, 0x30, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x55, + 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x6b, 0x0a, 0x12, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x66, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, + 0x29, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x12, 0x7d, 0x0a, 0x18, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x30, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, - 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x6b, 0x0a, 0x12, 0x67, 0x65, - 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, - 0x12, 0x29, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, - 0x6d, 0x62, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x2a, 0x2e, 0x4f, 0x70, + 0x75, 0x70, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x2a, 0x2e, 0x4f, 0x70, 0x65, + 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4f, 0x77, 0x6e, + 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x12, 0x7d, 0x0a, 0x18, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x41, + 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x2f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x70, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x52, 0x65, 0x71, 0x1a, 0x30, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x70, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x6b, 0x0a, 0x12, 0x67, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x29, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4c, - 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x6e, 0x0a, 0x13, 0x67, 0x65, 0x74, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2a, - 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x1a, 0x2b, 0x2e, 0x4f, 0x70, 0x65, - 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, - 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x12, 0x62, 0x0a, 0x0f, 0x6b, 0x69, 0x63, 0x6b, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x26, 0x2e, 0x4f, 0x70, 0x65, - 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, - 0x4b, 0x69, 0x63, 0x6b, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x1a, 0x27, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x4b, 0x69, 0x63, 0x6b, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x12, 0x6b, 0x0a, 0x12, 0x67, - 0x65, 0x74, 0x4a, 0x6f, 0x69, 0x6e, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x69, 0x73, - 0x74, 0x12, 0x29, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x69, 0x6e, 0x65, 0x64, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x2a, 0x2e, 0x4f, + 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x2a, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x12, 0x6e, 0x0a, 0x13, 0x67, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2a, 0x2e, 0x4f, 0x70, 0x65, 0x6e, + 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, + 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x65, 0x71, 0x1a, 0x2b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x73, 0x70, 0x12, 0x62, 0x0a, 0x0f, 0x6b, 0x69, 0x63, 0x6b, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x26, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x4b, 0x69, 0x63, 0x6b, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x27, 0x2e, + 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x2e, 0x4b, 0x69, 0x63, 0x6b, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x12, 0x6b, 0x0a, 0x12, 0x67, 0x65, 0x74, 0x4a, 0x6f, 0x69, + 0x6e, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x29, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x69, 0x6e, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x68, 0x0a, 0x11, 0x69, 0x6e, 0x76, 0x69, - 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x54, 0x6f, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x28, 0x2e, - 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, - 0x75, 0x70, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x54, 0x6f, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x29, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x49, 0x6e, 0x76, - 0x69, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x54, 0x6f, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, - 0x73, 0x70, 0x12, 0x50, 0x0a, 0x09, 0x67, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, - 0x20, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, - 0x71, 0x1a, 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x12, 0x6b, 0x0a, 0x12, 0x67, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x43, 0x4d, 0x53, 0x12, 0x29, 0x2e, 0x4f, 0x70, 0x65, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x2a, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, + 0x4a, 0x6f, 0x69, 0x6e, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x12, 0x68, 0x0a, 0x11, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x55, 0x73, 0x65, + 0x72, 0x54, 0x6f, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x28, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, + 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x49, 0x6e, + 0x76, 0x69, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x54, 0x6f, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, + 0x65, 0x71, 0x1a, 0x29, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x55, 0x73, + 0x65, 0x72, 0x54, 0x6f, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x12, 0x50, 0x0a, + 0x09, 0x67, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x20, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, - 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x43, - 0x4d, 0x53, 0x52, 0x65, 0x71, 0x1a, 0x2a, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, + 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x21, 0x2e, 0x4f, + 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, + 0x6b, 0x0a, 0x12, 0x67, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x73, 0x43, 0x4d, 0x53, 0x12, 0x29, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x43, 0x4d, 0x53, 0x52, 0x65, 0x73, - 0x70, 0x12, 0x59, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x6d, 0x69, 0x73, 0x73, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x12, 0x23, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x43, 0x4d, 0x53, 0x52, 0x65, 0x71, + 0x1a, 0x2a, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x73, 0x43, 0x4d, 0x53, 0x52, 0x65, 0x73, 0x70, 0x12, 0x59, 0x0a, 0x0c, + 0x64, 0x69, 0x73, 0x6d, 0x69, 0x73, 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x23, 0x2e, 0x4f, + 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x2e, 0x44, 0x69, 0x73, 0x6d, 0x69, 0x73, 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, + 0x71, 0x1a, 0x24, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x44, 0x69, 0x73, 0x6d, 0x69, 0x73, 0x73, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x24, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x44, 0x69, 0x73, 0x6d, - 0x69, 0x73, 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x12, 0x62, 0x0a, 0x0f, - 0x6d, 0x75, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, - 0x26, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x4d, 0x75, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, - 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x27, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x4d, 0x75, 0x74, - 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x12, 0x74, 0x0a, 0x15, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, - 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x43, + 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x12, 0x62, 0x0a, 0x0f, 0x6d, 0x75, 0x74, 0x65, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x26, 0x2e, 0x4f, 0x70, 0x65, + 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, + 0x4d, 0x75, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x1a, 0x27, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x4d, 0x75, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x12, 0x74, 0x0a, 0x15, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, - 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x2d, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x43, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x12, 0x50, 0x0a, 0x09, 0x6d, 0x75, 0x74, 0x65, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x12, 0x20, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x4d, 0x75, 0x74, 0x65, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x4d, 0x75, 0x74, 0x65, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x12, 0x62, 0x0a, 0x0f, 0x63, 0x61, 0x6e, 0x63, - 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x26, 0x2e, 0x4f, 0x70, - 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x52, 0x65, 0x71, 0x1a, 0x27, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4d, - 0x75, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x12, 0x7a, 0x0a, 0x17, - 0x67, 0x65, 0x74, 0x4a, 0x6f, 0x69, 0x6e, 0x65, 0x64, 0x53, 0x75, 0x70, 0x65, 0x72, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, - 0x4a, 0x6f, 0x69, 0x6e, 0x65, 0x64, 0x53, 0x75, 0x70, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x2f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, - 0x4a, 0x6f, 0x69, 0x6e, 0x65, 0x64, 0x53, 0x75, 0x70, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x6b, 0x0a, 0x12, 0x67, 0x65, 0x74, 0x53, - 0x75, 0x70, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x29, + 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, + 0x4d, 0x75, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x1a, 0x2d, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4d, 0x75, + 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x12, 0x50, 0x0a, 0x09, 0x6d, 0x75, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x20, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x70, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x1a, 0x2a, 0x2e, 0x4f, 0x70, 0x65, 0x6e, + 0x6f, 0x75, 0x70, 0x2e, 0x4d, 0x75, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, + 0x1a, 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x4d, 0x75, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, + 0x65, 0x73, 0x70, 0x12, 0x62, 0x0a, 0x0f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4d, 0x75, 0x74, + 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x26, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x43, 0x61, 0x6e, 0x63, + 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x27, + 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4d, 0x75, 0x74, 0x65, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x12, 0x7a, 0x0a, 0x17, 0x67, 0x65, 0x74, 0x4a, 0x6f, + 0x69, 0x6e, 0x65, 0x64, 0x53, 0x75, 0x70, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x69, + 0x73, 0x74, 0x12, 0x2e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x69, 0x6e, 0x65, + 0x64, 0x53, 0x75, 0x70, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x71, 0x1a, 0x2f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x69, 0x6e, 0x65, + 0x64, 0x53, 0x75, 0x70, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x12, 0x6b, 0x0a, 0x12, 0x67, 0x65, 0x74, 0x53, 0x75, 0x70, 0x65, 0x72, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x29, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x70, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x65, 0x73, 0x70, 0x12, 0x6b, 0x0a, 0x12, 0x73, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x29, 0x2e, 0x4f, 0x70, - 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x2e, 0x53, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x1a, 0x2a, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, + 0x6f, 0x52, 0x65, 0x71, 0x1a, 0x2a, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x70, + 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, + 0x12, 0x6b, 0x0a, 0x12, 0x73, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x29, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x53, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x73, 0x70, 0x12, 0x71, 0x0a, 0x14, 0x67, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x62, - 0x73, 0x74, 0x72, 0x61, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2b, 0x2e, 0x4f, 0x70, 0x65, - 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, - 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x62, 0x73, 0x74, 0x72, 0x61, 0x63, 0x74, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x1a, 0x2c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x62, 0x73, 0x74, 0x72, 0x61, 0x63, 0x74, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x65, 0x73, 0x70, 0x12, 0x74, 0x0a, 0x15, 0x67, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, - 0x49, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x2c, - 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x2d, 0x2e, 0x4f, - 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x74, 0x0a, 0x15, 0x67, - 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x55, 0x73, 0x65, - 0x72, 0x49, 0x44, 0x73, 0x12, 0x2c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x73, 0x52, - 0x65, 0x71, 0x1a, 0x2d, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x12, 0x7a, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, - 0x62, 0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x2e, 0x2e, 0x4f, - 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, - 0x52, 0x6f, 0x6c, 0x65, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x1a, 0x2f, 0x2e, 0x4f, - 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, - 0x52, 0x6f, 0x6c, 0x65, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x12, 0x68, 0x0a, - 0x11, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x43, 0x61, 0x63, - 0x68, 0x65, 0x12, 0x28, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x71, 0x1a, 0x2a, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x53, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x12, 0x71, 0x0a, + 0x14, 0x67, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x62, 0x73, 0x74, 0x72, 0x61, 0x63, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x41, 0x62, 0x73, 0x74, 0x72, 0x61, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x65, 0x71, 0x1a, 0x2c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x49, 0x6e, 0x66, 0x6f, 0x43, 0x61, 0x63, 0x68, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x29, 0x2e, 0x4f, - 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x43, 0x61, - 0x63, 0x68, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x6e, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x2a, - 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x43, 0x61, 0x63, 0x68, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x2b, 0x2e, 0x4f, 0x70, 0x65, - 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, - 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x43, 0x61, - 0x63, 0x68, 0x65, 0x52, 0x65, 0x73, 0x70, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x44, 0x4b, 0x2f, - 0x4f, 0x70, 0x65, 0x6e, 0x2d, 0x49, 0x4d, 0x2d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x70, - 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x41, 0x62, 0x73, 0x74, 0x72, 0x61, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, + 0x12, 0x74, 0x0a, 0x15, 0x67, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x2c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, + 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, + 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x2d, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, + 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x74, 0x0a, 0x15, 0x67, 0x65, 0x74, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x73, 0x12, + 0x2c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x2d, 0x2e, + 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x7a, 0x0a, 0x17, + 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x6f, + 0x6c, 0x65, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x2e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x4c, + 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x1a, 0x2f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x4c, + 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x12, 0x68, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x28, 0x2e, + 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x43, + 0x61, 0x63, 0x68, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x29, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x43, 0x61, 0x63, 0x68, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x12, 0x6e, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x2a, 0x2e, 0x4f, 0x70, 0x65, 0x6e, + 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, + 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x43, 0x61, 0x63, + 0x68, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x2b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x43, 0x61, 0x63, 0x68, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x12, 0x65, 0x0a, 0x10, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x27, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x1a, + 0x28, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x44, + 0x4b, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x2d, 0x49, 0x4d, 0x2d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -3983,7 +4124,7 @@ func file_group_group_proto_rawDescGZIP() []byte { return file_group_group_proto_rawDescData } -var file_group_group_proto_msgTypes = make([]protoimpl.MessageInfo, 66) +var file_group_group_proto_msgTypes = make([]protoimpl.MessageInfo, 69) var file_group_group_proto_goTypes = []interface{}{ (*CreateGroupReq)(nil), // 0: OpenIMServer.group.CreateGroupReq (*CreateGroupResp)(nil), // 1: OpenIMServer.group.CreateGroupResp @@ -4051,112 +4192,118 @@ var file_group_group_proto_goTypes = []interface{}{ (*GetGroupInfoCacheResp)(nil), // 63: OpenIMServer.group.GetGroupInfoCacheResp (*GetGroupMemberCacheReq)(nil), // 64: OpenIMServer.group.GetGroupMemberCacheReq (*GetGroupMemberCacheResp)(nil), // 65: OpenIMServer.group.GetGroupMemberCacheResp - (*sdkws.GroupInfo)(nil), // 66: OpenIMServer.sdkws.GroupInfo - (*sdkws.GroupInfoForSet)(nil), // 67: OpenIMServer.sdkws.GroupInfoForSet - (*sdkws.RequestPagination)(nil), // 68: OpenIMServer.sdkws.RequestPagination - (*sdkws.GroupRequest)(nil), // 69: OpenIMServer.sdkws.GroupRequest - (*sdkws.GroupMemberFullInfo)(nil), // 70: OpenIMServer.sdkws.GroupMemberFullInfo - (*wrapperspb.StringValue)(nil), // 71: OpenIMServer.protobuf.StringValue - (*wrapperspb.Int32Value)(nil), // 72: OpenIMServer.protobuf.Int32Value + (*GroupCreateCountReq)(nil), // 66: OpenIMServer.group.GroupCreateCountReq + (*GroupCreateCountResp)(nil), // 67: OpenIMServer.group.GroupCreateCountResp + nil, // 68: OpenIMServer.group.GroupCreateCountResp.CountEntry + (*sdkws.GroupInfo)(nil), // 69: OpenIMServer.sdkws.GroupInfo + (*sdkws.GroupInfoForSet)(nil), // 70: OpenIMServer.sdkws.GroupInfoForSet + (*sdkws.RequestPagination)(nil), // 71: OpenIMServer.sdkws.RequestPagination + (*sdkws.GroupRequest)(nil), // 72: OpenIMServer.sdkws.GroupRequest + (*sdkws.GroupMemberFullInfo)(nil), // 73: OpenIMServer.sdkws.GroupMemberFullInfo + (*wrapperspb.StringValue)(nil), // 74: OpenIMServer.protobuf.StringValue + (*wrapperspb.Int32Value)(nil), // 75: OpenIMServer.protobuf.Int32Value } var file_group_group_proto_depIdxs = []int32{ - 66, // 0: OpenIMServer.group.CreateGroupReq.groupInfo:type_name -> OpenIMServer.sdkws.GroupInfo - 66, // 1: OpenIMServer.group.CreateGroupResp.groupInfo:type_name -> OpenIMServer.sdkws.GroupInfo - 66, // 2: OpenIMServer.group.GetGroupsInfoResp.groupInfos:type_name -> OpenIMServer.sdkws.GroupInfo - 67, // 3: OpenIMServer.group.SetGroupInfoReq.groupInfoForSet:type_name -> OpenIMServer.sdkws.GroupInfoForSet - 68, // 4: OpenIMServer.group.GetGroupApplicationListReq.pagination:type_name -> OpenIMServer.sdkws.RequestPagination - 69, // 5: OpenIMServer.group.GetGroupApplicationListResp.groupRequests:type_name -> OpenIMServer.sdkws.GroupRequest - 68, // 6: OpenIMServer.group.GetUserReqApplicationListReq.pagination:type_name -> OpenIMServer.sdkws.RequestPagination - 69, // 7: OpenIMServer.group.GetUserReqApplicationListResp.groupRequests:type_name -> OpenIMServer.sdkws.GroupRequest - 68, // 8: OpenIMServer.group.GetGroupMemberListReq.pagination:type_name -> OpenIMServer.sdkws.RequestPagination - 70, // 9: OpenIMServer.group.GetGroupMemberListResp.members:type_name -> OpenIMServer.sdkws.GroupMemberFullInfo - 70, // 10: OpenIMServer.group.GetGroupMembersInfoResp.members:type_name -> OpenIMServer.sdkws.GroupMemberFullInfo - 68, // 11: OpenIMServer.group.GetJoinedGroupListReq.pagination:type_name -> OpenIMServer.sdkws.RequestPagination - 66, // 12: OpenIMServer.group.GetJoinedGroupListResp.groups:type_name -> OpenIMServer.sdkws.GroupInfo - 68, // 13: OpenIMServer.group.GetGroupAllMemberReq.pagination:type_name -> OpenIMServer.sdkws.RequestPagination - 70, // 14: OpenIMServer.group.GetGroupAllMemberResp.members:type_name -> OpenIMServer.sdkws.GroupMemberFullInfo - 66, // 15: OpenIMServer.group.CMSGroup.groupInfo:type_name -> OpenIMServer.sdkws.GroupInfo - 68, // 16: OpenIMServer.group.GetGroupsReq.pagination:type_name -> OpenIMServer.sdkws.RequestPagination + 69, // 0: OpenIMServer.group.CreateGroupReq.groupInfo:type_name -> OpenIMServer.sdkws.GroupInfo + 69, // 1: OpenIMServer.group.CreateGroupResp.groupInfo:type_name -> OpenIMServer.sdkws.GroupInfo + 69, // 2: OpenIMServer.group.GetGroupsInfoResp.groupInfos:type_name -> OpenIMServer.sdkws.GroupInfo + 70, // 3: OpenIMServer.group.SetGroupInfoReq.groupInfoForSet:type_name -> OpenIMServer.sdkws.GroupInfoForSet + 71, // 4: OpenIMServer.group.GetGroupApplicationListReq.pagination:type_name -> OpenIMServer.sdkws.RequestPagination + 72, // 5: OpenIMServer.group.GetGroupApplicationListResp.groupRequests:type_name -> OpenIMServer.sdkws.GroupRequest + 71, // 6: OpenIMServer.group.GetUserReqApplicationListReq.pagination:type_name -> OpenIMServer.sdkws.RequestPagination + 72, // 7: OpenIMServer.group.GetUserReqApplicationListResp.groupRequests:type_name -> OpenIMServer.sdkws.GroupRequest + 71, // 8: OpenIMServer.group.GetGroupMemberListReq.pagination:type_name -> OpenIMServer.sdkws.RequestPagination + 73, // 9: OpenIMServer.group.GetGroupMemberListResp.members:type_name -> OpenIMServer.sdkws.GroupMemberFullInfo + 73, // 10: OpenIMServer.group.GetGroupMembersInfoResp.members:type_name -> OpenIMServer.sdkws.GroupMemberFullInfo + 71, // 11: OpenIMServer.group.GetJoinedGroupListReq.pagination:type_name -> OpenIMServer.sdkws.RequestPagination + 69, // 12: OpenIMServer.group.GetJoinedGroupListResp.groups:type_name -> OpenIMServer.sdkws.GroupInfo + 71, // 13: OpenIMServer.group.GetGroupAllMemberReq.pagination:type_name -> OpenIMServer.sdkws.RequestPagination + 73, // 14: OpenIMServer.group.GetGroupAllMemberResp.members:type_name -> OpenIMServer.sdkws.GroupMemberFullInfo + 69, // 15: OpenIMServer.group.CMSGroup.groupInfo:type_name -> OpenIMServer.sdkws.GroupInfo + 71, // 16: OpenIMServer.group.GetGroupsReq.pagination:type_name -> OpenIMServer.sdkws.RequestPagination 30, // 17: OpenIMServer.group.GetGroupsResp.groups:type_name -> OpenIMServer.group.CMSGroup - 68, // 18: OpenIMServer.group.GetGroupMembersCMSReq.pagination:type_name -> OpenIMServer.sdkws.RequestPagination - 70, // 19: OpenIMServer.group.GetGroupMembersCMSResp.members:type_name -> OpenIMServer.sdkws.GroupMemberFullInfo - 66, // 20: OpenIMServer.group.GetJoinedSuperGroupListResp.groups:type_name -> OpenIMServer.sdkws.GroupInfo - 66, // 21: OpenIMServer.group.GetSuperGroupsInfoResp.groupInfos:type_name -> OpenIMServer.sdkws.GroupInfo - 71, // 22: OpenIMServer.group.SetGroupMemberInfo.nickname:type_name -> OpenIMServer.protobuf.StringValue - 71, // 23: OpenIMServer.group.SetGroupMemberInfo.faceURL:type_name -> OpenIMServer.protobuf.StringValue - 72, // 24: OpenIMServer.group.SetGroupMemberInfo.roleLevel:type_name -> OpenIMServer.protobuf.Int32Value - 71, // 25: OpenIMServer.group.SetGroupMemberInfo.ex:type_name -> OpenIMServer.protobuf.StringValue + 71, // 18: OpenIMServer.group.GetGroupMembersCMSReq.pagination:type_name -> OpenIMServer.sdkws.RequestPagination + 73, // 19: OpenIMServer.group.GetGroupMembersCMSResp.members:type_name -> OpenIMServer.sdkws.GroupMemberFullInfo + 69, // 20: OpenIMServer.group.GetJoinedSuperGroupListResp.groups:type_name -> OpenIMServer.sdkws.GroupInfo + 69, // 21: OpenIMServer.group.GetSuperGroupsInfoResp.groupInfos:type_name -> OpenIMServer.sdkws.GroupInfo + 74, // 22: OpenIMServer.group.SetGroupMemberInfo.nickname:type_name -> OpenIMServer.protobuf.StringValue + 74, // 23: OpenIMServer.group.SetGroupMemberInfo.faceURL:type_name -> OpenIMServer.protobuf.StringValue + 75, // 24: OpenIMServer.group.SetGroupMemberInfo.roleLevel:type_name -> OpenIMServer.protobuf.Int32Value + 74, // 25: OpenIMServer.group.SetGroupMemberInfo.ex:type_name -> OpenIMServer.protobuf.StringValue 50, // 26: OpenIMServer.group.SetGroupMemberInfoReq.members:type_name -> OpenIMServer.group.SetGroupMemberInfo 54, // 27: OpenIMServer.group.GetGroupAbstractInfoResp.groupAbstractInfos:type_name -> OpenIMServer.group.GroupAbstractInfo - 70, // 28: OpenIMServer.group.GetUserInGroupMembersResp.members:type_name -> OpenIMServer.sdkws.GroupMemberFullInfo - 70, // 29: OpenIMServer.group.GetGroupMemberRoleLevelResp.members:type_name -> OpenIMServer.sdkws.GroupMemberFullInfo - 66, // 30: OpenIMServer.group.GetGroupInfoCacheResp.groupInfo:type_name -> OpenIMServer.sdkws.GroupInfo - 70, // 31: OpenIMServer.group.GetGroupMemberCacheResp.member:type_name -> OpenIMServer.sdkws.GroupMemberFullInfo - 0, // 32: OpenIMServer.group.group.createGroup:input_type -> OpenIMServer.group.CreateGroupReq - 12, // 33: OpenIMServer.group.group.joinGroup:input_type -> OpenIMServer.group.JoinGroupReq - 16, // 34: OpenIMServer.group.group.quitGroup:input_type -> OpenIMServer.group.QuitGroupReq - 2, // 35: OpenIMServer.group.group.getGroupsInfo:input_type -> OpenIMServer.group.GetGroupsInfoReq - 4, // 36: OpenIMServer.group.group.setGroupInfo:input_type -> OpenIMServer.group.SetGroupInfoReq - 6, // 37: OpenIMServer.group.group.getGroupApplicationList:input_type -> OpenIMServer.group.GetGroupApplicationListReq - 8, // 38: OpenIMServer.group.group.getUserReqApplicationList:input_type -> OpenIMServer.group.GetUserReqApplicationListReq - 10, // 39: OpenIMServer.group.group.transferGroupOwner:input_type -> OpenIMServer.group.TransferGroupOwnerReq - 14, // 40: OpenIMServer.group.group.groupApplicationResponse:input_type -> OpenIMServer.group.GroupApplicationResponseReq - 18, // 41: OpenIMServer.group.group.getGroupMemberList:input_type -> OpenIMServer.group.GetGroupMemberListReq - 20, // 42: OpenIMServer.group.group.getGroupMembersInfo:input_type -> OpenIMServer.group.GetGroupMembersInfoReq - 22, // 43: OpenIMServer.group.group.kickGroupMember:input_type -> OpenIMServer.group.KickGroupMemberReq - 24, // 44: OpenIMServer.group.group.getJoinedGroupList:input_type -> OpenIMServer.group.GetJoinedGroupListReq - 26, // 45: OpenIMServer.group.group.inviteUserToGroup:input_type -> OpenIMServer.group.InviteUserToGroupReq - 31, // 46: OpenIMServer.group.group.getGroups:input_type -> OpenIMServer.group.GetGroupsReq - 34, // 47: OpenIMServer.group.group.getGroupMembersCMS:input_type -> OpenIMServer.group.GetGroupMembersCMSReq - 36, // 48: OpenIMServer.group.group.dismissGroup:input_type -> OpenIMServer.group.DismissGroupReq - 38, // 49: OpenIMServer.group.group.muteGroupMember:input_type -> OpenIMServer.group.MuteGroupMemberReq - 40, // 50: OpenIMServer.group.group.cancelMuteGroupMember:input_type -> OpenIMServer.group.CancelMuteGroupMemberReq - 42, // 51: OpenIMServer.group.group.muteGroup:input_type -> OpenIMServer.group.MuteGroupReq - 44, // 52: OpenIMServer.group.group.cancelMuteGroup:input_type -> OpenIMServer.group.CancelMuteGroupReq - 46, // 53: OpenIMServer.group.group.getJoinedSuperGroupList:input_type -> OpenIMServer.group.GetJoinedSuperGroupListReq - 48, // 54: OpenIMServer.group.group.getSuperGroupsInfo:input_type -> OpenIMServer.group.GetSuperGroupsInfoReq - 51, // 55: OpenIMServer.group.group.setGroupMemberInfo:input_type -> OpenIMServer.group.SetGroupMemberInfoReq - 53, // 56: OpenIMServer.group.group.getGroupAbstractInfo:input_type -> OpenIMServer.group.GetGroupAbstractInfoReq - 56, // 57: OpenIMServer.group.group.getUserInGroupMembers:input_type -> OpenIMServer.group.GetUserInGroupMembersReq - 58, // 58: OpenIMServer.group.group.getGroupMemberUserIDs:input_type -> OpenIMServer.group.GetGroupMemberUserIDsReq - 60, // 59: OpenIMServer.group.group.GetGroupMemberRoleLevel:input_type -> OpenIMServer.group.GetGroupMemberRoleLevelReq - 62, // 60: OpenIMServer.group.group.GetGroupInfoCache:input_type -> OpenIMServer.group.GetGroupInfoCacheReq - 64, // 61: OpenIMServer.group.group.GetGroupMemberCache:input_type -> OpenIMServer.group.GetGroupMemberCacheReq - 1, // 62: OpenIMServer.group.group.createGroup:output_type -> OpenIMServer.group.CreateGroupResp - 13, // 63: OpenIMServer.group.group.joinGroup:output_type -> OpenIMServer.group.JoinGroupResp - 17, // 64: OpenIMServer.group.group.quitGroup:output_type -> OpenIMServer.group.QuitGroupResp - 3, // 65: OpenIMServer.group.group.getGroupsInfo:output_type -> OpenIMServer.group.GetGroupsInfoResp - 5, // 66: OpenIMServer.group.group.setGroupInfo:output_type -> OpenIMServer.group.SetGroupInfoResp - 7, // 67: OpenIMServer.group.group.getGroupApplicationList:output_type -> OpenIMServer.group.GetGroupApplicationListResp - 9, // 68: OpenIMServer.group.group.getUserReqApplicationList:output_type -> OpenIMServer.group.GetUserReqApplicationListResp - 11, // 69: OpenIMServer.group.group.transferGroupOwner:output_type -> OpenIMServer.group.TransferGroupOwnerResp - 15, // 70: OpenIMServer.group.group.groupApplicationResponse:output_type -> OpenIMServer.group.GroupApplicationResponseResp - 19, // 71: OpenIMServer.group.group.getGroupMemberList:output_type -> OpenIMServer.group.GetGroupMemberListResp - 21, // 72: OpenIMServer.group.group.getGroupMembersInfo:output_type -> OpenIMServer.group.GetGroupMembersInfoResp - 23, // 73: OpenIMServer.group.group.kickGroupMember:output_type -> OpenIMServer.group.KickGroupMemberResp - 25, // 74: OpenIMServer.group.group.getJoinedGroupList:output_type -> OpenIMServer.group.GetJoinedGroupListResp - 27, // 75: OpenIMServer.group.group.inviteUserToGroup:output_type -> OpenIMServer.group.InviteUserToGroupResp - 32, // 76: OpenIMServer.group.group.getGroups:output_type -> OpenIMServer.group.GetGroupsResp - 35, // 77: OpenIMServer.group.group.getGroupMembersCMS:output_type -> OpenIMServer.group.GetGroupMembersCMSResp - 37, // 78: OpenIMServer.group.group.dismissGroup:output_type -> OpenIMServer.group.DismissGroupResp - 39, // 79: OpenIMServer.group.group.muteGroupMember:output_type -> OpenIMServer.group.MuteGroupMemberResp - 41, // 80: OpenIMServer.group.group.cancelMuteGroupMember:output_type -> OpenIMServer.group.CancelMuteGroupMemberResp - 43, // 81: OpenIMServer.group.group.muteGroup:output_type -> OpenIMServer.group.MuteGroupResp - 45, // 82: OpenIMServer.group.group.cancelMuteGroup:output_type -> OpenIMServer.group.CancelMuteGroupResp - 47, // 83: OpenIMServer.group.group.getJoinedSuperGroupList:output_type -> OpenIMServer.group.GetJoinedSuperGroupListResp - 49, // 84: OpenIMServer.group.group.getSuperGroupsInfo:output_type -> OpenIMServer.group.GetSuperGroupsInfoResp - 52, // 85: OpenIMServer.group.group.setGroupMemberInfo:output_type -> OpenIMServer.group.SetGroupMemberInfoResp - 55, // 86: OpenIMServer.group.group.getGroupAbstractInfo:output_type -> OpenIMServer.group.GetGroupAbstractInfoResp - 57, // 87: OpenIMServer.group.group.getUserInGroupMembers:output_type -> OpenIMServer.group.GetUserInGroupMembersResp - 59, // 88: OpenIMServer.group.group.getGroupMemberUserIDs:output_type -> OpenIMServer.group.GetGroupMemberUserIDsResp - 61, // 89: OpenIMServer.group.group.GetGroupMemberRoleLevel:output_type -> OpenIMServer.group.GetGroupMemberRoleLevelResp - 63, // 90: OpenIMServer.group.group.GetGroupInfoCache:output_type -> OpenIMServer.group.GetGroupInfoCacheResp - 65, // 91: OpenIMServer.group.group.GetGroupMemberCache:output_type -> OpenIMServer.group.GetGroupMemberCacheResp - 62, // [62:92] is the sub-list for method output_type - 32, // [32:62] is the sub-list for method input_type - 32, // [32:32] is the sub-list for extension type_name - 32, // [32:32] is the sub-list for extension extendee - 0, // [0:32] is the sub-list for field type_name + 73, // 28: OpenIMServer.group.GetUserInGroupMembersResp.members:type_name -> OpenIMServer.sdkws.GroupMemberFullInfo + 73, // 29: OpenIMServer.group.GetGroupMemberRoleLevelResp.members:type_name -> OpenIMServer.sdkws.GroupMemberFullInfo + 69, // 30: OpenIMServer.group.GetGroupInfoCacheResp.groupInfo:type_name -> OpenIMServer.sdkws.GroupInfo + 73, // 31: OpenIMServer.group.GetGroupMemberCacheResp.member:type_name -> OpenIMServer.sdkws.GroupMemberFullInfo + 68, // 32: OpenIMServer.group.GroupCreateCountResp.count:type_name -> OpenIMServer.group.GroupCreateCountResp.CountEntry + 0, // 33: OpenIMServer.group.group.createGroup:input_type -> OpenIMServer.group.CreateGroupReq + 12, // 34: OpenIMServer.group.group.joinGroup:input_type -> OpenIMServer.group.JoinGroupReq + 16, // 35: OpenIMServer.group.group.quitGroup:input_type -> OpenIMServer.group.QuitGroupReq + 2, // 36: OpenIMServer.group.group.getGroupsInfo:input_type -> OpenIMServer.group.GetGroupsInfoReq + 4, // 37: OpenIMServer.group.group.setGroupInfo:input_type -> OpenIMServer.group.SetGroupInfoReq + 6, // 38: OpenIMServer.group.group.getGroupApplicationList:input_type -> OpenIMServer.group.GetGroupApplicationListReq + 8, // 39: OpenIMServer.group.group.getUserReqApplicationList:input_type -> OpenIMServer.group.GetUserReqApplicationListReq + 10, // 40: OpenIMServer.group.group.transferGroupOwner:input_type -> OpenIMServer.group.TransferGroupOwnerReq + 14, // 41: OpenIMServer.group.group.groupApplicationResponse:input_type -> OpenIMServer.group.GroupApplicationResponseReq + 18, // 42: OpenIMServer.group.group.getGroupMemberList:input_type -> OpenIMServer.group.GetGroupMemberListReq + 20, // 43: OpenIMServer.group.group.getGroupMembersInfo:input_type -> OpenIMServer.group.GetGroupMembersInfoReq + 22, // 44: OpenIMServer.group.group.kickGroupMember:input_type -> OpenIMServer.group.KickGroupMemberReq + 24, // 45: OpenIMServer.group.group.getJoinedGroupList:input_type -> OpenIMServer.group.GetJoinedGroupListReq + 26, // 46: OpenIMServer.group.group.inviteUserToGroup:input_type -> OpenIMServer.group.InviteUserToGroupReq + 31, // 47: OpenIMServer.group.group.getGroups:input_type -> OpenIMServer.group.GetGroupsReq + 34, // 48: OpenIMServer.group.group.getGroupMembersCMS:input_type -> OpenIMServer.group.GetGroupMembersCMSReq + 36, // 49: OpenIMServer.group.group.dismissGroup:input_type -> OpenIMServer.group.DismissGroupReq + 38, // 50: OpenIMServer.group.group.muteGroupMember:input_type -> OpenIMServer.group.MuteGroupMemberReq + 40, // 51: OpenIMServer.group.group.cancelMuteGroupMember:input_type -> OpenIMServer.group.CancelMuteGroupMemberReq + 42, // 52: OpenIMServer.group.group.muteGroup:input_type -> OpenIMServer.group.MuteGroupReq + 44, // 53: OpenIMServer.group.group.cancelMuteGroup:input_type -> OpenIMServer.group.CancelMuteGroupReq + 46, // 54: OpenIMServer.group.group.getJoinedSuperGroupList:input_type -> OpenIMServer.group.GetJoinedSuperGroupListReq + 48, // 55: OpenIMServer.group.group.getSuperGroupsInfo:input_type -> OpenIMServer.group.GetSuperGroupsInfoReq + 51, // 56: OpenIMServer.group.group.setGroupMemberInfo:input_type -> OpenIMServer.group.SetGroupMemberInfoReq + 53, // 57: OpenIMServer.group.group.getGroupAbstractInfo:input_type -> OpenIMServer.group.GetGroupAbstractInfoReq + 56, // 58: OpenIMServer.group.group.getUserInGroupMembers:input_type -> OpenIMServer.group.GetUserInGroupMembersReq + 58, // 59: OpenIMServer.group.group.getGroupMemberUserIDs:input_type -> OpenIMServer.group.GetGroupMemberUserIDsReq + 60, // 60: OpenIMServer.group.group.GetGroupMemberRoleLevel:input_type -> OpenIMServer.group.GetGroupMemberRoleLevelReq + 62, // 61: OpenIMServer.group.group.GetGroupInfoCache:input_type -> OpenIMServer.group.GetGroupInfoCacheReq + 64, // 62: OpenIMServer.group.group.GetGroupMemberCache:input_type -> OpenIMServer.group.GetGroupMemberCacheReq + 66, // 63: OpenIMServer.group.group.GroupCreateCount:input_type -> OpenIMServer.group.GroupCreateCountReq + 1, // 64: OpenIMServer.group.group.createGroup:output_type -> OpenIMServer.group.CreateGroupResp + 13, // 65: OpenIMServer.group.group.joinGroup:output_type -> OpenIMServer.group.JoinGroupResp + 17, // 66: OpenIMServer.group.group.quitGroup:output_type -> OpenIMServer.group.QuitGroupResp + 3, // 67: OpenIMServer.group.group.getGroupsInfo:output_type -> OpenIMServer.group.GetGroupsInfoResp + 5, // 68: OpenIMServer.group.group.setGroupInfo:output_type -> OpenIMServer.group.SetGroupInfoResp + 7, // 69: OpenIMServer.group.group.getGroupApplicationList:output_type -> OpenIMServer.group.GetGroupApplicationListResp + 9, // 70: OpenIMServer.group.group.getUserReqApplicationList:output_type -> OpenIMServer.group.GetUserReqApplicationListResp + 11, // 71: OpenIMServer.group.group.transferGroupOwner:output_type -> OpenIMServer.group.TransferGroupOwnerResp + 15, // 72: OpenIMServer.group.group.groupApplicationResponse:output_type -> OpenIMServer.group.GroupApplicationResponseResp + 19, // 73: OpenIMServer.group.group.getGroupMemberList:output_type -> OpenIMServer.group.GetGroupMemberListResp + 21, // 74: OpenIMServer.group.group.getGroupMembersInfo:output_type -> OpenIMServer.group.GetGroupMembersInfoResp + 23, // 75: OpenIMServer.group.group.kickGroupMember:output_type -> OpenIMServer.group.KickGroupMemberResp + 25, // 76: OpenIMServer.group.group.getJoinedGroupList:output_type -> OpenIMServer.group.GetJoinedGroupListResp + 27, // 77: OpenIMServer.group.group.inviteUserToGroup:output_type -> OpenIMServer.group.InviteUserToGroupResp + 32, // 78: OpenIMServer.group.group.getGroups:output_type -> OpenIMServer.group.GetGroupsResp + 35, // 79: OpenIMServer.group.group.getGroupMembersCMS:output_type -> OpenIMServer.group.GetGroupMembersCMSResp + 37, // 80: OpenIMServer.group.group.dismissGroup:output_type -> OpenIMServer.group.DismissGroupResp + 39, // 81: OpenIMServer.group.group.muteGroupMember:output_type -> OpenIMServer.group.MuteGroupMemberResp + 41, // 82: OpenIMServer.group.group.cancelMuteGroupMember:output_type -> OpenIMServer.group.CancelMuteGroupMemberResp + 43, // 83: OpenIMServer.group.group.muteGroup:output_type -> OpenIMServer.group.MuteGroupResp + 45, // 84: OpenIMServer.group.group.cancelMuteGroup:output_type -> OpenIMServer.group.CancelMuteGroupResp + 47, // 85: OpenIMServer.group.group.getJoinedSuperGroupList:output_type -> OpenIMServer.group.GetJoinedSuperGroupListResp + 49, // 86: OpenIMServer.group.group.getSuperGroupsInfo:output_type -> OpenIMServer.group.GetSuperGroupsInfoResp + 52, // 87: OpenIMServer.group.group.setGroupMemberInfo:output_type -> OpenIMServer.group.SetGroupMemberInfoResp + 55, // 88: OpenIMServer.group.group.getGroupAbstractInfo:output_type -> OpenIMServer.group.GetGroupAbstractInfoResp + 57, // 89: OpenIMServer.group.group.getUserInGroupMembers:output_type -> OpenIMServer.group.GetUserInGroupMembersResp + 59, // 90: OpenIMServer.group.group.getGroupMemberUserIDs:output_type -> OpenIMServer.group.GetGroupMemberUserIDsResp + 61, // 91: OpenIMServer.group.group.GetGroupMemberRoleLevel:output_type -> OpenIMServer.group.GetGroupMemberRoleLevelResp + 63, // 92: OpenIMServer.group.group.GetGroupInfoCache:output_type -> OpenIMServer.group.GetGroupInfoCacheResp + 65, // 93: OpenIMServer.group.group.GetGroupMemberCache:output_type -> OpenIMServer.group.GetGroupMemberCacheResp + 67, // 94: OpenIMServer.group.group.GroupCreateCount:output_type -> OpenIMServer.group.GroupCreateCountResp + 64, // [64:95] is the sub-list for method output_type + 33, // [33:64] is the sub-list for method input_type + 33, // [33:33] is the sub-list for extension type_name + 33, // [33:33] is the sub-list for extension extendee + 0, // [0:33] is the sub-list for field type_name } func init() { file_group_group_proto_init() } @@ -4957,6 +5104,30 @@ func file_group_group_proto_init() { return nil } } + file_group_group_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupCreateCountReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_group_group_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupCreateCountResp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -4964,7 +5135,7 @@ func file_group_group_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_group_group_proto_rawDesc, NumEnums: 0, - NumMessages: 66, + NumMessages: 69, NumExtensions: 0, NumServices: 1, }, @@ -5047,6 +5218,7 @@ type GroupClient interface { GetGroupMemberRoleLevel(ctx context.Context, in *GetGroupMemberRoleLevelReq, opts ...grpc.CallOption) (*GetGroupMemberRoleLevelResp, error) GetGroupInfoCache(ctx context.Context, in *GetGroupInfoCacheReq, opts ...grpc.CallOption) (*GetGroupInfoCacheResp, error) GetGroupMemberCache(ctx context.Context, in *GetGroupMemberCacheReq, opts ...grpc.CallOption) (*GetGroupMemberCacheResp, error) + GroupCreateCount(ctx context.Context, in *GroupCreateCountReq, opts ...grpc.CallOption) (*GroupCreateCountResp, error) } type groupClient struct { @@ -5327,6 +5499,15 @@ func (c *groupClient) GetGroupMemberCache(ctx context.Context, in *GetGroupMembe return out, nil } +func (c *groupClient) GroupCreateCount(ctx context.Context, in *GroupCreateCountReq, opts ...grpc.CallOption) (*GroupCreateCountResp, error) { + out := new(GroupCreateCountResp) + err := c.cc.Invoke(ctx, "/OpenIMServer.group.group/GroupCreateCount", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // GroupServer is the server API for Group service. type GroupServer interface { // 创建群 @@ -5386,6 +5567,7 @@ type GroupServer interface { GetGroupMemberRoleLevel(context.Context, *GetGroupMemberRoleLevelReq) (*GetGroupMemberRoleLevelResp, error) GetGroupInfoCache(context.Context, *GetGroupInfoCacheReq) (*GetGroupInfoCacheResp, error) GetGroupMemberCache(context.Context, *GetGroupMemberCacheReq) (*GetGroupMemberCacheResp, error) + GroupCreateCount(context.Context, *GroupCreateCountReq) (*GroupCreateCountResp, error) } // UnimplementedGroupServer can be embedded to have forward compatible implementations. @@ -5482,6 +5664,9 @@ func (*UnimplementedGroupServer) GetGroupInfoCache(context.Context, *GetGroupInf func (*UnimplementedGroupServer) GetGroupMemberCache(context.Context, *GetGroupMemberCacheReq) (*GetGroupMemberCacheResp, error) { return nil, status.Errorf(codes.Unimplemented, "method GetGroupMemberCache not implemented") } +func (*UnimplementedGroupServer) GroupCreateCount(context.Context, *GroupCreateCountReq) (*GroupCreateCountResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method GroupCreateCount not implemented") +} func RegisterGroupServer(s *grpc.Server, srv GroupServer) { s.RegisterService(&_Group_serviceDesc, srv) @@ -6027,6 +6212,24 @@ func _Group_GetGroupMemberCache_Handler(srv interface{}, ctx context.Context, de return interceptor(ctx, in, info, handler) } +func _Group_GroupCreateCount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GroupCreateCountReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServer).GroupCreateCount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/OpenIMServer.group.group/GroupCreateCount", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServer).GroupCreateCount(ctx, req.(*GroupCreateCountReq)) + } + return interceptor(ctx, in, info, handler) +} + var _Group_serviceDesc = grpc.ServiceDesc{ ServiceName: "OpenIMServer.group.group", HandlerType: (*GroupServer)(nil), @@ -6151,6 +6354,10 @@ var _Group_serviceDesc = grpc.ServiceDesc{ MethodName: "GetGroupMemberCache", Handler: _Group_GetGroupMemberCache_Handler, }, + { + MethodName: "GroupCreateCount", + Handler: _Group_GroupCreateCount_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "group/group.proto", diff --git a/pkg/proto/group/group.proto b/pkg/proto/group/group.proto index 9d02f58f7..9379ebe86 100644 --- a/pkg/proto/group/group.proto +++ b/pkg/proto/group/group.proto @@ -332,6 +332,17 @@ message GetGroupMemberCacheResp { sdkws.GroupMemberFullInfo member = 1; } +message GroupCreateCountReq { + int64 start = 1; + int64 end = 2; +} + +message GroupCreateCountResp { + int64 total = 1; + int64 before = 2; + map count = 3; +} + service group{ //创建群 rpc createGroup(CreateGroupReq) returns(CreateGroupResp); @@ -394,6 +405,8 @@ service group{ rpc GetGroupInfoCache(GetGroupInfoCacheReq) returns (GetGroupInfoCacheResp); rpc GetGroupMemberCache(GetGroupMemberCacheReq) returns (GetGroupMemberCacheResp); + + rpc GroupCreateCount(GroupCreateCountReq) returns (GroupCreateCountResp); } diff --git a/pkg/proto/msg/msg.pb.go b/pkg/proto/msg/msg.pb.go index f52af2b63..2686704cc 100644 --- a/pkg/proto/msg/msg.pb.go +++ b/pkg/proto/msg/msg.pb.go @@ -8,17 +8,14 @@ package msg import ( context "context" - reflect "reflect" - sync "sync" - + sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - - sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" - wrapperspb "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/wrapperspb" + reflect "reflect" + sync "sync" ) const ( @@ -630,24 +627,17 @@ func (x *GetSendMsgStatusResp) GetStatus() int32 { return 0 } -type ModifyMessageReactionExtensionsReq struct { +type MsgDataToModifyByMQ struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ConversationID string `protobuf:"bytes,1,opt,name=conversationID,proto3" json:"conversationID"` - SessionType int32 `protobuf:"varint,2,opt,name=sessionType,proto3" json:"sessionType"` - ReactionExtensions map[string]*sdkws.KeyValue `protobuf:"bytes,3,rep,name=reactionExtensions,proto3" json:"reactionExtensions" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - ClientMsgID string `protobuf:"bytes,4,opt,name=clientMsgID,proto3" json:"clientMsgID"` - Ex *wrapperspb.StringValue `protobuf:"bytes,5,opt,name=ex,proto3" json:"ex"` - AttachedInfo *wrapperspb.StringValue `protobuf:"bytes,6,opt,name=attachedInfo,proto3" json:"attachedInfo"` - IsReact bool `protobuf:"varint,7,opt,name=isReact,proto3" json:"isReact"` - IsExternalExtensions bool `protobuf:"varint,8,opt,name=isExternalExtensions,proto3" json:"isExternalExtensions"` - MsgFirstModifyTime int64 `protobuf:"varint,9,opt,name=msgFirstModifyTime,proto3" json:"msgFirstModifyTime"` + Messages []*sdkws.MsgData `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages"` + ConversationID string `protobuf:"bytes,2,opt,name=conversationID,proto3" json:"conversationID"` } -func (x *ModifyMessageReactionExtensionsReq) Reset() { - *x = ModifyMessageReactionExtensionsReq{} +func (x *MsgDataToModifyByMQ) Reset() { + *x = MsgDataToModifyByMQ{} if protoimpl.UnsafeEnabled { mi := &file_msg_msg_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -655,13 +645,13 @@ func (x *ModifyMessageReactionExtensionsReq) Reset() { } } -func (x *ModifyMessageReactionExtensionsReq) String() string { +func (x *MsgDataToModifyByMQ) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ModifyMessageReactionExtensionsReq) ProtoMessage() {} +func (*MsgDataToModifyByMQ) ProtoMessage() {} -func (x *ModifyMessageReactionExtensionsReq) ProtoReflect() protoreflect.Message { +func (x *MsgDataToModifyByMQ) ProtoReflect() protoreflect.Message { mi := &file_msg_msg_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -673,92 +663,33 @@ func (x *ModifyMessageReactionExtensionsReq) ProtoReflect() protoreflect.Message return mi.MessageOf(x) } -// Deprecated: Use ModifyMessageReactionExtensionsReq.ProtoReflect.Descriptor instead. -func (*ModifyMessageReactionExtensionsReq) Descriptor() ([]byte, []int) { +// Deprecated: Use MsgDataToModifyByMQ.ProtoReflect.Descriptor instead. +func (*MsgDataToModifyByMQ) Descriptor() ([]byte, []int) { return file_msg_msg_proto_rawDescGZIP(), []int{12} } -func (x *ModifyMessageReactionExtensionsReq) GetConversationID() string { - if x != nil { - return x.ConversationID - } - return "" -} - -func (x *ModifyMessageReactionExtensionsReq) GetSessionType() int32 { - if x != nil { - return x.SessionType - } - return 0 -} - -func (x *ModifyMessageReactionExtensionsReq) GetReactionExtensions() map[string]*sdkws.KeyValue { +func (x *MsgDataToModifyByMQ) GetMessages() []*sdkws.MsgData { if x != nil { - return x.ReactionExtensions + return x.Messages } return nil } -func (x *ModifyMessageReactionExtensionsReq) GetClientMsgID() string { +func (x *MsgDataToModifyByMQ) GetConversationID() string { if x != nil { - return x.ClientMsgID + return x.ConversationID } return "" } -func (x *ModifyMessageReactionExtensionsReq) GetEx() *wrapperspb.StringValue { - if x != nil { - return x.Ex - } - return nil -} - -func (x *ModifyMessageReactionExtensionsReq) GetAttachedInfo() *wrapperspb.StringValue { - if x != nil { - return x.AttachedInfo - } - return nil -} - -func (x *ModifyMessageReactionExtensionsReq) GetIsReact() bool { - if x != nil { - return x.IsReact - } - return false -} - -func (x *ModifyMessageReactionExtensionsReq) GetIsExternalExtensions() bool { - if x != nil { - return x.IsExternalExtensions - } - return false -} - -func (x *ModifyMessageReactionExtensionsReq) GetMsgFirstModifyTime() int64 { - if x != nil { - return x.MsgFirstModifyTime - } - return 0 -} - -type SetMessageReactionExtensionsReq struct { +type DelMsgsReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - ConversationID string `protobuf:"bytes,1,opt,name=conversationID,proto3" json:"conversationID"` - SessionType int32 `protobuf:"varint,2,opt,name=sessionType,proto3" json:"sessionType"` - ReactionExtensions map[string]*sdkws.KeyValue `protobuf:"bytes,3,rep,name=reactionExtensions,proto3" json:"reactionExtensions" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - ClientMsgID string `protobuf:"bytes,4,opt,name=clientMsgID,proto3" json:"clientMsgID"` - Ex *wrapperspb.StringValue `protobuf:"bytes,5,opt,name=ex,proto3" json:"ex"` - AttachedInfo *wrapperspb.StringValue `protobuf:"bytes,6,opt,name=attachedInfo,proto3" json:"attachedInfo"` - IsReact bool `protobuf:"varint,7,opt,name=isReact,proto3" json:"isReact"` - IsExternalExtensions bool `protobuf:"varint,8,opt,name=isExternalExtensions,proto3" json:"isExternalExtensions"` - MsgFirstModifyTime int64 `protobuf:"varint,9,opt,name=msgFirstModifyTime,proto3" json:"msgFirstModifyTime"` } -func (x *SetMessageReactionExtensionsReq) Reset() { - *x = SetMessageReactionExtensionsReq{} +func (x *DelMsgsReq) Reset() { + *x = DelMsgsReq{} if protoimpl.UnsafeEnabled { mi := &file_msg_msg_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -766,13 +697,13 @@ func (x *SetMessageReactionExtensionsReq) Reset() { } } -func (x *SetMessageReactionExtensionsReq) String() string { +func (x *DelMsgsReq) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SetMessageReactionExtensionsReq) ProtoMessage() {} +func (*DelMsgsReq) ProtoMessage() {} -func (x *SetMessageReactionExtensionsReq) ProtoReflect() protoreflect.Message { +func (x *DelMsgsReq) ProtoReflect() protoreflect.Message { mi := &file_msg_msg_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -784,87 +715,19 @@ func (x *SetMessageReactionExtensionsReq) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SetMessageReactionExtensionsReq.ProtoReflect.Descriptor instead. -func (*SetMessageReactionExtensionsReq) Descriptor() ([]byte, []int) { +// Deprecated: Use DelMsgsReq.ProtoReflect.Descriptor instead. +func (*DelMsgsReq) Descriptor() ([]byte, []int) { return file_msg_msg_proto_rawDescGZIP(), []int{13} } -func (x *SetMessageReactionExtensionsReq) GetConversationID() string { - if x != nil { - return x.ConversationID - } - return "" -} - -func (x *SetMessageReactionExtensionsReq) GetSessionType() int32 { - if x != nil { - return x.SessionType - } - return 0 -} - -func (x *SetMessageReactionExtensionsReq) GetReactionExtensions() map[string]*sdkws.KeyValue { - if x != nil { - return x.ReactionExtensions - } - return nil -} - -func (x *SetMessageReactionExtensionsReq) GetClientMsgID() string { - if x != nil { - return x.ClientMsgID - } - return "" -} - -func (x *SetMessageReactionExtensionsReq) GetEx() *wrapperspb.StringValue { - if x != nil { - return x.Ex - } - return nil -} - -func (x *SetMessageReactionExtensionsReq) GetAttachedInfo() *wrapperspb.StringValue { - if x != nil { - return x.AttachedInfo - } - return nil -} - -func (x *SetMessageReactionExtensionsReq) GetIsReact() bool { - if x != nil { - return x.IsReact - } - return false -} - -func (x *SetMessageReactionExtensionsReq) GetIsExternalExtensions() bool { - if x != nil { - return x.IsExternalExtensions - } - return false -} - -func (x *SetMessageReactionExtensionsReq) GetMsgFirstModifyTime() int64 { - if x != nil { - return x.MsgFirstModifyTime - } - return 0 -} - -type SetMessageReactionExtensionsResp struct { +type DelMsgsResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - ClientMsgID string `protobuf:"bytes,1,opt,name=clientMsgID,proto3" json:"clientMsgID"` - MsgFirstModifyTime int64 `protobuf:"varint,2,opt,name=msgFirstModifyTime,proto3" json:"msgFirstModifyTime"` - IsReact bool `protobuf:"varint,3,opt,name=isReact,proto3" json:"isReact"` - Result []*KeyValueResp `protobuf:"bytes,4,rep,name=result,proto3" json:"result"` } -func (x *SetMessageReactionExtensionsResp) Reset() { - *x = SetMessageReactionExtensionsResp{} +func (x *DelMsgsResp) Reset() { + *x = DelMsgsResp{} if protoimpl.UnsafeEnabled { mi := &file_msg_msg_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -872,13 +735,13 @@ func (x *SetMessageReactionExtensionsResp) Reset() { } } -func (x *SetMessageReactionExtensionsResp) String() string { +func (x *DelMsgsResp) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SetMessageReactionExtensionsResp) ProtoMessage() {} +func (*DelMsgsResp) ProtoMessage() {} -func (x *SetMessageReactionExtensionsResp) ProtoReflect() protoreflect.Message { +func (x *DelMsgsResp) ProtoReflect() protoreflect.Message { mi := &file_msg_msg_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -890,52 +753,23 @@ func (x *SetMessageReactionExtensionsResp) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SetMessageReactionExtensionsResp.ProtoReflect.Descriptor instead. -func (*SetMessageReactionExtensionsResp) Descriptor() ([]byte, []int) { +// Deprecated: Use DelMsgsResp.ProtoReflect.Descriptor instead. +func (*DelMsgsResp) Descriptor() ([]byte, []int) { return file_msg_msg_proto_rawDescGZIP(), []int{14} } -func (x *SetMessageReactionExtensionsResp) GetClientMsgID() string { - if x != nil { - return x.ClientMsgID - } - return "" -} - -func (x *SetMessageReactionExtensionsResp) GetMsgFirstModifyTime() int64 { - if x != nil { - return x.MsgFirstModifyTime - } - return 0 -} - -func (x *SetMessageReactionExtensionsResp) GetIsReact() bool { - if x != nil { - return x.IsReact - } - return false -} - -func (x *SetMessageReactionExtensionsResp) GetResult() []*KeyValueResp { - if x != nil { - return x.Result - } - return nil -} - -type GetMessagesReactionExtensionsReq struct { +type RevokeMsgReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ConversationID string `protobuf:"bytes,1,opt,name=conversationID,proto3" json:"conversationID"` - SessionType int32 `protobuf:"varint,2,opt,name=sessionType,proto3" json:"sessionType"` - MessageReactionKeys []*GetMessagesReactionExtensionsReq_MessageReactionKey `protobuf:"bytes,3,rep,name=messageReactionKeys,proto3" json:"messageReactionKeys"` - TypeKeys []string `protobuf:"bytes,4,rep,name=TypeKeys,proto3" json:"TypeKeys"` + ConversationID string `protobuf:"bytes,1,opt,name=conversationID,proto3" json:"conversationID"` + Seq int64 `protobuf:"varint,2,opt,name=seq,proto3" json:"seq"` + UserID string `protobuf:"bytes,3,opt,name=userID,proto3" json:"userID"` } -func (x *GetMessagesReactionExtensionsReq) Reset() { - *x = GetMessagesReactionExtensionsReq{} +func (x *RevokeMsgReq) Reset() { + *x = RevokeMsgReq{} if protoimpl.UnsafeEnabled { mi := &file_msg_msg_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -943,13 +777,13 @@ func (x *GetMessagesReactionExtensionsReq) Reset() { } } -func (x *GetMessagesReactionExtensionsReq) String() string { +func (x *RevokeMsgReq) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetMessagesReactionExtensionsReq) ProtoMessage() {} +func (*RevokeMsgReq) ProtoMessage() {} -func (x *GetMessagesReactionExtensionsReq) ProtoReflect() protoreflect.Message { +func (x *RevokeMsgReq) ProtoReflect() protoreflect.Message { mi := &file_msg_msg_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -961,49 +795,40 @@ func (x *GetMessagesReactionExtensionsReq) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetMessagesReactionExtensionsReq.ProtoReflect.Descriptor instead. -func (*GetMessagesReactionExtensionsReq) Descriptor() ([]byte, []int) { +// Deprecated: Use RevokeMsgReq.ProtoReflect.Descriptor instead. +func (*RevokeMsgReq) Descriptor() ([]byte, []int) { return file_msg_msg_proto_rawDescGZIP(), []int{15} } -func (x *GetMessagesReactionExtensionsReq) GetConversationID() string { +func (x *RevokeMsgReq) GetConversationID() string { if x != nil { return x.ConversationID } return "" } -func (x *GetMessagesReactionExtensionsReq) GetSessionType() int32 { +func (x *RevokeMsgReq) GetSeq() int64 { if x != nil { - return x.SessionType + return x.Seq } return 0 } -func (x *GetMessagesReactionExtensionsReq) GetMessageReactionKeys() []*GetMessagesReactionExtensionsReq_MessageReactionKey { - if x != nil { - return x.MessageReactionKeys - } - return nil -} - -func (x *GetMessagesReactionExtensionsReq) GetTypeKeys() []string { +func (x *RevokeMsgReq) GetUserID() string { if x != nil { - return x.TypeKeys + return x.UserID } - return nil + return "" } -type GetMessagesReactionExtensionsResp struct { +type RevokeMsgResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - SingleMessageResult []*SingleMessageExtensionResult `protobuf:"bytes,1,rep,name=singleMessageResult,proto3" json:"singleMessageResult"` } -func (x *GetMessagesReactionExtensionsResp) Reset() { - *x = GetMessagesReactionExtensionsResp{} +func (x *RevokeMsgResp) Reset() { + *x = RevokeMsgResp{} if protoimpl.UnsafeEnabled { mi := &file_msg_msg_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1011,13 +836,13 @@ func (x *GetMessagesReactionExtensionsResp) Reset() { } } -func (x *GetMessagesReactionExtensionsResp) String() string { +func (x *RevokeMsgResp) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetMessagesReactionExtensionsResp) ProtoMessage() {} +func (*RevokeMsgResp) ProtoMessage() {} -func (x *GetMessagesReactionExtensionsResp) ProtoReflect() protoreflect.Message { +func (x *RevokeMsgResp) ProtoReflect() protoreflect.Message { mi := &file_msg_msg_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1029,29 +854,23 @@ func (x *GetMessagesReactionExtensionsResp) ProtoReflect() protoreflect.Message return mi.MessageOf(x) } -// Deprecated: Use GetMessagesReactionExtensionsResp.ProtoReflect.Descriptor instead. -func (*GetMessagesReactionExtensionsResp) Descriptor() ([]byte, []int) { +// Deprecated: Use RevokeMsgResp.ProtoReflect.Descriptor instead. +func (*RevokeMsgResp) Descriptor() ([]byte, []int) { return file_msg_msg_proto_rawDescGZIP(), []int{16} } -func (x *GetMessagesReactionExtensionsResp) GetSingleMessageResult() []*SingleMessageExtensionResult { - if x != nil { - return x.SingleMessageResult - } - return nil -} - -type SingleMessageExtensionResult struct { +type MarkMsgsAsReadReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ReactionExtensions map[string]*sdkws.KeyValue `protobuf:"bytes,1,rep,name=reactionExtensions,proto3" json:"reactionExtensions" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - ClientMsgID string `protobuf:"bytes,2,opt,name=clientMsgID,proto3" json:"clientMsgID"` + ConversationID string `protobuf:"bytes,1,opt,name=conversationID,proto3" json:"conversationID"` + Seqs []int64 `protobuf:"varint,2,rep,packed,name=seqs,proto3" json:"seqs"` + UserID string `protobuf:"bytes,3,opt,name=userID,proto3" json:"userID"` } -func (x *SingleMessageExtensionResult) Reset() { - *x = SingleMessageExtensionResult{} +func (x *MarkMsgsAsReadReq) Reset() { + *x = MarkMsgsAsReadReq{} if protoimpl.UnsafeEnabled { mi := &file_msg_msg_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1059,13 +878,13 @@ func (x *SingleMessageExtensionResult) Reset() { } } -func (x *SingleMessageExtensionResult) String() string { +func (x *MarkMsgsAsReadReq) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SingleMessageExtensionResult) ProtoMessage() {} +func (*MarkMsgsAsReadReq) ProtoMessage() {} -func (x *SingleMessageExtensionResult) ProtoReflect() protoreflect.Message { +func (x *MarkMsgsAsReadReq) ProtoReflect() protoreflect.Message { mi := &file_msg_msg_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1077,36 +896,40 @@ func (x *SingleMessageExtensionResult) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SingleMessageExtensionResult.ProtoReflect.Descriptor instead. -func (*SingleMessageExtensionResult) Descriptor() ([]byte, []int) { +// Deprecated: Use MarkMsgsAsReadReq.ProtoReflect.Descriptor instead. +func (*MarkMsgsAsReadReq) Descriptor() ([]byte, []int) { return file_msg_msg_proto_rawDescGZIP(), []int{17} } -func (x *SingleMessageExtensionResult) GetReactionExtensions() map[string]*sdkws.KeyValue { +func (x *MarkMsgsAsReadReq) GetConversationID() string { + if x != nil { + return x.ConversationID + } + return "" +} + +func (x *MarkMsgsAsReadReq) GetSeqs() []int64 { if x != nil { - return x.ReactionExtensions + return x.Seqs } return nil } -func (x *SingleMessageExtensionResult) GetClientMsgID() string { +func (x *MarkMsgsAsReadReq) GetUserID() string { if x != nil { - return x.ClientMsgID + return x.UserID } return "" } -type ModifyMessageReactionExtensionsResp struct { +type MarkMsgsAsReadResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - SuccessList []*ExtendMsgResp `protobuf:"bytes,1,rep,name=successList,proto3" json:"successList"` - FailedList []*ExtendMsgResp `protobuf:"bytes,2,rep,name=failedList,proto3" json:"failedList"` } -func (x *ModifyMessageReactionExtensionsResp) Reset() { - *x = ModifyMessageReactionExtensionsResp{} +func (x *MarkMsgsAsReadResp) Reset() { + *x = MarkMsgsAsReadResp{} if protoimpl.UnsafeEnabled { mi := &file_msg_msg_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1114,13 +937,13 @@ func (x *ModifyMessageReactionExtensionsResp) Reset() { } } -func (x *ModifyMessageReactionExtensionsResp) String() string { +func (x *MarkMsgsAsReadResp) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ModifyMessageReactionExtensionsResp) ProtoMessage() {} +func (*MarkMsgsAsReadResp) ProtoMessage() {} -func (x *ModifyMessageReactionExtensionsResp) ProtoReflect() protoreflect.Message { +func (x *MarkMsgsAsReadResp) ProtoReflect() protoreflect.Message { mi := &file_msg_msg_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1132,42 +955,24 @@ func (x *ModifyMessageReactionExtensionsResp) ProtoReflect() protoreflect.Messag return mi.MessageOf(x) } -// Deprecated: Use ModifyMessageReactionExtensionsResp.ProtoReflect.Descriptor instead. -func (*ModifyMessageReactionExtensionsResp) Descriptor() ([]byte, []int) { +// Deprecated: Use MarkMsgsAsReadResp.ProtoReflect.Descriptor instead. +func (*MarkMsgsAsReadResp) Descriptor() ([]byte, []int) { return file_msg_msg_proto_rawDescGZIP(), []int{18} } -func (x *ModifyMessageReactionExtensionsResp) GetSuccessList() []*ExtendMsgResp { - if x != nil { - return x.SuccessList - } - return nil -} - -func (x *ModifyMessageReactionExtensionsResp) GetFailedList() []*ExtendMsgResp { - if x != nil { - return x.FailedList - } - return nil -} - -type DeleteMessagesReactionExtensionsReq struct { +type MarkConversationAsReadReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - OperationID string `protobuf:"bytes,1,opt,name=operationID,proto3" json:"operationID"` - OpUserID string `protobuf:"bytes,2,opt,name=opUserID,proto3" json:"opUserID"` - ConversationID string `protobuf:"bytes,3,opt,name=conversationID,proto3" json:"conversationID"` - SessionType int32 `protobuf:"varint,4,opt,name=sessionType,proto3" json:"sessionType"` - ClientMsgID string `protobuf:"bytes,5,opt,name=clientMsgID,proto3" json:"clientMsgID"` - IsExternalExtensions bool `protobuf:"varint,6,opt,name=isExternalExtensions,proto3" json:"isExternalExtensions"` - MsgFirstModifyTime int64 `protobuf:"varint,7,opt,name=msgFirstModifyTime,proto3" json:"msgFirstModifyTime"` - ReactionExtensions []*sdkws.KeyValue `protobuf:"bytes,8,rep,name=reactionExtensions,proto3" json:"reactionExtensions"` + ConversationID string `protobuf:"bytes,1,opt,name=conversationID,proto3" json:"conversationID"` + UserID string `protobuf:"bytes,2,opt,name=userID,proto3" json:"userID"` + HasReadSeq int64 `protobuf:"varint,3,opt,name=hasReadSeq,proto3" json:"hasReadSeq"` + Seqs []int64 `protobuf:"varint,4,rep,packed,name=seqs,proto3" json:"seqs"` } -func (x *DeleteMessagesReactionExtensionsReq) Reset() { - *x = DeleteMessagesReactionExtensionsReq{} +func (x *MarkConversationAsReadReq) Reset() { + *x = MarkConversationAsReadReq{} if protoimpl.UnsafeEnabled { mi := &file_msg_msg_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1175,13 +980,13 @@ func (x *DeleteMessagesReactionExtensionsReq) Reset() { } } -func (x *DeleteMessagesReactionExtensionsReq) String() string { +func (x *MarkConversationAsReadReq) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DeleteMessagesReactionExtensionsReq) ProtoMessage() {} +func (*MarkConversationAsReadReq) ProtoMessage() {} -func (x *DeleteMessagesReactionExtensionsReq) ProtoReflect() protoreflect.Message { +func (x *MarkConversationAsReadReq) ProtoReflect() protoreflect.Message { mi := &file_msg_msg_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1193,77 +998,47 @@ func (x *DeleteMessagesReactionExtensionsReq) ProtoReflect() protoreflect.Messag return mi.MessageOf(x) } -// Deprecated: Use DeleteMessagesReactionExtensionsReq.ProtoReflect.Descriptor instead. -func (*DeleteMessagesReactionExtensionsReq) Descriptor() ([]byte, []int) { +// Deprecated: Use MarkConversationAsReadReq.ProtoReflect.Descriptor instead. +func (*MarkConversationAsReadReq) Descriptor() ([]byte, []int) { return file_msg_msg_proto_rawDescGZIP(), []int{19} } -func (x *DeleteMessagesReactionExtensionsReq) GetOperationID() string { - if x != nil { - return x.OperationID - } - return "" -} - -func (x *DeleteMessagesReactionExtensionsReq) GetOpUserID() string { +func (x *MarkConversationAsReadReq) GetConversationID() string { if x != nil { - return x.OpUserID + return x.ConversationID } return "" } -func (x *DeleteMessagesReactionExtensionsReq) GetConversationID() string { +func (x *MarkConversationAsReadReq) GetUserID() string { if x != nil { - return x.ConversationID + return x.UserID } return "" } -func (x *DeleteMessagesReactionExtensionsReq) GetSessionType() int32 { +func (x *MarkConversationAsReadReq) GetHasReadSeq() int64 { if x != nil { - return x.SessionType + return x.HasReadSeq } return 0 } -func (x *DeleteMessagesReactionExtensionsReq) GetClientMsgID() string { +func (x *MarkConversationAsReadReq) GetSeqs() []int64 { if x != nil { - return x.ClientMsgID + return x.Seqs } - return "" + return nil } -func (x *DeleteMessagesReactionExtensionsReq) GetIsExternalExtensions() bool { - if x != nil { - return x.IsExternalExtensions - } - return false -} - -func (x *DeleteMessagesReactionExtensionsReq) GetMsgFirstModifyTime() int64 { - if x != nil { - return x.MsgFirstModifyTime - } - return 0 -} - -func (x *DeleteMessagesReactionExtensionsReq) GetReactionExtensions() []*sdkws.KeyValue { - if x != nil { - return x.ReactionExtensions - } - return nil -} - -type DeleteMessagesReactionExtensionsResp struct { +type MarkConversationAsReadResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - Result []*KeyValueResp `protobuf:"bytes,1,rep,name=result,proto3" json:"result"` } -func (x *DeleteMessagesReactionExtensionsResp) Reset() { - *x = DeleteMessagesReactionExtensionsResp{} +func (x *MarkConversationAsReadResp) Reset() { + *x = MarkConversationAsReadResp{} if protoimpl.UnsafeEnabled { mi := &file_msg_msg_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1271,13 +1046,13 @@ func (x *DeleteMessagesReactionExtensionsResp) Reset() { } } -func (x *DeleteMessagesReactionExtensionsResp) String() string { +func (x *MarkConversationAsReadResp) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DeleteMessagesReactionExtensionsResp) ProtoMessage() {} +func (*MarkConversationAsReadResp) ProtoMessage() {} -func (x *DeleteMessagesReactionExtensionsResp) ProtoReflect() protoreflect.Message { +func (x *MarkConversationAsReadResp) ProtoReflect() protoreflect.Message { mi := &file_msg_msg_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1289,28 +1064,23 @@ func (x *DeleteMessagesReactionExtensionsResp) ProtoReflect() protoreflect.Messa return mi.MessageOf(x) } -// Deprecated: Use DeleteMessagesReactionExtensionsResp.ProtoReflect.Descriptor instead. -func (*DeleteMessagesReactionExtensionsResp) Descriptor() ([]byte, []int) { +// Deprecated: Use MarkConversationAsReadResp.ProtoReflect.Descriptor instead. +func (*MarkConversationAsReadResp) Descriptor() ([]byte, []int) { return file_msg_msg_proto_rawDescGZIP(), []int{20} } -func (x *DeleteMessagesReactionExtensionsResp) GetResult() []*KeyValueResp { - if x != nil { - return x.Result - } - return nil -} - -type ExtendMsgResp struct { +type SetConversationHasReadSeqReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ExtendMsg *ExtendMsg `protobuf:"bytes,1,opt,name=extendMsg,proto3" json:"extendMsg"` + ConversationID string `protobuf:"bytes,1,opt,name=conversationID,proto3" json:"conversationID"` + UserID string `protobuf:"bytes,2,opt,name=userID,proto3" json:"userID"` + HasReadSeq int64 `protobuf:"varint,3,opt,name=hasReadSeq,proto3" json:"hasReadSeq"` } -func (x *ExtendMsgResp) Reset() { - *x = ExtendMsgResp{} +func (x *SetConversationHasReadSeqReq) Reset() { + *x = SetConversationHasReadSeqReq{} if protoimpl.UnsafeEnabled { mi := &file_msg_msg_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1318,13 +1088,13 @@ func (x *ExtendMsgResp) Reset() { } } -func (x *ExtendMsgResp) String() string { +func (x *SetConversationHasReadSeqReq) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ExtendMsgResp) ProtoMessage() {} +func (*SetConversationHasReadSeqReq) ProtoMessage() {} -func (x *ExtendMsgResp) ProtoReflect() protoreflect.Message { +func (x *SetConversationHasReadSeqReq) ProtoReflect() protoreflect.Message { mi := &file_msg_msg_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1336,32 +1106,40 @@ func (x *ExtendMsgResp) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ExtendMsgResp.ProtoReflect.Descriptor instead. -func (*ExtendMsgResp) Descriptor() ([]byte, []int) { +// Deprecated: Use SetConversationHasReadSeqReq.ProtoReflect.Descriptor instead. +func (*SetConversationHasReadSeqReq) Descriptor() ([]byte, []int) { return file_msg_msg_proto_rawDescGZIP(), []int{21} } -func (x *ExtendMsgResp) GetExtendMsg() *ExtendMsg { +func (x *SetConversationHasReadSeqReq) GetConversationID() string { if x != nil { - return x.ExtendMsg + return x.ConversationID } - return nil + return "" +} + +func (x *SetConversationHasReadSeqReq) GetUserID() string { + if x != nil { + return x.UserID + } + return "" } -type ExtendMsg struct { +func (x *SetConversationHasReadSeqReq) GetHasReadSeq() int64 { + if x != nil { + return x.HasReadSeq + } + return 0 +} + +type SetConversationHasReadSeqResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - ReactionExtensions map[string]*KeyValueResp `protobuf:"bytes,1,rep,name=reactionExtensions,proto3" json:"reactionExtensions" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - ClientMsgID string `protobuf:"bytes,2,opt,name=clientMsgID,proto3" json:"clientMsgID"` - MsgFirstModifyTime int64 `protobuf:"varint,3,opt,name=msgFirstModifyTime,proto3" json:"msgFirstModifyTime"` - AttachedInfo string `protobuf:"bytes,4,opt,name=attachedInfo,proto3" json:"attachedInfo"` - Ex string `protobuf:"bytes,5,opt,name=ex,proto3" json:"ex"` } -func (x *ExtendMsg) Reset() { - *x = ExtendMsg{} +func (x *SetConversationHasReadSeqResp) Reset() { + *x = SetConversationHasReadSeqResp{} if protoimpl.UnsafeEnabled { mi := &file_msg_msg_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1369,13 +1147,13 @@ func (x *ExtendMsg) Reset() { } } -func (x *ExtendMsg) String() string { +func (x *SetConversationHasReadSeqResp) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ExtendMsg) ProtoMessage() {} +func (*SetConversationHasReadSeqResp) ProtoMessage() {} -func (x *ExtendMsg) ProtoReflect() protoreflect.Message { +func (x *SetConversationHasReadSeqResp) ProtoReflect() protoreflect.Message { mi := &file_msg_msg_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1387,58 +1165,22 @@ func (x *ExtendMsg) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ExtendMsg.ProtoReflect.Descriptor instead. -func (*ExtendMsg) Descriptor() ([]byte, []int) { +// Deprecated: Use SetConversationHasReadSeqResp.ProtoReflect.Descriptor instead. +func (*SetConversationHasReadSeqResp) Descriptor() ([]byte, []int) { return file_msg_msg_proto_rawDescGZIP(), []int{22} } -func (x *ExtendMsg) GetReactionExtensions() map[string]*KeyValueResp { - if x != nil { - return x.ReactionExtensions - } - return nil -} - -func (x *ExtendMsg) GetClientMsgID() string { - if x != nil { - return x.ClientMsgID - } - return "" -} - -func (x *ExtendMsg) GetMsgFirstModifyTime() int64 { - if x != nil { - return x.MsgFirstModifyTime - } - return 0 -} - -func (x *ExtendMsg) GetAttachedInfo() string { - if x != nil { - return x.AttachedInfo - } - return "" -} - -func (x *ExtendMsg) GetEx() string { - if x != nil { - return x.Ex - } - return "" -} - -type KeyValueResp struct { +type DeleteSyncOpt struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - KeyValue *sdkws.KeyValue `protobuf:"bytes,1,opt,name=keyValue,proto3" json:"keyValue"` - ErrCode int32 `protobuf:"varint,2,opt,name=errCode,proto3" json:"errCode"` - ErrMsg string `protobuf:"bytes,3,opt,name=errMsg,proto3" json:"errMsg"` + IsSyncSelf bool `protobuf:"varint,3,opt,name=IsSyncSelf,proto3" json:"IsSyncSelf"` + IsSyncOther bool `protobuf:"varint,4,opt,name=IsSyncOther,proto3" json:"IsSyncOther"` } -func (x *KeyValueResp) Reset() { - *x = KeyValueResp{} +func (x *DeleteSyncOpt) Reset() { + *x = DeleteSyncOpt{} if protoimpl.UnsafeEnabled { mi := &file_msg_msg_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1446,13 +1188,13 @@ func (x *KeyValueResp) Reset() { } } -func (x *KeyValueResp) String() string { +func (x *DeleteSyncOpt) String() string { return protoimpl.X.MessageStringOf(x) } -func (*KeyValueResp) ProtoMessage() {} +func (*DeleteSyncOpt) ProtoMessage() {} -func (x *KeyValueResp) ProtoReflect() protoreflect.Message { +func (x *DeleteSyncOpt) ProtoReflect() protoreflect.Message { mi := &file_msg_msg_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1464,43 +1206,37 @@ func (x *KeyValueResp) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use KeyValueResp.ProtoReflect.Descriptor instead. -func (*KeyValueResp) Descriptor() ([]byte, []int) { +// Deprecated: Use DeleteSyncOpt.ProtoReflect.Descriptor instead. +func (*DeleteSyncOpt) Descriptor() ([]byte, []int) { return file_msg_msg_proto_rawDescGZIP(), []int{23} } -func (x *KeyValueResp) GetKeyValue() *sdkws.KeyValue { - if x != nil { - return x.KeyValue - } - return nil -} - -func (x *KeyValueResp) GetErrCode() int32 { +func (x *DeleteSyncOpt) GetIsSyncSelf() bool { if x != nil { - return x.ErrCode + return x.IsSyncSelf } - return 0 + return false } -func (x *KeyValueResp) GetErrMsg() string { +func (x *DeleteSyncOpt) GetIsSyncOther() bool { if x != nil { - return x.ErrMsg + return x.IsSyncOther } - return "" + return false } -type MsgDataToModifyByMQ struct { +type ClearConversationsMsgReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Messages []*sdkws.MsgData `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages"` - ConversationID string `protobuf:"bytes,2,opt,name=conversationID,proto3" json:"conversationID"` + ConversationIDs []string `protobuf:"bytes,1,rep,name=conversationIDs,proto3" json:"conversationIDs"` + UserID string `protobuf:"bytes,2,opt,name=userID,proto3" json:"userID"` + DeleteSyncOpt *DeleteSyncOpt `protobuf:"bytes,3,opt,name=deleteSyncOpt,proto3" json:"deleteSyncOpt"` } -func (x *MsgDataToModifyByMQ) Reset() { - *x = MsgDataToModifyByMQ{} +func (x *ClearConversationsMsgReq) Reset() { + *x = ClearConversationsMsgReq{} if protoimpl.UnsafeEnabled { mi := &file_msg_msg_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1508,13 +1244,13 @@ func (x *MsgDataToModifyByMQ) Reset() { } } -func (x *MsgDataToModifyByMQ) String() string { +func (x *ClearConversationsMsgReq) String() string { return protoimpl.X.MessageStringOf(x) } -func (*MsgDataToModifyByMQ) ProtoMessage() {} +func (*ClearConversationsMsgReq) ProtoMessage() {} -func (x *MsgDataToModifyByMQ) ProtoReflect() protoreflect.Message { +func (x *ClearConversationsMsgReq) ProtoReflect() protoreflect.Message { mi := &file_msg_msg_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1526,33 +1262,40 @@ func (x *MsgDataToModifyByMQ) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use MsgDataToModifyByMQ.ProtoReflect.Descriptor instead. -func (*MsgDataToModifyByMQ) Descriptor() ([]byte, []int) { +// Deprecated: Use ClearConversationsMsgReq.ProtoReflect.Descriptor instead. +func (*ClearConversationsMsgReq) Descriptor() ([]byte, []int) { return file_msg_msg_proto_rawDescGZIP(), []int{24} } -func (x *MsgDataToModifyByMQ) GetMessages() []*sdkws.MsgData { +func (x *ClearConversationsMsgReq) GetConversationIDs() []string { if x != nil { - return x.Messages + return x.ConversationIDs } return nil } -func (x *MsgDataToModifyByMQ) GetConversationID() string { +func (x *ClearConversationsMsgReq) GetUserID() string { if x != nil { - return x.ConversationID + return x.UserID } return "" } -type DelMsgsReq struct { +func (x *ClearConversationsMsgReq) GetDeleteSyncOpt() *DeleteSyncOpt { + if x != nil { + return x.DeleteSyncOpt + } + return nil +} + +type ClearConversationsMsgResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } -func (x *DelMsgsReq) Reset() { - *x = DelMsgsReq{} +func (x *ClearConversationsMsgResp) Reset() { + *x = ClearConversationsMsgResp{} if protoimpl.UnsafeEnabled { mi := &file_msg_msg_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1560,13 +1303,13 @@ func (x *DelMsgsReq) Reset() { } } -func (x *DelMsgsReq) String() string { +func (x *ClearConversationsMsgResp) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DelMsgsReq) ProtoMessage() {} +func (*ClearConversationsMsgResp) ProtoMessage() {} -func (x *DelMsgsReq) ProtoReflect() protoreflect.Message { +func (x *ClearConversationsMsgResp) ProtoReflect() protoreflect.Message { mi := &file_msg_msg_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1578,19 +1321,22 @@ func (x *DelMsgsReq) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DelMsgsReq.ProtoReflect.Descriptor instead. -func (*DelMsgsReq) Descriptor() ([]byte, []int) { +// Deprecated: Use ClearConversationsMsgResp.ProtoReflect.Descriptor instead. +func (*ClearConversationsMsgResp) Descriptor() ([]byte, []int) { return file_msg_msg_proto_rawDescGZIP(), []int{25} } -type DelMsgsResp struct { +type UserClearAllMsgReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + UserID string `protobuf:"bytes,1,opt,name=userID,proto3" json:"userID"` + DeleteSyncOpt *DeleteSyncOpt `protobuf:"bytes,3,opt,name=deleteSyncOpt,proto3" json:"deleteSyncOpt"` } -func (x *DelMsgsResp) Reset() { - *x = DelMsgsResp{} +func (x *UserClearAllMsgReq) Reset() { + *x = UserClearAllMsgReq{} if protoimpl.UnsafeEnabled { mi := &file_msg_msg_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1598,13 +1344,13 @@ func (x *DelMsgsResp) Reset() { } } -func (x *DelMsgsResp) String() string { +func (x *UserClearAllMsgReq) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DelMsgsResp) ProtoMessage() {} +func (*UserClearAllMsgReq) ProtoMessage() {} -func (x *DelMsgsResp) ProtoReflect() protoreflect.Message { +func (x *UserClearAllMsgReq) ProtoReflect() protoreflect.Message { mi := &file_msg_msg_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1616,23 +1362,33 @@ func (x *DelMsgsResp) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DelMsgsResp.ProtoReflect.Descriptor instead. -func (*DelMsgsResp) Descriptor() ([]byte, []int) { +// Deprecated: Use UserClearAllMsgReq.ProtoReflect.Descriptor instead. +func (*UserClearAllMsgReq) Descriptor() ([]byte, []int) { return file_msg_msg_proto_rawDescGZIP(), []int{26} } -type RevokeMsgReq struct { +func (x *UserClearAllMsgReq) GetUserID() string { + if x != nil { + return x.UserID + } + return "" +} + +func (x *UserClearAllMsgReq) GetDeleteSyncOpt() *DeleteSyncOpt { + if x != nil { + return x.DeleteSyncOpt + } + return nil +} + +type UserClearAllMsgResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - ConversationID string `protobuf:"bytes,1,opt,name=conversationID,proto3" json:"conversationID"` - Seq int64 `protobuf:"varint,2,opt,name=seq,proto3" json:"seq"` - UserID string `protobuf:"bytes,3,opt,name=userID,proto3" json:"userID"` } -func (x *RevokeMsgReq) Reset() { - *x = RevokeMsgReq{} +func (x *UserClearAllMsgResp) Reset() { + *x = UserClearAllMsgResp{} if protoimpl.UnsafeEnabled { mi := &file_msg_msg_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1640,13 +1396,13 @@ func (x *RevokeMsgReq) Reset() { } } -func (x *RevokeMsgReq) String() string { +func (x *UserClearAllMsgResp) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RevokeMsgReq) ProtoMessage() {} +func (*UserClearAllMsgResp) ProtoMessage() {} -func (x *RevokeMsgReq) ProtoReflect() protoreflect.Message { +func (x *UserClearAllMsgResp) ProtoReflect() protoreflect.Message { mi := &file_msg_msg_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1658,40 +1414,24 @@ func (x *RevokeMsgReq) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use RevokeMsgReq.ProtoReflect.Descriptor instead. -func (*RevokeMsgReq) Descriptor() ([]byte, []int) { +// Deprecated: Use UserClearAllMsgResp.ProtoReflect.Descriptor instead. +func (*UserClearAllMsgResp) Descriptor() ([]byte, []int) { return file_msg_msg_proto_rawDescGZIP(), []int{27} } -func (x *RevokeMsgReq) GetConversationID() string { - if x != nil { - return x.ConversationID - } - return "" -} - -func (x *RevokeMsgReq) GetSeq() int64 { - if x != nil { - return x.Seq - } - return 0 -} - -func (x *RevokeMsgReq) GetUserID() string { - if x != nil { - return x.UserID - } - return "" -} - -type RevokeMsgResp struct { +type DeleteMsgsReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + ConversationID string `protobuf:"bytes,1,opt,name=conversationID,proto3" json:"conversationID"` + Seqs []int64 `protobuf:"varint,2,rep,packed,name=seqs,proto3" json:"seqs"` + UserID string `protobuf:"bytes,3,opt,name=userID,proto3" json:"userID"` + DeleteSyncOpt *DeleteSyncOpt `protobuf:"bytes,4,opt,name=deleteSyncOpt,proto3" json:"deleteSyncOpt"` } -func (x *RevokeMsgResp) Reset() { - *x = RevokeMsgResp{} +func (x *DeleteMsgsReq) Reset() { + *x = DeleteMsgsReq{} if protoimpl.UnsafeEnabled { mi := &file_msg_msg_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1699,13 +1439,13 @@ func (x *RevokeMsgResp) Reset() { } } -func (x *RevokeMsgResp) String() string { +func (x *DeleteMsgsReq) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RevokeMsgResp) ProtoMessage() {} +func (*DeleteMsgsReq) ProtoMessage() {} -func (x *RevokeMsgResp) ProtoReflect() protoreflect.Message { +func (x *DeleteMsgsReq) ProtoReflect() protoreflect.Message { mi := &file_msg_msg_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1717,97 +1457,62 @@ func (x *RevokeMsgResp) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use RevokeMsgResp.ProtoReflect.Descriptor instead. -func (*RevokeMsgResp) Descriptor() ([]byte, []int) { +// Deprecated: Use DeleteMsgsReq.ProtoReflect.Descriptor instead. +func (*DeleteMsgsReq) Descriptor() ([]byte, []int) { return file_msg_msg_proto_rawDescGZIP(), []int{28} } -type MarkMsgsAsReadReq struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ConversationID string `protobuf:"bytes,1,opt,name=conversationID,proto3" json:"conversationID"` - Seqs []int64 `protobuf:"varint,2,rep,packed,name=seqs,proto3" json:"seqs"` - UserID string `protobuf:"bytes,3,opt,name=userID,proto3" json:"userID"` -} - -func (x *MarkMsgsAsReadReq) Reset() { - *x = MarkMsgsAsReadReq{} - if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[29] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *MarkMsgsAsReadReq) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*MarkMsgsAsReadReq) ProtoMessage() {} - -func (x *MarkMsgsAsReadReq) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[29] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use MarkMsgsAsReadReq.ProtoReflect.Descriptor instead. -func (*MarkMsgsAsReadReq) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{29} -} - -func (x *MarkMsgsAsReadReq) GetConversationID() string { +func (x *DeleteMsgsReq) GetConversationID() string { if x != nil { return x.ConversationID } return "" } -func (x *MarkMsgsAsReadReq) GetSeqs() []int64 { +func (x *DeleteMsgsReq) GetSeqs() []int64 { if x != nil { return x.Seqs } return nil } -func (x *MarkMsgsAsReadReq) GetUserID() string { +func (x *DeleteMsgsReq) GetUserID() string { if x != nil { return x.UserID } return "" } -type MarkMsgsAsReadResp struct { +func (x *DeleteMsgsReq) GetDeleteSyncOpt() *DeleteSyncOpt { + if x != nil { + return x.DeleteSyncOpt + } + return nil +} + +type DeleteMsgsResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } -func (x *MarkMsgsAsReadResp) Reset() { - *x = MarkMsgsAsReadResp{} +func (x *DeleteMsgsResp) Reset() { + *x = DeleteMsgsResp{} if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[30] + mi := &file_msg_msg_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *MarkMsgsAsReadResp) String() string { +func (x *DeleteMsgsResp) String() string { return protoimpl.X.MessageStringOf(x) } -func (*MarkMsgsAsReadResp) ProtoMessage() {} +func (*DeleteMsgsResp) ProtoMessage() {} -func (x *MarkMsgsAsReadResp) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[30] +func (x *DeleteMsgsResp) ProtoReflect() protoreflect.Message { + mi := &file_msg_msg_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1818,39 +1523,37 @@ func (x *MarkMsgsAsReadResp) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use MarkMsgsAsReadResp.ProtoReflect.Descriptor instead. -func (*MarkMsgsAsReadResp) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{30} +// Deprecated: Use DeleteMsgsResp.ProtoReflect.Descriptor instead. +func (*DeleteMsgsResp) Descriptor() ([]byte, []int) { + return file_msg_msg_proto_rawDescGZIP(), []int{29} } -type MarkConversationAsReadReq struct { +type DeleteMsgPhysicalReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ConversationID string `protobuf:"bytes,1,opt,name=conversationID,proto3" json:"conversationID"` - UserID string `protobuf:"bytes,2,opt,name=userID,proto3" json:"userID"` - HasReadSeq int64 `protobuf:"varint,3,opt,name=hasReadSeq,proto3" json:"hasReadSeq"` - Seqs []int64 `protobuf:"varint,4,rep,packed,name=seqs,proto3" json:"seqs"` + ConversationIDs []string `protobuf:"bytes,1,rep,name=conversationIDs,proto3" json:"conversationIDs"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp"` } -func (x *MarkConversationAsReadReq) Reset() { - *x = MarkConversationAsReadReq{} +func (x *DeleteMsgPhysicalReq) Reset() { + *x = DeleteMsgPhysicalReq{} if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[31] + mi := &file_msg_msg_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *MarkConversationAsReadReq) String() string { +func (x *DeleteMsgPhysicalReq) String() string { return protoimpl.X.MessageStringOf(x) } -func (*MarkConversationAsReadReq) ProtoMessage() {} +func (*DeleteMsgPhysicalReq) ProtoMessage() {} -func (x *MarkConversationAsReadReq) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[31] +func (x *DeleteMsgPhysicalReq) ProtoReflect() protoreflect.Message { + mi := &file_msg_msg_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1861,62 +1564,48 @@ func (x *MarkConversationAsReadReq) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use MarkConversationAsReadReq.ProtoReflect.Descriptor instead. -func (*MarkConversationAsReadReq) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{31} -} - -func (x *MarkConversationAsReadReq) GetConversationID() string { - if x != nil { - return x.ConversationID - } - return "" +// Deprecated: Use DeleteMsgPhysicalReq.ProtoReflect.Descriptor instead. +func (*DeleteMsgPhysicalReq) Descriptor() ([]byte, []int) { + return file_msg_msg_proto_rawDescGZIP(), []int{30} } -func (x *MarkConversationAsReadReq) GetUserID() string { +func (x *DeleteMsgPhysicalReq) GetConversationIDs() []string { if x != nil { - return x.UserID + return x.ConversationIDs } - return "" + return nil } -func (x *MarkConversationAsReadReq) GetHasReadSeq() int64 { +func (x *DeleteMsgPhysicalReq) GetTimestamp() int64 { if x != nil { - return x.HasReadSeq + return x.Timestamp } return 0 } -func (x *MarkConversationAsReadReq) GetSeqs() []int64 { - if x != nil { - return x.Seqs - } - return nil -} - -type MarkConversationAsReadResp struct { +type DeleteMsgPhysicalResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } -func (x *MarkConversationAsReadResp) Reset() { - *x = MarkConversationAsReadResp{} +func (x *DeleteMsgPhysicalResp) Reset() { + *x = DeleteMsgPhysicalResp{} if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[32] + mi := &file_msg_msg_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *MarkConversationAsReadResp) String() string { +func (x *DeleteMsgPhysicalResp) String() string { return protoimpl.X.MessageStringOf(x) } -func (*MarkConversationAsReadResp) ProtoMessage() {} +func (*DeleteMsgPhysicalResp) ProtoMessage() {} -func (x *MarkConversationAsReadResp) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[32] +func (x *DeleteMsgPhysicalResp) ProtoReflect() protoreflect.Message { + mi := &file_msg_msg_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1927,38 +1616,37 @@ func (x *MarkConversationAsReadResp) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use MarkConversationAsReadResp.ProtoReflect.Descriptor instead. -func (*MarkConversationAsReadResp) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{32} +// Deprecated: Use DeleteMsgPhysicalResp.ProtoReflect.Descriptor instead. +func (*DeleteMsgPhysicalResp) Descriptor() ([]byte, []int) { + return file_msg_msg_proto_rawDescGZIP(), []int{31} } -type SetConversationHasReadSeqReq struct { +type DeleteMsgPhysicalBySeqReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ConversationID string `protobuf:"bytes,1,opt,name=conversationID,proto3" json:"conversationID"` - UserID string `protobuf:"bytes,2,opt,name=userID,proto3" json:"userID"` - HasReadSeq int64 `protobuf:"varint,3,opt,name=hasReadSeq,proto3" json:"hasReadSeq"` + ConversationID string `protobuf:"bytes,1,opt,name=conversationID,proto3" json:"conversationID"` + Seqs []int64 `protobuf:"varint,2,rep,packed,name=seqs,proto3" json:"seqs"` } -func (x *SetConversationHasReadSeqReq) Reset() { - *x = SetConversationHasReadSeqReq{} +func (x *DeleteMsgPhysicalBySeqReq) Reset() { + *x = DeleteMsgPhysicalBySeqReq{} if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[33] + mi := &file_msg_msg_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SetConversationHasReadSeqReq) String() string { +func (x *DeleteMsgPhysicalBySeqReq) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SetConversationHasReadSeqReq) ProtoMessage() {} +func (*DeleteMsgPhysicalBySeqReq) ProtoMessage() {} -func (x *SetConversationHasReadSeqReq) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[33] +func (x *DeleteMsgPhysicalBySeqReq) ProtoReflect() protoreflect.Message { + mi := &file_msg_msg_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1969,55 +1657,48 @@ func (x *SetConversationHasReadSeqReq) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SetConversationHasReadSeqReq.ProtoReflect.Descriptor instead. -func (*SetConversationHasReadSeqReq) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{33} +// Deprecated: Use DeleteMsgPhysicalBySeqReq.ProtoReflect.Descriptor instead. +func (*DeleteMsgPhysicalBySeqReq) Descriptor() ([]byte, []int) { + return file_msg_msg_proto_rawDescGZIP(), []int{32} } -func (x *SetConversationHasReadSeqReq) GetConversationID() string { +func (x *DeleteMsgPhysicalBySeqReq) GetConversationID() string { if x != nil { return x.ConversationID } return "" } -func (x *SetConversationHasReadSeqReq) GetUserID() string { - if x != nil { - return x.UserID - } - return "" -} - -func (x *SetConversationHasReadSeqReq) GetHasReadSeq() int64 { +func (x *DeleteMsgPhysicalBySeqReq) GetSeqs() []int64 { if x != nil { - return x.HasReadSeq + return x.Seqs } - return 0 + return nil } -type SetConversationHasReadSeqResp struct { +type DeleteMsgPhysicalBySeqResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } -func (x *SetConversationHasReadSeqResp) Reset() { - *x = SetConversationHasReadSeqResp{} +func (x *DeleteMsgPhysicalBySeqResp) Reset() { + *x = DeleteMsgPhysicalBySeqResp{} if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[34] + mi := &file_msg_msg_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SetConversationHasReadSeqResp) String() string { +func (x *DeleteMsgPhysicalBySeqResp) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SetConversationHasReadSeqResp) ProtoMessage() {} +func (*DeleteMsgPhysicalBySeqResp) ProtoMessage() {} -func (x *SetConversationHasReadSeqResp) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[34] +func (x *DeleteMsgPhysicalBySeqResp) ProtoReflect() protoreflect.Message { + mi := &file_msg_msg_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2028,37 +1709,36 @@ func (x *SetConversationHasReadSeqResp) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SetConversationHasReadSeqResp.ProtoReflect.Descriptor instead. -func (*SetConversationHasReadSeqResp) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{34} +// Deprecated: Use DeleteMsgPhysicalBySeqResp.ProtoReflect.Descriptor instead. +func (*DeleteMsgPhysicalBySeqResp) Descriptor() ([]byte, []int) { + return file_msg_msg_proto_rawDescGZIP(), []int{33} } -type DeleteSyncOpt struct { +type GetConversationMaxSeqReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - IsSyncSelf bool `protobuf:"varint,3,opt,name=IsSyncSelf,proto3" json:"IsSyncSelf"` - IsSyncOther bool `protobuf:"varint,4,opt,name=IsSyncOther,proto3" json:"IsSyncOther"` + ConversationID string `protobuf:"bytes,1,opt,name=conversationID,proto3" json:"conversationID"` } -func (x *DeleteSyncOpt) Reset() { - *x = DeleteSyncOpt{} +func (x *GetConversationMaxSeqReq) Reset() { + *x = GetConversationMaxSeqReq{} if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[35] + mi := &file_msg_msg_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *DeleteSyncOpt) String() string { +func (x *GetConversationMaxSeqReq) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DeleteSyncOpt) ProtoMessage() {} +func (*GetConversationMaxSeqReq) ProtoMessage() {} -func (x *DeleteSyncOpt) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[35] +func (x *GetConversationMaxSeqReq) ProtoReflect() protoreflect.Message { + mi := &file_msg_msg_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2069,354 +1749,43 @@ func (x *DeleteSyncOpt) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DeleteSyncOpt.ProtoReflect.Descriptor instead. -func (*DeleteSyncOpt) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{35} +// Deprecated: Use GetConversationMaxSeqReq.ProtoReflect.Descriptor instead. +func (*GetConversationMaxSeqReq) Descriptor() ([]byte, []int) { + return file_msg_msg_proto_rawDescGZIP(), []int{34} } -func (x *DeleteSyncOpt) GetIsSyncSelf() bool { +func (x *GetConversationMaxSeqReq) GetConversationID() string { if x != nil { - return x.IsSyncSelf + return x.ConversationID } - return false + return "" } -func (x *DeleteSyncOpt) GetIsSyncOther() bool { - if x != nil { - return x.IsSyncOther - } - return false -} - -type ClearConversationsMsgReq struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ConversationIDs []string `protobuf:"bytes,1,rep,name=conversationIDs,proto3" json:"conversationIDs"` - UserID string `protobuf:"bytes,2,opt,name=userID,proto3" json:"userID"` - DeleteSyncOpt *DeleteSyncOpt `protobuf:"bytes,3,opt,name=deleteSyncOpt,proto3" json:"deleteSyncOpt"` -} - -func (x *ClearConversationsMsgReq) Reset() { - *x = ClearConversationsMsgReq{} - if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[36] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ClearConversationsMsgReq) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ClearConversationsMsgReq) ProtoMessage() {} - -func (x *ClearConversationsMsgReq) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[36] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ClearConversationsMsgReq.ProtoReflect.Descriptor instead. -func (*ClearConversationsMsgReq) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{36} -} - -func (x *ClearConversationsMsgReq) GetConversationIDs() []string { - if x != nil { - return x.ConversationIDs - } - return nil -} - -func (x *ClearConversationsMsgReq) GetUserID() string { - if x != nil { - return x.UserID - } - return "" -} - -func (x *ClearConversationsMsgReq) GetDeleteSyncOpt() *DeleteSyncOpt { - if x != nil { - return x.DeleteSyncOpt - } - return nil -} - -type ClearConversationsMsgResp struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *ClearConversationsMsgResp) Reset() { - *x = ClearConversationsMsgResp{} - if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[37] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ClearConversationsMsgResp) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ClearConversationsMsgResp) ProtoMessage() {} - -func (x *ClearConversationsMsgResp) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[37] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ClearConversationsMsgResp.ProtoReflect.Descriptor instead. -func (*ClearConversationsMsgResp) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{37} -} - -type UserClearAllMsgReq struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - UserID string `protobuf:"bytes,1,opt,name=userID,proto3" json:"userID"` - DeleteSyncOpt *DeleteSyncOpt `protobuf:"bytes,3,opt,name=deleteSyncOpt,proto3" json:"deleteSyncOpt"` -} - -func (x *UserClearAllMsgReq) Reset() { - *x = UserClearAllMsgReq{} - if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[38] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *UserClearAllMsgReq) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UserClearAllMsgReq) ProtoMessage() {} - -func (x *UserClearAllMsgReq) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[38] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UserClearAllMsgReq.ProtoReflect.Descriptor instead. -func (*UserClearAllMsgReq) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{38} -} - -func (x *UserClearAllMsgReq) GetUserID() string { - if x != nil { - return x.UserID - } - return "" -} - -func (x *UserClearAllMsgReq) GetDeleteSyncOpt() *DeleteSyncOpt { - if x != nil { - return x.DeleteSyncOpt - } - return nil -} - -type UserClearAllMsgResp struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *UserClearAllMsgResp) Reset() { - *x = UserClearAllMsgResp{} - if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[39] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *UserClearAllMsgResp) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UserClearAllMsgResp) ProtoMessage() {} - -func (x *UserClearAllMsgResp) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[39] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UserClearAllMsgResp.ProtoReflect.Descriptor instead. -func (*UserClearAllMsgResp) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{39} -} - -type DeleteMsgsReq struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ConversationID string `protobuf:"bytes,1,opt,name=conversationID,proto3" json:"conversationID"` - Seqs []int64 `protobuf:"varint,2,rep,packed,name=seqs,proto3" json:"seqs"` - UserID string `protobuf:"bytes,3,opt,name=userID,proto3" json:"userID"` - DeleteSyncOpt *DeleteSyncOpt `protobuf:"bytes,4,opt,name=deleteSyncOpt,proto3" json:"deleteSyncOpt"` -} - -func (x *DeleteMsgsReq) Reset() { - *x = DeleteMsgsReq{} - if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[40] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DeleteMsgsReq) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteMsgsReq) ProtoMessage() {} - -func (x *DeleteMsgsReq) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[40] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteMsgsReq.ProtoReflect.Descriptor instead. -func (*DeleteMsgsReq) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{40} -} - -func (x *DeleteMsgsReq) GetConversationID() string { - if x != nil { - return x.ConversationID - } - return "" -} - -func (x *DeleteMsgsReq) GetSeqs() []int64 { - if x != nil { - return x.Seqs - } - return nil -} - -func (x *DeleteMsgsReq) GetUserID() string { - if x != nil { - return x.UserID - } - return "" -} - -func (x *DeleteMsgsReq) GetDeleteSyncOpt() *DeleteSyncOpt { - if x != nil { - return x.DeleteSyncOpt - } - return nil -} - -type DeleteMsgsResp struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *DeleteMsgsResp) Reset() { - *x = DeleteMsgsResp{} - if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[41] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DeleteMsgsResp) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteMsgsResp) ProtoMessage() {} - -func (x *DeleteMsgsResp) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[41] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteMsgsResp.ProtoReflect.Descriptor instead. -func (*DeleteMsgsResp) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{41} -} - -type DeleteMsgPhysicalReq struct { +type GetConversationMaxSeqResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ConversationIDs []string `protobuf:"bytes,1,rep,name=conversationIDs,proto3" json:"conversationIDs"` - Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp"` + MaxSeq int64 `protobuf:"varint,1,opt,name=maxSeq,proto3" json:"maxSeq"` } -func (x *DeleteMsgPhysicalReq) Reset() { - *x = DeleteMsgPhysicalReq{} +func (x *GetConversationMaxSeqResp) Reset() { + *x = GetConversationMaxSeqResp{} if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[42] + mi := &file_msg_msg_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *DeleteMsgPhysicalReq) String() string { +func (x *GetConversationMaxSeqResp) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DeleteMsgPhysicalReq) ProtoMessage() {} +func (*GetConversationMaxSeqResp) ProtoMessage() {} -func (x *DeleteMsgPhysicalReq) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[42] +func (x *GetConversationMaxSeqResp) ProtoReflect() protoreflect.Message { + mi := &file_msg_msg_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2427,48 +1796,43 @@ func (x *DeleteMsgPhysicalReq) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DeleteMsgPhysicalReq.ProtoReflect.Descriptor instead. -func (*DeleteMsgPhysicalReq) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{42} -} - -func (x *DeleteMsgPhysicalReq) GetConversationIDs() []string { - if x != nil { - return x.ConversationIDs - } - return nil +// Deprecated: Use GetConversationMaxSeqResp.ProtoReflect.Descriptor instead. +func (*GetConversationMaxSeqResp) Descriptor() ([]byte, []int) { + return file_msg_msg_proto_rawDescGZIP(), []int{35} } -func (x *DeleteMsgPhysicalReq) GetTimestamp() int64 { +func (x *GetConversationMaxSeqResp) GetMaxSeq() int64 { if x != nil { - return x.Timestamp + return x.MaxSeq } return 0 } -type DeleteMsgPhysicalResp struct { +type GetConversationsHasReadAndMaxSeqReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + UserID string `protobuf:"bytes,1,opt,name=userID,proto3" json:"userID"` } -func (x *DeleteMsgPhysicalResp) Reset() { - *x = DeleteMsgPhysicalResp{} +func (x *GetConversationsHasReadAndMaxSeqReq) Reset() { + *x = GetConversationsHasReadAndMaxSeqReq{} if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[43] + mi := &file_msg_msg_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *DeleteMsgPhysicalResp) String() string { +func (x *GetConversationsHasReadAndMaxSeqReq) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DeleteMsgPhysicalResp) ProtoMessage() {} +func (*GetConversationsHasReadAndMaxSeqReq) ProtoMessage() {} -func (x *DeleteMsgPhysicalResp) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[43] +func (x *GetConversationsHasReadAndMaxSeqReq) ProtoReflect() protoreflect.Message { + mi := &file_msg_msg_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2479,37 +1843,44 @@ func (x *DeleteMsgPhysicalResp) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DeleteMsgPhysicalResp.ProtoReflect.Descriptor instead. -func (*DeleteMsgPhysicalResp) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{43} +// Deprecated: Use GetConversationsHasReadAndMaxSeqReq.ProtoReflect.Descriptor instead. +func (*GetConversationsHasReadAndMaxSeqReq) Descriptor() ([]byte, []int) { + return file_msg_msg_proto_rawDescGZIP(), []int{36} } -type DeleteMsgPhysicalBySeqReq struct { +func (x *GetConversationsHasReadAndMaxSeqReq) GetUserID() string { + if x != nil { + return x.UserID + } + return "" +} + +type Seqs struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ConversationID string `protobuf:"bytes,1,opt,name=conversationID,proto3" json:"conversationID"` - Seqs []int64 `protobuf:"varint,2,rep,packed,name=seqs,proto3" json:"seqs"` + MaxSeq int64 `protobuf:"varint,1,opt,name=maxSeq,proto3" json:"maxSeq"` + HasReadSeq int64 `protobuf:"varint,2,opt,name=hasReadSeq,proto3" json:"hasReadSeq"` } -func (x *DeleteMsgPhysicalBySeqReq) Reset() { - *x = DeleteMsgPhysicalBySeqReq{} +func (x *Seqs) Reset() { + *x = Seqs{} if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[44] + mi := &file_msg_msg_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *DeleteMsgPhysicalBySeqReq) String() string { +func (x *Seqs) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DeleteMsgPhysicalBySeqReq) ProtoMessage() {} +func (*Seqs) ProtoMessage() {} -func (x *DeleteMsgPhysicalBySeqReq) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[44] +func (x *Seqs) ProtoReflect() protoreflect.Message { + mi := &file_msg_msg_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2520,48 +1891,50 @@ func (x *DeleteMsgPhysicalBySeqReq) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DeleteMsgPhysicalBySeqReq.ProtoReflect.Descriptor instead. -func (*DeleteMsgPhysicalBySeqReq) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{44} +// Deprecated: Use Seqs.ProtoReflect.Descriptor instead. +func (*Seqs) Descriptor() ([]byte, []int) { + return file_msg_msg_proto_rawDescGZIP(), []int{37} } -func (x *DeleteMsgPhysicalBySeqReq) GetConversationID() string { +func (x *Seqs) GetMaxSeq() int64 { if x != nil { - return x.ConversationID + return x.MaxSeq } - return "" + return 0 } -func (x *DeleteMsgPhysicalBySeqReq) GetSeqs() []int64 { +func (x *Seqs) GetHasReadSeq() int64 { if x != nil { - return x.Seqs + return x.HasReadSeq } - return nil + return 0 } -type DeleteMsgPhysicalBySeqResp struct { +type GetConversationsHasReadAndMaxSeqResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + Seqs map[string]*Seqs `protobuf:"bytes,1,rep,name=seqs,proto3" json:"seqs" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } -func (x *DeleteMsgPhysicalBySeqResp) Reset() { - *x = DeleteMsgPhysicalBySeqResp{} +func (x *GetConversationsHasReadAndMaxSeqResp) Reset() { + *x = GetConversationsHasReadAndMaxSeqResp{} if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[45] + mi := &file_msg_msg_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *DeleteMsgPhysicalBySeqResp) String() string { +func (x *GetConversationsHasReadAndMaxSeqResp) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DeleteMsgPhysicalBySeqResp) ProtoMessage() {} +func (*GetConversationsHasReadAndMaxSeqResp) ProtoMessage() {} -func (x *DeleteMsgPhysicalBySeqResp) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[45] +func (x *GetConversationsHasReadAndMaxSeqResp) ProtoReflect() protoreflect.Message { + mi := &file_msg_msg_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2572,36 +1945,47 @@ func (x *DeleteMsgPhysicalBySeqResp) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DeleteMsgPhysicalBySeqResp.ProtoReflect.Descriptor instead. -func (*DeleteMsgPhysicalBySeqResp) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{45} +// Deprecated: Use GetConversationsHasReadAndMaxSeqResp.ProtoReflect.Descriptor instead. +func (*GetConversationsHasReadAndMaxSeqResp) Descriptor() ([]byte, []int) { + return file_msg_msg_proto_rawDescGZIP(), []int{38} } -type GetConversationMaxSeqReq struct { +func (x *GetConversationsHasReadAndMaxSeqResp) GetSeqs() map[string]*Seqs { + if x != nil { + return x.Seqs + } + return nil +} + +type GetActiveUserReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ConversationID string `protobuf:"bytes,1,opt,name=conversationID,proto3" json:"conversationID"` + Start int64 `protobuf:"varint,1,opt,name=start,proto3" json:"start"` + End int64 `protobuf:"varint,2,opt,name=end,proto3" json:"end"` + Ase bool `protobuf:"varint,3,opt,name=ase,proto3" json:"ase"` + Group bool `protobuf:"varint,4,opt,name=group,proto3" json:"group"` + Pagination *sdkws.RequestPagination `protobuf:"bytes,5,opt,name=pagination,proto3" json:"pagination"` } -func (x *GetConversationMaxSeqReq) Reset() { - *x = GetConversationMaxSeqReq{} +func (x *GetActiveUserReq) Reset() { + *x = GetActiveUserReq{} if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[46] + mi := &file_msg_msg_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetConversationMaxSeqReq) String() string { +func (x *GetActiveUserReq) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetConversationMaxSeqReq) ProtoMessage() {} +func (*GetActiveUserReq) ProtoMessage() {} -func (x *GetConversationMaxSeqReq) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[46] +func (x *GetActiveUserReq) ProtoReflect() protoreflect.Message { + mi := &file_msg_msg_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2612,43 +1996,72 @@ func (x *GetConversationMaxSeqReq) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetConversationMaxSeqReq.ProtoReflect.Descriptor instead. -func (*GetConversationMaxSeqReq) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{46} +// Deprecated: Use GetActiveUserReq.ProtoReflect.Descriptor instead. +func (*GetActiveUserReq) Descriptor() ([]byte, []int) { + return file_msg_msg_proto_rawDescGZIP(), []int{39} } -func (x *GetConversationMaxSeqReq) GetConversationID() string { +func (x *GetActiveUserReq) GetStart() int64 { if x != nil { - return x.ConversationID + return x.Start } - return "" + return 0 } -type GetConversationMaxSeqResp struct { +func (x *GetActiveUserReq) GetEnd() int64 { + if x != nil { + return x.End + } + return 0 +} + +func (x *GetActiveUserReq) GetAse() bool { + if x != nil { + return x.Ase + } + return false +} + +func (x *GetActiveUserReq) GetGroup() bool { + if x != nil { + return x.Group + } + return false +} + +func (x *GetActiveUserReq) GetPagination() *sdkws.RequestPagination { + if x != nil { + return x.Pagination + } + return nil +} + +type ActiveUser struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - MaxSeq int64 `protobuf:"varint,1,opt,name=maxSeq,proto3" json:"maxSeq"` + User *sdkws.UserInfo `protobuf:"bytes,1,opt,name=user,proto3" json:"user"` + Count int64 `protobuf:"varint,2,opt,name=count,proto3" json:"count"` } -func (x *GetConversationMaxSeqResp) Reset() { - *x = GetConversationMaxSeqResp{} +func (x *ActiveUser) Reset() { + *x = ActiveUser{} if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[47] + mi := &file_msg_msg_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetConversationMaxSeqResp) String() string { +func (x *ActiveUser) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetConversationMaxSeqResp) ProtoMessage() {} +func (*ActiveUser) ProtoMessage() {} -func (x *GetConversationMaxSeqResp) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[47] +func (x *ActiveUser) ProtoReflect() protoreflect.Message { + mi := &file_msg_msg_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2659,43 +2072,53 @@ func (x *GetConversationMaxSeqResp) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetConversationMaxSeqResp.ProtoReflect.Descriptor instead. -func (*GetConversationMaxSeqResp) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{47} +// Deprecated: Use ActiveUser.ProtoReflect.Descriptor instead. +func (*ActiveUser) Descriptor() ([]byte, []int) { + return file_msg_msg_proto_rawDescGZIP(), []int{40} } -func (x *GetConversationMaxSeqResp) GetMaxSeq() int64 { +func (x *ActiveUser) GetUser() *sdkws.UserInfo { if x != nil { - return x.MaxSeq + return x.User + } + return nil +} + +func (x *ActiveUser) GetCount() int64 { + if x != nil { + return x.Count } return 0 } -type GetConversationsHasReadAndMaxSeqReq struct { +type GetActiveUserResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - UserID string `protobuf:"bytes,1,opt,name=userID,proto3" json:"userID"` + MsgCount int64 `protobuf:"varint,1,opt,name=msgCount,proto3" json:"msgCount"` + UserCount int64 `protobuf:"varint,2,opt,name=userCount,proto3" json:"userCount"` + DateCount map[string]int64 `protobuf:"bytes,3,rep,name=dateCount,proto3" json:"dateCount" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + Users []*ActiveUser `protobuf:"bytes,4,rep,name=users,proto3" json:"users"` } -func (x *GetConversationsHasReadAndMaxSeqReq) Reset() { - *x = GetConversationsHasReadAndMaxSeqReq{} +func (x *GetActiveUserResp) Reset() { + *x = GetActiveUserResp{} if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[48] + mi := &file_msg_msg_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetConversationsHasReadAndMaxSeqReq) String() string { +func (x *GetActiveUserResp) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetConversationsHasReadAndMaxSeqReq) ProtoMessage() {} +func (*GetActiveUserResp) ProtoMessage() {} -func (x *GetConversationsHasReadAndMaxSeqReq) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[48] +func (x *GetActiveUserResp) ProtoReflect() protoreflect.Message { + mi := &file_msg_msg_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2706,44 +2129,67 @@ func (x *GetConversationsHasReadAndMaxSeqReq) ProtoReflect() protoreflect.Messag return mi.MessageOf(x) } -// Deprecated: Use GetConversationsHasReadAndMaxSeqReq.ProtoReflect.Descriptor instead. -func (*GetConversationsHasReadAndMaxSeqReq) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{48} +// Deprecated: Use GetActiveUserResp.ProtoReflect.Descriptor instead. +func (*GetActiveUserResp) Descriptor() ([]byte, []int) { + return file_msg_msg_proto_rawDescGZIP(), []int{41} } -func (x *GetConversationsHasReadAndMaxSeqReq) GetUserID() string { +func (x *GetActiveUserResp) GetMsgCount() int64 { if x != nil { - return x.UserID + return x.MsgCount } - return "" + return 0 } -type Seqs struct { +func (x *GetActiveUserResp) GetUserCount() int64 { + if x != nil { + return x.UserCount + } + return 0 +} + +func (x *GetActiveUserResp) GetDateCount() map[string]int64 { + if x != nil { + return x.DateCount + } + return nil +} + +func (x *GetActiveUserResp) GetUsers() []*ActiveUser { + if x != nil { + return x.Users + } + return nil +} + +type GetActiveGroupReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - MaxSeq int64 `protobuf:"varint,1,opt,name=maxSeq,proto3" json:"maxSeq"` - HasReadSeq int64 `protobuf:"varint,2,opt,name=hasReadSeq,proto3" json:"hasReadSeq"` + Start int64 `protobuf:"varint,1,opt,name=start,proto3" json:"start"` + End int64 `protobuf:"varint,2,opt,name=end,proto3" json:"end"` + Ase bool `protobuf:"varint,3,opt,name=ase,proto3" json:"ase"` + Pagination *sdkws.RequestPagination `protobuf:"bytes,4,opt,name=pagination,proto3" json:"pagination"` } -func (x *Seqs) Reset() { - *x = Seqs{} +func (x *GetActiveGroupReq) Reset() { + *x = GetActiveGroupReq{} if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[49] + mi := &file_msg_msg_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *Seqs) String() string { +func (x *GetActiveGroupReq) String() string { return protoimpl.X.MessageStringOf(x) } -func (*Seqs) ProtoMessage() {} +func (*GetActiveGroupReq) ProtoMessage() {} -func (x *Seqs) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[49] +func (x *GetActiveGroupReq) ProtoReflect() protoreflect.Message { + mi := &file_msg_msg_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2754,50 +2200,65 @@ func (x *Seqs) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use Seqs.ProtoReflect.Descriptor instead. -func (*Seqs) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{49} +// Deprecated: Use GetActiveGroupReq.ProtoReflect.Descriptor instead. +func (*GetActiveGroupReq) Descriptor() ([]byte, []int) { + return file_msg_msg_proto_rawDescGZIP(), []int{42} } -func (x *Seqs) GetMaxSeq() int64 { +func (x *GetActiveGroupReq) GetStart() int64 { if x != nil { - return x.MaxSeq + return x.Start } return 0 } -func (x *Seqs) GetHasReadSeq() int64 { +func (x *GetActiveGroupReq) GetEnd() int64 { if x != nil { - return x.HasReadSeq + return x.End } return 0 } -type GetConversationsHasReadAndMaxSeqResp struct { +func (x *GetActiveGroupReq) GetAse() bool { + if x != nil { + return x.Ase + } + return false +} + +func (x *GetActiveGroupReq) GetPagination() *sdkws.RequestPagination { + if x != nil { + return x.Pagination + } + return nil +} + +type ActiveGroup struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Seqs map[string]*Seqs `protobuf:"bytes,1,rep,name=seqs,proto3" json:"seqs" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Group *sdkws.GroupInfo `protobuf:"bytes,1,opt,name=group,proto3" json:"group"` + Count int64 `protobuf:"varint,2,opt,name=count,proto3" json:"count"` } -func (x *GetConversationsHasReadAndMaxSeqResp) Reset() { - *x = GetConversationsHasReadAndMaxSeqResp{} +func (x *ActiveGroup) Reset() { + *x = ActiveGroup{} if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[50] + mi := &file_msg_msg_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetConversationsHasReadAndMaxSeqResp) String() string { +func (x *ActiveGroup) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetConversationsHasReadAndMaxSeqResp) ProtoMessage() {} +func (*ActiveGroup) ProtoMessage() {} -func (x *GetConversationsHasReadAndMaxSeqResp) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[50] +func (x *ActiveGroup) ProtoReflect() protoreflect.Message { + mi := &file_msg_msg_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2808,44 +2269,53 @@ func (x *GetConversationsHasReadAndMaxSeqResp) ProtoReflect() protoreflect.Messa return mi.MessageOf(x) } -// Deprecated: Use GetConversationsHasReadAndMaxSeqResp.ProtoReflect.Descriptor instead. -func (*GetConversationsHasReadAndMaxSeqResp) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{50} +// Deprecated: Use ActiveGroup.ProtoReflect.Descriptor instead. +func (*ActiveGroup) Descriptor() ([]byte, []int) { + return file_msg_msg_proto_rawDescGZIP(), []int{43} } -func (x *GetConversationsHasReadAndMaxSeqResp) GetSeqs() map[string]*Seqs { +func (x *ActiveGroup) GetGroup() *sdkws.GroupInfo { if x != nil { - return x.Seqs + return x.Group } return nil } -type GetMessagesReactionExtensionsReq_MessageReactionKey struct { +func (x *ActiveGroup) GetCount() int64 { + if x != nil { + return x.Count + } + return 0 +} + +type GetActiveGroupResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClientMsgID string `protobuf:"bytes,1,opt,name=clientMsgID,proto3" json:"clientMsgID"` - MsgFirstModifyTime int64 `protobuf:"varint,2,opt,name=msgFirstModifyTime,proto3" json:"msgFirstModifyTime"` + MsgCount int64 `protobuf:"varint,1,opt,name=msgCount,proto3" json:"msgCount"` + GroupCount int64 `protobuf:"varint,2,opt,name=groupCount,proto3" json:"groupCount"` + DateCount map[string]int64 `protobuf:"bytes,3,rep,name=dateCount,proto3" json:"dateCount" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + Groups []*ActiveGroup `protobuf:"bytes,4,rep,name=groups,proto3" json:"groups"` } -func (x *GetMessagesReactionExtensionsReq_MessageReactionKey) Reset() { - *x = GetMessagesReactionExtensionsReq_MessageReactionKey{} +func (x *GetActiveGroupResp) Reset() { + *x = GetActiveGroupResp{} if protoimpl.UnsafeEnabled { - mi := &file_msg_msg_proto_msgTypes[53] + mi := &file_msg_msg_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetMessagesReactionExtensionsReq_MessageReactionKey) String() string { +func (x *GetActiveGroupResp) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetMessagesReactionExtensionsReq_MessageReactionKey) ProtoMessage() {} +func (*GetActiveGroupResp) ProtoMessage() {} -func (x *GetMessagesReactionExtensionsReq_MessageReactionKey) ProtoReflect() protoreflect.Message { - mi := &file_msg_msg_proto_msgTypes[53] +func (x *GetActiveGroupResp) ProtoReflect() protoreflect.Message { + mi := &file_msg_msg_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2856,572 +2326,411 @@ func (x *GetMessagesReactionExtensionsReq_MessageReactionKey) ProtoReflect() pro return mi.MessageOf(x) } -// Deprecated: Use GetMessagesReactionExtensionsReq_MessageReactionKey.ProtoReflect.Descriptor instead. -func (*GetMessagesReactionExtensionsReq_MessageReactionKey) Descriptor() ([]byte, []int) { - return file_msg_msg_proto_rawDescGZIP(), []int{15, 0} +// Deprecated: Use GetActiveGroupResp.ProtoReflect.Descriptor instead. +func (*GetActiveGroupResp) Descriptor() ([]byte, []int) { + return file_msg_msg_proto_rawDescGZIP(), []int{44} } -func (x *GetMessagesReactionExtensionsReq_MessageReactionKey) GetClientMsgID() string { +func (x *GetActiveGroupResp) GetMsgCount() int64 { if x != nil { - return x.ClientMsgID + return x.MsgCount } - return "" + return 0 } -func (x *GetMessagesReactionExtensionsReq_MessageReactionKey) GetMsgFirstModifyTime() int64 { +func (x *GetActiveGroupResp) GetGroupCount() int64 { if x != nil { - return x.MsgFirstModifyTime + return x.GroupCount } return 0 } +func (x *GetActiveGroupResp) GetDateCount() map[string]int64 { + if x != nil { + return x.DateCount + } + return nil +} + +func (x *GetActiveGroupResp) GetGroups() []*ActiveGroup { + if x != nil { + return x.Groups + } + return nil +} + var File_msg_msg_proto protoreflect.FileDescriptor var file_msg_msg_proto_rawDesc = []byte{ 0x0a, 0x0d, 0x6d, 0x73, 0x67, 0x2f, 0x6d, 0x73, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x1a, 0x11, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2f, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x70, 0x62, - 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x22, 0x5a, 0x0a, 0x0b, 0x4d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x54, 0x6f, 0x4d, 0x51, - 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x35, 0x0a, 0x07, 0x6d, 0x73, 0x67, 0x44, 0x61, 0x74, - 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x4d, 0x73, 0x67, - 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x6d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x22, 0x44, 0x0a, - 0x0b, 0x4d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x54, 0x6f, 0x44, 0x42, 0x12, 0x35, 0x0a, 0x07, - 0x6d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, - 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, - 0x77, 0x73, 0x2e, 0x4d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x6d, 0x73, 0x67, 0x44, - 0x61, 0x74, 0x61, 0x22, 0x70, 0x0a, 0x0f, 0x50, 0x75, 0x73, 0x68, 0x4d, 0x73, 0x67, 0x44, 0x61, - 0x74, 0x61, 0x54, 0x6f, 0x4d, 0x51, 0x12, 0x35, 0x0a, 0x07, 0x6d, 0x73, 0x67, 0x44, 0x61, 0x74, - 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x4d, 0x73, 0x67, - 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x6d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x26, 0x0a, - 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x22, 0x8d, 0x01, 0x0a, 0x12, 0x4d, 0x73, 0x67, 0x44, 0x61, 0x74, - 0x61, 0x54, 0x6f, 0x4d, 0x6f, 0x6e, 0x67, 0x6f, 0x42, 0x79, 0x4d, 0x51, 0x12, 0x18, 0x0a, 0x07, - 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x71, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6c, - 0x61, 0x73, 0x74, 0x53, 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, - 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, - 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x35, - 0x0a, 0x07, 0x6d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, - 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x4d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x6d, 0x73, - 0x67, 0x44, 0x61, 0x74, 0x61, 0x22, 0x2c, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x78, 0x41, - 0x6e, 0x64, 0x4d, 0x69, 0x6e, 0x53, 0x65, 0x71, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x55, - 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x55, 0x73, 0x65, - 0x72, 0x49, 0x44, 0x22, 0x45, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x78, 0x41, 0x6e, 0x64, - 0x4d, 0x69, 0x6e, 0x53, 0x65, 0x71, 0x52, 0x65, 0x73, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x4d, 0x61, - 0x78, 0x53, 0x65, 0x71, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x4d, 0x61, 0x78, 0x53, - 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x4d, 0x69, 0x6e, 0x53, 0x65, 0x71, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x06, 0x4d, 0x69, 0x6e, 0x53, 0x65, 0x71, 0x22, 0x43, 0x0a, 0x0a, 0x53, 0x65, - 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x71, 0x12, 0x35, 0x0a, 0x07, 0x6d, 0x73, 0x67, 0x44, - 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, - 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x4d, - 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x6d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x22, - 0x6d, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x73, 0x70, 0x12, 0x20, - 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4d, 0x73, 0x67, 0x49, 0x44, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4d, 0x73, 0x67, 0x49, 0x44, - 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x73, 0x67, 0x49, 0x44, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x73, 0x67, - 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x73, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x2d, - 0x0a, 0x13, 0x53, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x16, 0x0a, - 0x14, 0x53, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x15, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, - 0x4d, 0x73, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x22, 0x2e, 0x0a, 0x14, - 0x47, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xed, 0x04, 0x0a, - 0x22, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, - 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x7c, 0x0a, - 0x12, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, - 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x4d, 0x6f, 0x64, - 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x2e, - 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x73, 0x67, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x73, 0x67, 0x49, 0x44, 0x12, 0x32, 0x0a, - 0x02, 0x65, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x4f, 0x70, 0x65, 0x6e, - 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x02, 0x65, - 0x78, 0x12, 0x46, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x49, 0x6e, 0x66, - 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x61, 0x74, 0x74, - 0x61, 0x63, 0x68, 0x65, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x73, 0x52, - 0x65, 0x61, 0x63, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x52, 0x65, - 0x61, 0x63, 0x74, 0x12, 0x32, 0x0a, 0x14, 0x69, 0x73, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x14, 0x69, 0x73, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x45, 0x78, 0x74, - 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x6d, 0x73, 0x67, 0x46, 0x69, - 0x72, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x12, 0x6d, 0x73, 0x67, 0x46, 0x69, 0x72, 0x73, 0x74, 0x4d, 0x6f, 0x64, - 0x69, 0x66, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x1a, 0x63, 0x0a, 0x17, 0x52, 0x65, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xe7, 0x04, 0x0a, - 0x1f, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5a, 0x0a, 0x0b, 0x4d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x54, + 0x6f, 0x4d, 0x51, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x35, 0x0a, 0x07, 0x6d, 0x73, 0x67, + 0x44, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x4f, 0x70, 0x65, + 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, + 0x4d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x6d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, + 0x22, 0x44, 0x0a, 0x0b, 0x4d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x54, 0x6f, 0x44, 0x42, 0x12, + 0x35, 0x0a, 0x07, 0x6d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x4d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x6d, + 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x22, 0x70, 0x0a, 0x0f, 0x50, 0x75, 0x73, 0x68, 0x4d, 0x73, + 0x67, 0x44, 0x61, 0x74, 0x61, 0x54, 0x6f, 0x4d, 0x51, 0x12, 0x35, 0x0a, 0x07, 0x6d, 0x73, 0x67, + 0x44, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x4f, 0x70, 0x65, + 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, + 0x4d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x6d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, - 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x79, 0x0a, 0x12, 0x72, 0x65, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x49, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, - 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x12, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, - 0x73, 0x67, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x4d, 0x73, 0x67, 0x49, 0x44, 0x12, 0x32, 0x0a, 0x02, 0x65, 0x78, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, - 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x02, 0x65, 0x78, 0x12, 0x46, 0x0a, 0x0c, 0x61, - 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x22, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x49, - 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, 0x12, 0x32, 0x0a, - 0x14, 0x69, 0x73, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x69, 0x73, 0x45, - 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, - 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x6d, 0x73, 0x67, 0x46, 0x69, 0x72, 0x73, 0x74, 0x4d, 0x6f, 0x64, - 0x69, 0x66, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6d, - 0x73, 0x67, 0x46, 0x69, 0x72, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x54, 0x69, 0x6d, - 0x65, 0x1a, 0x63, 0x0a, 0x17, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, - 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x32, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, - 0x77, 0x73, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc6, 0x01, 0x0a, 0x20, 0x53, 0x65, 0x74, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, - 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x20, 0x0a, 0x0b, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x73, 0x67, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x73, 0x67, 0x49, 0x44, 0x12, 0x2e, 0x0a, - 0x12, 0x6d, 0x73, 0x67, 0x46, 0x69, 0x72, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x54, - 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6d, 0x73, 0x67, 0x46, 0x69, - 0x72, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, - 0x07, 0x69, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, - 0x69, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, 0x12, 0x36, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, - 0xe9, 0x02, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, - 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, - 0x73, 0x52, 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x22, 0x8d, 0x01, 0x0a, 0x12, 0x4d, 0x73, 0x67, + 0x44, 0x61, 0x74, 0x61, 0x54, 0x6f, 0x4d, 0x6f, 0x6e, 0x67, 0x6f, 0x42, 0x79, 0x4d, 0x51, 0x12, + 0x18, 0x0a, 0x07, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x71, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x07, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x44, 0x12, 0x35, 0x0a, 0x07, 0x6d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x4d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x52, + 0x07, 0x6d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x22, 0x2c, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4d, + 0x61, 0x78, 0x41, 0x6e, 0x64, 0x4d, 0x69, 0x6e, 0x53, 0x65, 0x71, 0x52, 0x65, 0x71, 0x12, 0x16, + 0x0a, 0x06, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x22, 0x45, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x78, + 0x41, 0x6e, 0x64, 0x4d, 0x69, 0x6e, 0x53, 0x65, 0x71, 0x52, 0x65, 0x73, 0x70, 0x12, 0x16, 0x0a, + 0x06, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x4d, + 0x61, 0x78, 0x53, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x4d, 0x69, 0x6e, 0x53, 0x65, 0x71, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x4d, 0x69, 0x6e, 0x53, 0x65, 0x71, 0x22, 0x43, 0x0a, + 0x0a, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x71, 0x12, 0x35, 0x0a, 0x07, 0x6d, + 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x4f, + 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, + 0x73, 0x2e, 0x4d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x6d, 0x73, 0x67, 0x44, 0x61, + 0x74, 0x61, 0x22, 0x6d, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x73, + 0x70, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4d, 0x73, 0x67, 0x49, 0x44, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4d, 0x73, + 0x67, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x73, 0x67, + 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x4d, 0x73, 0x67, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x73, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, + 0x65, 0x22, 0x2d, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x22, 0x16, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x15, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, + 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x22, + 0x2e, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, + 0x76, 0x0a, 0x13, 0x4d, 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x54, 0x6f, 0x4d, 0x6f, 0x64, 0x69, + 0x66, 0x79, 0x42, 0x79, 0x4d, 0x51, 0x12, 0x37, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, + 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x4d, 0x73, + 0x67, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, + 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x22, 0x0c, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x4d, 0x73, + 0x67, 0x73, 0x52, 0x65, 0x71, 0x22, 0x0d, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x4d, 0x73, 0x67, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x22, 0x60, 0x0a, 0x0c, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x4d, 0x73, + 0x67, 0x52, 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x77, - 0x0a, 0x13, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x4f, 0x70, - 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x47, - 0x65, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x2e, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4b, - 0x65, 0x79, 0x52, 0x13, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x54, 0x79, 0x70, 0x65, 0x4b, - 0x65, 0x79, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x54, 0x79, 0x70, 0x65, 0x4b, - 0x65, 0x79, 0x73, 0x1a, 0x66, 0x0a, 0x12, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x4d, 0x73, 0x67, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x73, 0x67, 0x49, 0x44, 0x12, 0x2e, 0x0a, 0x12, 0x6d, - 0x73, 0x67, 0x46, 0x69, 0x72, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x54, 0x69, 0x6d, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6d, 0x73, 0x67, 0x46, 0x69, 0x72, 0x73, - 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x21, - 0x47, 0x65, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x12, 0x60, 0x0a, 0x13, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, - 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, - 0x67, 0x2e, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, - 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x13, - 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x22, 0x9d, 0x02, 0x0a, 0x1c, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x12, 0x76, 0x0a, 0x12, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x46, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, - 0x6d, 0x73, 0x67, 0x2e, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x2e, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x20, 0x0a, 0x0b, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x73, 0x67, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x73, 0x67, 0x49, 0x44, 0x1a, 0x63, - 0x0a, 0x17, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x4f, 0x70, 0x65, - 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, - 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0xa9, 0x01, 0x0a, 0x23, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, - 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x41, 0x0a, 0x0b, 0x73, - 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x10, 0x0a, 0x03, + 0x73, 0x65, 0x71, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x73, 0x65, 0x71, 0x12, 0x16, + 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x22, 0x0f, 0x0a, 0x0d, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, + 0x4d, 0x73, 0x67, 0x52, 0x65, 0x73, 0x70, 0x22, 0x67, 0x0a, 0x11, 0x4d, 0x61, 0x72, 0x6b, 0x4d, + 0x73, 0x67, 0x73, 0x41, 0x73, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, + 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x71, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x03, 0x52, 0x04, 0x73, 0x65, 0x71, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, + 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, + 0x22, 0x14, 0x0a, 0x12, 0x4d, 0x61, 0x72, 0x6b, 0x4d, 0x73, 0x67, 0x73, 0x41, 0x73, 0x52, 0x65, + 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x22, 0x8f, 0x01, 0x0a, 0x19, 0x4d, 0x61, 0x72, 0x6b, 0x43, + 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x73, 0x52, 0x65, 0x61, + 0x64, 0x52, 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, + 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, + 0x65, 0x72, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x68, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x53, + 0x65, 0x71, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x68, 0x61, 0x73, 0x52, 0x65, 0x61, + 0x64, 0x53, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x71, 0x73, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x03, 0x52, 0x04, 0x73, 0x65, 0x71, 0x73, 0x22, 0x1c, 0x0a, 0x1a, 0x4d, 0x61, 0x72, 0x6b, + 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x73, 0x52, 0x65, + 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x22, 0x7e, 0x0a, 0x1c, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, + 0x53, 0x65, 0x71, 0x52, 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x16, + 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x68, 0x61, 0x73, 0x52, 0x65, 0x61, + 0x64, 0x53, 0x65, 0x71, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x68, 0x61, 0x73, 0x52, + 0x65, 0x61, 0x64, 0x53, 0x65, 0x71, 0x22, 0x1f, 0x0a, 0x1d, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, + 0x53, 0x65, 0x71, 0x52, 0x65, 0x73, 0x70, 0x22, 0x51, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x53, 0x79, 0x6e, 0x63, 0x4f, 0x70, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x49, 0x73, 0x53, 0x79, + 0x6e, 0x63, 0x53, 0x65, 0x6c, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x49, 0x73, + 0x53, 0x79, 0x6e, 0x63, 0x53, 0x65, 0x6c, 0x66, 0x12, 0x20, 0x0a, 0x0b, 0x49, 0x73, 0x53, 0x79, + 0x6e, 0x63, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x49, + 0x73, 0x53, 0x79, 0x6e, 0x63, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x22, 0xa3, 0x01, 0x0a, 0x18, 0x43, + 0x6c, 0x65, 0x61, 0x72, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x71, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x76, 0x65, + 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, + 0x73, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x45, 0x0a, 0x0d, 0x64, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x4f, 0x70, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, - 0x6d, 0x73, 0x67, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x73, - 0x70, 0x52, 0x0b, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x3f, - 0x0a, 0x0a, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x52, - 0x65, 0x73, 0x70, 0x52, 0x0a, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x22, - 0x81, 0x03, 0x0a, 0x23, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x70, 0x65, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x70, - 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x55, - 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x70, 0x55, - 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, - 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x20, 0x0a, - 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x20, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x73, 0x67, 0x49, 0x44, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x73, 0x67, 0x49, - 0x44, 0x12, 0x32, 0x0a, 0x14, 0x69, 0x73, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x45, - 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x14, 0x69, 0x73, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x6d, 0x73, 0x67, 0x46, 0x69, 0x72, 0x73, - 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x12, 0x6d, 0x73, 0x67, 0x46, 0x69, 0x72, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, - 0x79, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4c, 0x0a, 0x12, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x12, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x22, 0x5e, 0x0a, 0x24, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, - 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x36, 0x0a, 0x06, 0x72, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x4f, 0x70, - 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x4b, - 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x52, 0x06, 0x72, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x22, 0x4a, 0x0a, 0x0d, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, - 0x52, 0x65, 0x73, 0x70, 0x12, 0x39, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x4d, 0x73, - 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, - 0x64, 0x4d, 0x73, 0x67, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x22, - 0xdd, 0x02, 0x0a, 0x09, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x12, 0x63, 0x0a, - 0x12, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x4f, 0x70, 0x65, 0x6e, - 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x45, 0x78, 0x74, - 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, - 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, - 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x73, 0x67, 0x49, - 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, - 0x73, 0x67, 0x49, 0x44, 0x12, 0x2e, 0x0a, 0x12, 0x6d, 0x73, 0x67, 0x46, 0x69, 0x72, 0x73, 0x74, - 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x12, 0x6d, 0x73, 0x67, 0x46, 0x69, 0x72, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, - 0x54, 0x69, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, - 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x61, - 0x63, 0x68, 0x65, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x65, 0x78, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x65, 0x78, 0x1a, 0x65, 0x0a, 0x17, 0x52, 0x65, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, - 0x7a, 0x0a, 0x0c, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, - 0x38, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x08, 0x6b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x72, 0x72, - 0x43, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x65, 0x72, 0x72, 0x43, - 0x6f, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x4d, 0x73, 0x67, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x72, 0x72, 0x4d, 0x73, 0x67, 0x22, 0x76, 0x0a, 0x13, 0x4d, - 0x73, 0x67, 0x44, 0x61, 0x74, 0x61, 0x54, 0x6f, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x42, 0x79, - 0x4d, 0x51, 0x12, 0x37, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x4d, 0x73, 0x67, 0x44, 0x61, 0x74, - 0x61, 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x63, - 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x49, 0x44, 0x22, 0x0c, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x4d, 0x73, 0x67, 0x73, 0x52, 0x65, - 0x71, 0x22, 0x0d, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x4d, 0x73, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x22, 0x60, 0x0a, 0x0c, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x71, - 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, - 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x71, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x73, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, - 0x65, 0x72, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, - 0x49, 0x44, 0x22, 0x0f, 0x0a, 0x0d, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x4d, 0x73, 0x67, 0x52, - 0x65, 0x73, 0x70, 0x22, 0x67, 0x0a, 0x11, 0x4d, 0x61, 0x72, 0x6b, 0x4d, 0x73, 0x67, 0x73, 0x41, - 0x73, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, - 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x71, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x03, 0x52, 0x04, - 0x73, 0x65, 0x71, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x22, 0x14, 0x0a, 0x12, - 0x4d, 0x61, 0x72, 0x6b, 0x4d, 0x73, 0x67, 0x73, 0x41, 0x73, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, - 0x73, 0x70, 0x22, 0x8f, 0x01, 0x0a, 0x19, 0x4d, 0x61, 0x72, 0x6b, 0x43, 0x6f, 0x6e, 0x76, 0x65, - 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x73, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, - 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, - 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, - 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, - 0x12, 0x1e, 0x0a, 0x0a, 0x68, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x53, 0x65, 0x71, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x68, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x53, 0x65, 0x71, - 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x71, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x03, 0x52, 0x04, - 0x73, 0x65, 0x71, 0x73, 0x22, 0x1c, 0x0a, 0x1a, 0x4d, 0x61, 0x72, 0x6b, 0x43, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x73, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, - 0x73, 0x70, 0x22, 0x7e, 0x0a, 0x1c, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x53, 0x65, 0x71, 0x52, - 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, - 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, - 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x68, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x53, 0x65, 0x71, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x68, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x53, - 0x65, 0x71, 0x22, 0x1f, 0x0a, 0x1d, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x53, 0x65, 0x71, 0x52, - 0x65, 0x73, 0x70, 0x22, 0x51, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x79, 0x6e, - 0x63, 0x4f, 0x70, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x49, 0x73, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x65, - 0x6c, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x49, 0x73, 0x53, 0x79, 0x6e, 0x63, - 0x53, 0x65, 0x6c, 0x66, 0x12, 0x20, 0x0a, 0x0b, 0x49, 0x73, 0x53, 0x79, 0x6e, 0x63, 0x4f, 0x74, - 0x68, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x49, 0x73, 0x53, 0x79, 0x6e, - 0x63, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x22, 0xa3, 0x01, 0x0a, 0x18, 0x43, 0x6c, 0x65, 0x61, 0x72, - 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4d, 0x73, 0x67, - 0x52, 0x65, 0x71, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x12, 0x16, 0x0a, - 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, - 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x45, 0x0a, 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, - 0x79, 0x6e, 0x63, 0x4f, 0x70, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x4f, - 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x4f, 0x70, 0x74, 0x52, 0x0d, 0x64, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x4f, 0x70, 0x74, 0x22, 0x1b, 0x0a, 0x19, - 0x43, 0x6c, 0x65, 0x61, 0x72, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x73, 0x70, 0x22, 0x73, 0x0a, 0x12, 0x55, 0x73, 0x65, - 0x72, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x41, 0x6c, 0x6c, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x71, 0x12, - 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x45, 0x0a, 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x53, 0x79, 0x6e, 0x63, 0x4f, 0x70, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, - 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, - 0x67, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x4f, 0x70, 0x74, 0x52, - 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x4f, 0x70, 0x74, 0x22, 0x15, - 0x0a, 0x13, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x41, 0x6c, 0x6c, 0x4d, 0x73, - 0x67, 0x52, 0x65, 0x73, 0x70, 0x22, 0xaa, 0x01, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x4d, 0x73, 0x67, 0x73, 0x52, 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, - 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, - 0x12, 0x0a, 0x04, 0x73, 0x65, 0x71, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x03, 0x52, 0x04, 0x73, - 0x65, 0x71, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x03, 0x20, + 0x6d, 0x73, 0x67, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x4f, 0x70, + 0x74, 0x52, 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x4f, 0x70, 0x74, + 0x22, 0x1b, 0x0a, 0x19, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x73, 0x70, 0x22, 0x73, 0x0a, + 0x12, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x41, 0x6c, 0x6c, 0x4d, 0x73, 0x67, + 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x45, 0x0a, 0x0d, 0x64, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x4f, 0x70, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x4f, 0x70, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x4f, 0x70, 0x74, 0x52, 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x4f, - 0x70, 0x74, 0x22, 0x10, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x22, 0x5e, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, - 0x67, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x12, 0x28, 0x0a, 0x0f, - 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x22, 0x17, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, - 0x67, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x22, 0x57, 0x0a, - 0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, - 0x61, 0x6c, 0x42, 0x79, 0x53, 0x65, 0x71, 0x52, 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x71, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x03, - 0x52, 0x04, 0x73, 0x65, 0x71, 0x73, 0x22, 0x1c, 0x0a, 0x1a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x4d, 0x73, 0x67, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x42, 0x79, 0x53, 0x65, 0x71, - 0x52, 0x65, 0x73, 0x70, 0x22, 0x42, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, - 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, 0x71, - 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, - 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x22, 0x33, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x43, + 0x70, 0x74, 0x22, 0x15, 0x0a, 0x13, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x41, + 0x6c, 0x6c, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x73, 0x70, 0x22, 0xaa, 0x01, 0x0a, 0x0d, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x73, 0x52, 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, 0x63, + 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x71, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x03, 0x52, 0x04, 0x73, 0x65, 0x71, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, + 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, + 0x45, 0x0a, 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x4f, 0x70, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x53, 0x79, 0x6e, 0x63, 0x4f, 0x70, 0x74, 0x52, 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, + 0x79, 0x6e, 0x63, 0x4f, 0x70, 0x74, 0x22, 0x10, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x4d, 0x73, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x5e, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x4d, 0x73, 0x67, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x52, 0x65, 0x71, + 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x76, 0x65, + 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x17, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x4d, 0x73, 0x67, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x52, 0x65, 0x73, + 0x70, 0x22, 0x57, 0x0a, 0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x50, 0x68, + 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x42, 0x79, 0x53, 0x65, 0x71, 0x52, 0x65, 0x71, 0x12, 0x26, + 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x71, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x03, 0x52, 0x04, 0x73, 0x65, 0x71, 0x73, 0x22, 0x1c, 0x0a, 0x1a, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x42, + 0x79, 0x53, 0x65, 0x71, 0x52, 0x65, 0x73, 0x70, 0x22, 0x42, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, - 0x71, 0x52, 0x65, 0x73, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x22, 0x3d, 0x0a, - 0x23, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x48, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x41, 0x6e, 0x64, 0x4d, 0x61, 0x78, 0x53, 0x65, - 0x71, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x22, 0x3e, 0x0a, 0x04, - 0x53, 0x65, 0x71, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x12, 0x1e, 0x0a, 0x0a, - 0x68, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x53, 0x65, 0x71, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0a, 0x68, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x53, 0x65, 0x71, 0x22, 0xcd, 0x01, 0x0a, - 0x24, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x48, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x41, 0x6e, 0x64, 0x4d, 0x61, 0x78, 0x53, 0x65, - 0x71, 0x52, 0x65, 0x73, 0x70, 0x12, 0x54, 0x0a, 0x04, 0x73, 0x65, 0x71, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, - 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x48, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x41, 0x6e, - 0x64, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, 0x73, 0x70, 0x2e, 0x53, 0x65, 0x71, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x73, 0x65, 0x71, 0x73, 0x1a, 0x4f, 0x0a, 0x09, 0x53, - 0x65, 0x71, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x4f, 0x70, 0x65, 0x6e, - 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x53, 0x65, 0x71, - 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0xa5, 0x11, 0x0a, - 0x03, 0x6d, 0x73, 0x67, 0x12, 0x50, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x78, 0x53, 0x65, - 0x71, 0x12, 0x20, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, - 0x52, 0x65, 0x71, 0x1a, 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x78, 0x53, - 0x65, 0x71, 0x52, 0x65, 0x73, 0x70, 0x12, 0x70, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, - 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x12, - 0x2a, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, - 0x73, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, 0x71, 0x1a, 0x2b, 0x2e, 0x4f, 0x70, - 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x47, - 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, - 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, 0x73, 0x70, 0x12, 0x68, 0x0a, 0x11, 0x50, 0x75, 0x6c, 0x6c, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x79, 0x53, 0x65, 0x71, 0x73, 0x12, 0x28, 0x2e, + 0x71, 0x52, 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x22, 0x33, 0x0a, 0x19, + 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, + 0x61, 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, 0x73, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x78, + 0x53, 0x65, 0x71, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x53, 0x65, + 0x71, 0x22, 0x3d, 0x0a, 0x23, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x48, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x41, 0x6e, 0x64, 0x4d, + 0x61, 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, + 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, + 0x22, 0x3e, 0x0a, 0x04, 0x53, 0x65, 0x71, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x78, 0x53, + 0x65, 0x71, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x53, 0x65, 0x71, + 0x12, 0x1e, 0x0a, 0x0a, 0x68, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x53, 0x65, 0x71, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x68, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x53, 0x65, 0x71, + 0x22, 0xcd, 0x01, 0x0a, 0x24, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x48, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x41, 0x6e, 0x64, 0x4d, + 0x61, 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, 0x73, 0x70, 0x12, 0x54, 0x0a, 0x04, 0x73, 0x65, 0x71, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x48, 0x61, 0x73, 0x52, 0x65, + 0x61, 0x64, 0x41, 0x6e, 0x64, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, 0x73, 0x70, 0x2e, + 0x53, 0x65, 0x71, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x73, 0x65, 0x71, 0x73, 0x1a, + 0x4f, 0x0a, 0x09, 0x53, 0x65, 0x71, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, + 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, + 0x2e, 0x53, 0x65, 0x71, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0xa9, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x55, 0x73, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, + 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x73, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x45, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x4f, 0x70, 0x65, 0x6e, + 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x54, 0x0a, 0x0a, + 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x30, 0x0a, 0x04, 0x75, 0x73, + 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, + 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x55, 0x73, + 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x22, 0x91, 0x02, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, + 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x73, 0x67, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x73, 0x67, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x50, 0x0a, 0x09, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, + 0x76, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x2e, 0x44, 0x61, 0x74, 0x65, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x64, 0x61, 0x74, 0x65, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x55, 0x73, 0x65, + 0x72, 0x52, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x1a, 0x3c, 0x0a, 0x0e, 0x44, 0x61, 0x74, 0x65, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x94, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x41, 0x63, + 0x74, 0x69, 0x76, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x03, 0x65, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x03, 0x61, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x4f, 0x70, 0x65, + 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x58, 0x0a, + 0x0b, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x33, 0x0a, 0x05, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x4f, 0x70, + 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, + 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x98, 0x02, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x41, + 0x63, 0x74, 0x69, 0x76, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x12, 0x1a, + 0x0a, 0x08, 0x6d, 0x73, 0x67, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x08, 0x6d, 0x73, 0x67, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x51, 0x0a, 0x09, 0x64, 0x61, + 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, + 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, + 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, + 0x65, 0x73, 0x70, 0x2e, 0x44, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x09, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x35, 0x0a, + 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, + 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, + 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x06, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x73, 0x1a, 0x3c, 0x0a, 0x0e, 0x44, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x32, 0xa8, 0x0e, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x50, 0x0a, 0x09, 0x47, 0x65, + 0x74, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x12, 0x20, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x47, 0x65, 0x74, + 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, 0x71, 0x1a, 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x6e, + 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x47, + 0x65, 0x74, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, 0x73, 0x70, 0x12, 0x70, 0x0a, 0x15, + 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, + 0x61, 0x78, 0x53, 0x65, 0x71, 0x12, 0x2a, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, + 0x71, 0x1a, 0x2b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, 0x73, 0x70, 0x12, 0x68, + 0x0a, 0x11, 0x50, 0x75, 0x6c, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x79, 0x53, + 0x65, 0x71, 0x73, 0x12, 0x28, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x42, 0x79, 0x53, 0x65, 0x71, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x29, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x79, - 0x53, 0x65, 0x71, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x29, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x50, 0x75, 0x6c, - 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x79, 0x53, 0x65, 0x71, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x12, 0x46, 0x0a, 0x07, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x12, 0x1c, 0x2e, - 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, - 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x71, 0x1a, 0x1d, 0x2e, 0x4f, 0x70, - 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x53, - 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x73, 0x70, 0x12, 0x70, 0x0a, 0x15, 0x43, 0x6c, - 0x65, 0x61, 0x72, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x4d, 0x73, 0x67, 0x12, 0x2a, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x43, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x71, 0x1a, - 0x2b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, - 0x73, 0x67, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x73, 0x70, 0x12, 0x5e, 0x0a, 0x0f, - 0x55, 0x73, 0x65, 0x72, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x41, 0x6c, 0x6c, 0x4d, 0x73, 0x67, 0x12, - 0x24, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, - 0x73, 0x67, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x41, 0x6c, 0x6c, 0x4d, - 0x73, 0x67, 0x52, 0x65, 0x71, 0x1a, 0x25, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, + 0x53, 0x65, 0x71, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x46, 0x0a, 0x07, 0x53, 0x65, 0x6e, 0x64, + 0x4d, 0x73, 0x67, 0x12, 0x1c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x52, 0x65, + 0x71, 0x1a, 0x1d, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x73, 0x70, + 0x12, 0x70, 0x0a, 0x15, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4d, 0x73, 0x67, 0x12, 0x2a, 0x2e, 0x4f, 0x70, 0x65, 0x6e, + 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x43, 0x6c, 0x65, + 0x61, 0x72, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4d, + 0x73, 0x67, 0x52, 0x65, 0x71, 0x1a, 0x2b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x43, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4d, 0x73, 0x67, 0x52, 0x65, + 0x73, 0x70, 0x12, 0x5e, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x41, + 0x6c, 0x6c, 0x4d, 0x73, 0x67, 0x12, 0x24, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6c, 0x65, - 0x61, 0x72, 0x41, 0x6c, 0x6c, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x73, 0x70, 0x12, 0x4f, 0x0a, 0x0a, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x73, 0x12, 0x1f, 0x2e, 0x4f, 0x70, 0x65, + 0x61, 0x72, 0x41, 0x6c, 0x6c, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x71, 0x1a, 0x25, 0x2e, 0x4f, 0x70, + 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x55, + 0x73, 0x65, 0x72, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x41, 0x6c, 0x6c, 0x4d, 0x73, 0x67, 0x52, 0x65, + 0x73, 0x70, 0x12, 0x4f, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x73, + 0x12, 0x1f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x6d, 0x73, 0x67, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x73, 0x52, 0x65, + 0x71, 0x1a, 0x20, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x12, 0x73, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, 0x67, + 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x42, 0x79, 0x53, 0x65, 0x71, 0x12, 0x2b, 0x2e, + 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, + 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, + 0x61, 0x6c, 0x42, 0x79, 0x53, 0x65, 0x71, 0x52, 0x65, 0x71, 0x1a, 0x2c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x20, 0x2e, 0x4f, 0x70, - 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x73, 0x0a, - 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, - 0x61, 0x6c, 0x42, 0x79, 0x53, 0x65, 0x71, 0x12, 0x2b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x4d, 0x73, 0x67, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x42, 0x79, 0x53, 0x65, - 0x71, 0x52, 0x65, 0x71, 0x1a, 0x2c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, - 0x67, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x42, 0x79, 0x53, 0x65, 0x71, 0x52, 0x65, - 0x73, 0x70, 0x12, 0x64, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x50, - 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x12, 0x26, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x4d, 0x73, 0x67, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x1a, - 0x27, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, - 0x73, 0x67, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x50, 0x68, 0x79, 0x73, - 0x69, 0x63, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x12, 0x61, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x53, - 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x25, 0x2e, 0x4f, - 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, - 0x53, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x71, 0x1a, 0x26, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, - 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x61, 0x0a, 0x10, 0x47, - 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, - 0x25, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, - 0x73, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x26, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x6e, - 0x64, 0x4d, 0x73, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x4c, - 0x0a, 0x09, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x4d, 0x73, 0x67, 0x12, 0x1e, 0x2e, 0x4f, 0x70, - 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x52, - 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x71, 0x1a, 0x1f, 0x2e, 0x4f, 0x70, - 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x52, - 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x73, 0x70, 0x12, 0x5b, 0x0a, 0x0e, - 0x4d, 0x61, 0x72, 0x6b, 0x4d, 0x73, 0x67, 0x73, 0x41, 0x73, 0x52, 0x65, 0x61, 0x64, 0x12, 0x23, - 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, - 0x67, 0x2e, 0x4d, 0x61, 0x72, 0x6b, 0x4d, 0x73, 0x67, 0x73, 0x41, 0x73, 0x52, 0x65, 0x61, 0x64, - 0x52, 0x65, 0x71, 0x1a, 0x24, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x4d, 0x61, 0x72, 0x6b, 0x4d, 0x73, 0x67, 0x73, 0x41, - 0x73, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12, 0x73, 0x0a, 0x16, 0x4d, 0x61, 0x72, - 0x6b, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x73, 0x52, - 0x65, 0x61, 0x64, 0x12, 0x2b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x4d, 0x61, 0x72, 0x6b, 0x43, 0x6f, 0x6e, 0x76, 0x65, - 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x73, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, - 0x1a, 0x2c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, - 0x6d, 0x73, 0x67, 0x2e, 0x4d, 0x61, 0x72, 0x6b, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x73, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12, 0x7c, - 0x0a, 0x19, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x48, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x53, 0x65, 0x71, 0x12, 0x2e, 0x2e, 0x4f, 0x70, - 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x53, - 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x61, - 0x73, 0x52, 0x65, 0x61, 0x64, 0x53, 0x65, 0x71, 0x52, 0x65, 0x71, 0x1a, 0x2f, 0x2e, 0x4f, 0x70, - 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x53, - 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x61, - 0x73, 0x52, 0x65, 0x61, 0x64, 0x53, 0x65, 0x71, 0x52, 0x65, 0x73, 0x70, 0x12, 0x85, 0x01, 0x0a, - 0x1c, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x31, 0x2e, + 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x42, + 0x79, 0x53, 0x65, 0x71, 0x52, 0x65, 0x73, 0x70, 0x12, 0x64, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x4d, 0x73, 0x67, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x12, 0x26, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, - 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, - 0x1a, 0x32, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, - 0x6d, 0x73, 0x67, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x12, 0x88, 0x01, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, - 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x32, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, - 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x33, 0x2e, 0x4f, 0x70, 0x65, - 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x47, 0x65, - 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, - 0x8b, 0x01, 0x0a, 0x1c, 0x41, 0x64, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0x34, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, - 0x6d, 0x73, 0x67, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x35, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, - 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x90, 0x01, - 0x0a, 0x1f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, - 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, - 0x73, 0x12, 0x35, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x36, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, - 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x12, 0x91, 0x01, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x48, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x41, 0x6e, 0x64, 0x4d, - 0x61, 0x78, 0x53, 0x65, 0x71, 0x12, 0x35, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x48, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, - 0x41, 0x6e, 0x64, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, 0x71, 0x1a, 0x36, 0x2e, 0x4f, + 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, + 0x61, 0x6c, 0x52, 0x65, 0x71, 0x1a, 0x27, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, + 0x73, 0x67, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x12, 0x61, + 0x0a, 0x10, 0x53, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x25, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x26, 0x2e, 0x4f, 0x70, 0x65, 0x6e, + 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x53, 0x65, 0x74, + 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x12, 0x61, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x25, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, + 0x4d, 0x73, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x26, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, - 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x48, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x41, 0x6e, 0x64, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, - 0x52, 0x65, 0x73, 0x70, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x44, 0x4b, 0x2f, 0x4f, 0x70, 0x65, - 0x6e, 0x2d, 0x49, 0x4d, 0x2d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6d, 0x73, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x47, 0x65, 0x74, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x12, 0x4c, 0x0a, 0x09, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x4d, 0x73, + 0x67, 0x12, 0x1e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x4d, 0x73, 0x67, 0x52, 0x65, + 0x71, 0x1a, 0x1f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x4d, 0x73, 0x67, 0x52, 0x65, + 0x73, 0x70, 0x12, 0x5b, 0x0a, 0x0e, 0x4d, 0x61, 0x72, 0x6b, 0x4d, 0x73, 0x67, 0x73, 0x41, 0x73, + 0x52, 0x65, 0x61, 0x64, 0x12, 0x23, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x4d, 0x61, 0x72, 0x6b, 0x4d, 0x73, 0x67, 0x73, + 0x41, 0x73, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x24, 0x2e, 0x4f, 0x70, 0x65, 0x6e, + 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x4d, 0x61, 0x72, + 0x6b, 0x4d, 0x73, 0x67, 0x73, 0x41, 0x73, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12, + 0x73, 0x0a, 0x16, 0x4d, 0x61, 0x72, 0x6b, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x41, 0x73, 0x52, 0x65, 0x61, 0x64, 0x12, 0x2b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, + 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x4d, 0x61, 0x72, + 0x6b, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x73, 0x52, + 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x2c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x4d, 0x61, 0x72, 0x6b, 0x43, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x73, 0x52, 0x65, 0x61, 0x64, + 0x52, 0x65, 0x73, 0x70, 0x12, 0x7c, 0x0a, 0x19, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, + 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x53, 0x65, + 0x71, 0x12, 0x2e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x53, 0x65, 0x71, 0x52, 0x65, + 0x71, 0x1a, 0x2f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x53, 0x65, 0x71, 0x52, 0x65, + 0x73, 0x70, 0x12, 0x91, 0x01, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x48, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x41, 0x6e, + 0x64, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x12, 0x35, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x48, 0x61, 0x73, 0x52, 0x65, + 0x61, 0x64, 0x41, 0x6e, 0x64, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x71, 0x52, 0x65, 0x71, 0x1a, 0x36, + 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, + 0x67, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x48, 0x61, 0x73, 0x52, 0x65, 0x61, 0x64, 0x41, 0x6e, 0x64, 0x4d, 0x61, 0x78, 0x53, + 0x65, 0x71, 0x52, 0x65, 0x73, 0x70, 0x12, 0x58, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, + 0x69, 0x76, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x22, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, + 0x74, 0x69, 0x76, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x23, 0x2e, 0x4f, 0x70, + 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x47, + 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x12, 0x5b, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x12, 0x23, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x24, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, + 0x74, 0x69, 0x76, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x42, 0x33, 0x5a, + 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x70, 0x65, 0x6e, + 0x49, 0x4d, 0x53, 0x44, 0x4b, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x2d, 0x49, 0x4d, 0x2d, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6d, + 0x73, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -3436,7 +2745,7 @@ func file_msg_msg_proto_rawDescGZIP() []byte { return file_msg_msg_proto_rawDescData } -var file_msg_msg_proto_msgTypes = make([]protoimpl.MessageInfo, 57) +var file_msg_msg_proto_msgTypes = make([]protoimpl.MessageInfo, 48) var file_msg_msg_proto_goTypes = []interface{}{ (*MsgDataToMQ)(nil), // 0: OpenIMServer.msg.MsgDataToMQ (*MsgDataToDB)(nil), // 1: OpenIMServer.msg.MsgDataToDB @@ -3450,137 +2759,112 @@ var file_msg_msg_proto_goTypes = []interface{}{ (*SetSendMsgStatusResp)(nil), // 9: OpenIMServer.msg.SetSendMsgStatusResp (*GetSendMsgStatusReq)(nil), // 10: OpenIMServer.msg.GetSendMsgStatusReq (*GetSendMsgStatusResp)(nil), // 11: OpenIMServer.msg.GetSendMsgStatusResp - (*ModifyMessageReactionExtensionsReq)(nil), // 12: OpenIMServer.msg.ModifyMessageReactionExtensionsReq - (*SetMessageReactionExtensionsReq)(nil), // 13: OpenIMServer.msg.SetMessageReactionExtensionsReq - (*SetMessageReactionExtensionsResp)(nil), // 14: OpenIMServer.msg.SetMessageReactionExtensionsResp - (*GetMessagesReactionExtensionsReq)(nil), // 15: OpenIMServer.msg.GetMessagesReactionExtensionsReq - (*GetMessagesReactionExtensionsResp)(nil), // 16: OpenIMServer.msg.GetMessagesReactionExtensionsResp - (*SingleMessageExtensionResult)(nil), // 17: OpenIMServer.msg.SingleMessageExtensionResult - (*ModifyMessageReactionExtensionsResp)(nil), // 18: OpenIMServer.msg.ModifyMessageReactionExtensionsResp - (*DeleteMessagesReactionExtensionsReq)(nil), // 19: OpenIMServer.msg.DeleteMessagesReactionExtensionsReq - (*DeleteMessagesReactionExtensionsResp)(nil), // 20: OpenIMServer.msg.DeleteMessagesReactionExtensionsResp - (*ExtendMsgResp)(nil), // 21: OpenIMServer.msg.ExtendMsgResp - (*ExtendMsg)(nil), // 22: OpenIMServer.msg.ExtendMsg - (*KeyValueResp)(nil), // 23: OpenIMServer.msg.KeyValueResp - (*MsgDataToModifyByMQ)(nil), // 24: OpenIMServer.msg.MsgDataToModifyByMQ - (*DelMsgsReq)(nil), // 25: OpenIMServer.msg.DelMsgsReq - (*DelMsgsResp)(nil), // 26: OpenIMServer.msg.DelMsgsResp - (*RevokeMsgReq)(nil), // 27: OpenIMServer.msg.RevokeMsgReq - (*RevokeMsgResp)(nil), // 28: OpenIMServer.msg.RevokeMsgResp - (*MarkMsgsAsReadReq)(nil), // 29: OpenIMServer.msg.MarkMsgsAsReadReq - (*MarkMsgsAsReadResp)(nil), // 30: OpenIMServer.msg.MarkMsgsAsReadResp - (*MarkConversationAsReadReq)(nil), // 31: OpenIMServer.msg.MarkConversationAsReadReq - (*MarkConversationAsReadResp)(nil), // 32: OpenIMServer.msg.MarkConversationAsReadResp - (*SetConversationHasReadSeqReq)(nil), // 33: OpenIMServer.msg.SetConversationHasReadSeqReq - (*SetConversationHasReadSeqResp)(nil), // 34: OpenIMServer.msg.SetConversationHasReadSeqResp - (*DeleteSyncOpt)(nil), // 35: OpenIMServer.msg.DeleteSyncOpt - (*ClearConversationsMsgReq)(nil), // 36: OpenIMServer.msg.ClearConversationsMsgReq - (*ClearConversationsMsgResp)(nil), // 37: OpenIMServer.msg.ClearConversationsMsgResp - (*UserClearAllMsgReq)(nil), // 38: OpenIMServer.msg.UserClearAllMsgReq - (*UserClearAllMsgResp)(nil), // 39: OpenIMServer.msg.UserClearAllMsgResp - (*DeleteMsgsReq)(nil), // 40: OpenIMServer.msg.DeleteMsgsReq - (*DeleteMsgsResp)(nil), // 41: OpenIMServer.msg.DeleteMsgsResp - (*DeleteMsgPhysicalReq)(nil), // 42: OpenIMServer.msg.DeleteMsgPhysicalReq - (*DeleteMsgPhysicalResp)(nil), // 43: OpenIMServer.msg.DeleteMsgPhysicalResp - (*DeleteMsgPhysicalBySeqReq)(nil), // 44: OpenIMServer.msg.DeleteMsgPhysicalBySeqReq - (*DeleteMsgPhysicalBySeqResp)(nil), // 45: OpenIMServer.msg.DeleteMsgPhysicalBySeqResp - (*GetConversationMaxSeqReq)(nil), // 46: OpenIMServer.msg.GetConversationMaxSeqReq - (*GetConversationMaxSeqResp)(nil), // 47: OpenIMServer.msg.GetConversationMaxSeqResp - (*GetConversationsHasReadAndMaxSeqReq)(nil), // 48: OpenIMServer.msg.GetConversationsHasReadAndMaxSeqReq - (*Seqs)(nil), // 49: OpenIMServer.msg.Seqs - (*GetConversationsHasReadAndMaxSeqResp)(nil), // 50: OpenIMServer.msg.GetConversationsHasReadAndMaxSeqResp - nil, // 51: OpenIMServer.msg.ModifyMessageReactionExtensionsReq.ReactionExtensionsEntry - nil, // 52: OpenIMServer.msg.SetMessageReactionExtensionsReq.ReactionExtensionsEntry - (*GetMessagesReactionExtensionsReq_MessageReactionKey)(nil), // 53: OpenIMServer.msg.GetMessagesReactionExtensionsReq.MessageReactionKey - nil, // 54: OpenIMServer.msg.SingleMessageExtensionResult.ReactionExtensionsEntry - nil, // 55: OpenIMServer.msg.ExtendMsg.ReactionExtensionsEntry - nil, // 56: OpenIMServer.msg.GetConversationsHasReadAndMaxSeqResp.SeqsEntry - (*sdkws.MsgData)(nil), // 57: OpenIMServer.sdkws.MsgData - (*wrapperspb.StringValue)(nil), // 58: OpenIMServer.protobuf.StringValue - (*sdkws.KeyValue)(nil), // 59: OpenIMServer.sdkws.KeyValue - (*sdkws.GetMaxSeqReq)(nil), // 60: OpenIMServer.sdkws.GetMaxSeqReq - (*sdkws.PullMessageBySeqsReq)(nil), // 61: OpenIMServer.sdkws.PullMessageBySeqsReq - (*sdkws.GetMaxSeqResp)(nil), // 62: OpenIMServer.sdkws.GetMaxSeqResp - (*sdkws.PullMessageBySeqsResp)(nil), // 63: OpenIMServer.sdkws.PullMessageBySeqsResp + (*MsgDataToModifyByMQ)(nil), // 12: OpenIMServer.msg.MsgDataToModifyByMQ + (*DelMsgsReq)(nil), // 13: OpenIMServer.msg.DelMsgsReq + (*DelMsgsResp)(nil), // 14: OpenIMServer.msg.DelMsgsResp + (*RevokeMsgReq)(nil), // 15: OpenIMServer.msg.RevokeMsgReq + (*RevokeMsgResp)(nil), // 16: OpenIMServer.msg.RevokeMsgResp + (*MarkMsgsAsReadReq)(nil), // 17: OpenIMServer.msg.MarkMsgsAsReadReq + (*MarkMsgsAsReadResp)(nil), // 18: OpenIMServer.msg.MarkMsgsAsReadResp + (*MarkConversationAsReadReq)(nil), // 19: OpenIMServer.msg.MarkConversationAsReadReq + (*MarkConversationAsReadResp)(nil), // 20: OpenIMServer.msg.MarkConversationAsReadResp + (*SetConversationHasReadSeqReq)(nil), // 21: OpenIMServer.msg.SetConversationHasReadSeqReq + (*SetConversationHasReadSeqResp)(nil), // 22: OpenIMServer.msg.SetConversationHasReadSeqResp + (*DeleteSyncOpt)(nil), // 23: OpenIMServer.msg.DeleteSyncOpt + (*ClearConversationsMsgReq)(nil), // 24: OpenIMServer.msg.ClearConversationsMsgReq + (*ClearConversationsMsgResp)(nil), // 25: OpenIMServer.msg.ClearConversationsMsgResp + (*UserClearAllMsgReq)(nil), // 26: OpenIMServer.msg.UserClearAllMsgReq + (*UserClearAllMsgResp)(nil), // 27: OpenIMServer.msg.UserClearAllMsgResp + (*DeleteMsgsReq)(nil), // 28: OpenIMServer.msg.DeleteMsgsReq + (*DeleteMsgsResp)(nil), // 29: OpenIMServer.msg.DeleteMsgsResp + (*DeleteMsgPhysicalReq)(nil), // 30: OpenIMServer.msg.DeleteMsgPhysicalReq + (*DeleteMsgPhysicalResp)(nil), // 31: OpenIMServer.msg.DeleteMsgPhysicalResp + (*DeleteMsgPhysicalBySeqReq)(nil), // 32: OpenIMServer.msg.DeleteMsgPhysicalBySeqReq + (*DeleteMsgPhysicalBySeqResp)(nil), // 33: OpenIMServer.msg.DeleteMsgPhysicalBySeqResp + (*GetConversationMaxSeqReq)(nil), // 34: OpenIMServer.msg.GetConversationMaxSeqReq + (*GetConversationMaxSeqResp)(nil), // 35: OpenIMServer.msg.GetConversationMaxSeqResp + (*GetConversationsHasReadAndMaxSeqReq)(nil), // 36: OpenIMServer.msg.GetConversationsHasReadAndMaxSeqReq + (*Seqs)(nil), // 37: OpenIMServer.msg.Seqs + (*GetConversationsHasReadAndMaxSeqResp)(nil), // 38: OpenIMServer.msg.GetConversationsHasReadAndMaxSeqResp + (*GetActiveUserReq)(nil), // 39: OpenIMServer.msg.GetActiveUserReq + (*ActiveUser)(nil), // 40: OpenIMServer.msg.ActiveUser + (*GetActiveUserResp)(nil), // 41: OpenIMServer.msg.GetActiveUserResp + (*GetActiveGroupReq)(nil), // 42: OpenIMServer.msg.GetActiveGroupReq + (*ActiveGroup)(nil), // 43: OpenIMServer.msg.ActiveGroup + (*GetActiveGroupResp)(nil), // 44: OpenIMServer.msg.GetActiveGroupResp + nil, // 45: OpenIMServer.msg.GetConversationsHasReadAndMaxSeqResp.SeqsEntry + nil, // 46: OpenIMServer.msg.GetActiveUserResp.DateCountEntry + nil, // 47: OpenIMServer.msg.GetActiveGroupResp.DateCountEntry + (*sdkws.MsgData)(nil), // 48: OpenIMServer.sdkws.MsgData + (*sdkws.RequestPagination)(nil), // 49: OpenIMServer.sdkws.RequestPagination + (*sdkws.UserInfo)(nil), // 50: OpenIMServer.sdkws.UserInfo + (*sdkws.GroupInfo)(nil), // 51: OpenIMServer.sdkws.GroupInfo + (*sdkws.GetMaxSeqReq)(nil), // 52: OpenIMServer.sdkws.GetMaxSeqReq + (*sdkws.PullMessageBySeqsReq)(nil), // 53: OpenIMServer.sdkws.PullMessageBySeqsReq + (*sdkws.GetMaxSeqResp)(nil), // 54: OpenIMServer.sdkws.GetMaxSeqResp + (*sdkws.PullMessageBySeqsResp)(nil), // 55: OpenIMServer.sdkws.PullMessageBySeqsResp } var file_msg_msg_proto_depIdxs = []int32{ - 57, // 0: OpenIMServer.msg.MsgDataToMQ.msgData:type_name -> OpenIMServer.sdkws.MsgData - 57, // 1: OpenIMServer.msg.MsgDataToDB.msgData:type_name -> OpenIMServer.sdkws.MsgData - 57, // 2: OpenIMServer.msg.PushMsgDataToMQ.msgData:type_name -> OpenIMServer.sdkws.MsgData - 57, // 3: OpenIMServer.msg.MsgDataToMongoByMQ.msgData:type_name -> OpenIMServer.sdkws.MsgData - 57, // 4: OpenIMServer.msg.SendMsgReq.msgData:type_name -> OpenIMServer.sdkws.MsgData - 51, // 5: OpenIMServer.msg.ModifyMessageReactionExtensionsReq.reactionExtensions:type_name -> OpenIMServer.msg.ModifyMessageReactionExtensionsReq.ReactionExtensionsEntry - 58, // 6: OpenIMServer.msg.ModifyMessageReactionExtensionsReq.ex:type_name -> OpenIMServer.protobuf.StringValue - 58, // 7: OpenIMServer.msg.ModifyMessageReactionExtensionsReq.attachedInfo:type_name -> OpenIMServer.protobuf.StringValue - 52, // 8: OpenIMServer.msg.SetMessageReactionExtensionsReq.reactionExtensions:type_name -> OpenIMServer.msg.SetMessageReactionExtensionsReq.ReactionExtensionsEntry - 58, // 9: OpenIMServer.msg.SetMessageReactionExtensionsReq.ex:type_name -> OpenIMServer.protobuf.StringValue - 58, // 10: OpenIMServer.msg.SetMessageReactionExtensionsReq.attachedInfo:type_name -> OpenIMServer.protobuf.StringValue - 23, // 11: OpenIMServer.msg.SetMessageReactionExtensionsResp.result:type_name -> OpenIMServer.msg.KeyValueResp - 53, // 12: OpenIMServer.msg.GetMessagesReactionExtensionsReq.messageReactionKeys:type_name -> OpenIMServer.msg.GetMessagesReactionExtensionsReq.MessageReactionKey - 17, // 13: OpenIMServer.msg.GetMessagesReactionExtensionsResp.singleMessageResult:type_name -> OpenIMServer.msg.SingleMessageExtensionResult - 54, // 14: OpenIMServer.msg.SingleMessageExtensionResult.reactionExtensions:type_name -> OpenIMServer.msg.SingleMessageExtensionResult.ReactionExtensionsEntry - 21, // 15: OpenIMServer.msg.ModifyMessageReactionExtensionsResp.successList:type_name -> OpenIMServer.msg.ExtendMsgResp - 21, // 16: OpenIMServer.msg.ModifyMessageReactionExtensionsResp.failedList:type_name -> OpenIMServer.msg.ExtendMsgResp - 59, // 17: OpenIMServer.msg.DeleteMessagesReactionExtensionsReq.reactionExtensions:type_name -> OpenIMServer.sdkws.KeyValue - 23, // 18: OpenIMServer.msg.DeleteMessagesReactionExtensionsResp.result:type_name -> OpenIMServer.msg.KeyValueResp - 22, // 19: OpenIMServer.msg.ExtendMsgResp.extendMsg:type_name -> OpenIMServer.msg.ExtendMsg - 55, // 20: OpenIMServer.msg.ExtendMsg.reactionExtensions:type_name -> OpenIMServer.msg.ExtendMsg.ReactionExtensionsEntry - 59, // 21: OpenIMServer.msg.KeyValueResp.keyValue:type_name -> OpenIMServer.sdkws.KeyValue - 57, // 22: OpenIMServer.msg.MsgDataToModifyByMQ.messages:type_name -> OpenIMServer.sdkws.MsgData - 35, // 23: OpenIMServer.msg.ClearConversationsMsgReq.deleteSyncOpt:type_name -> OpenIMServer.msg.DeleteSyncOpt - 35, // 24: OpenIMServer.msg.UserClearAllMsgReq.deleteSyncOpt:type_name -> OpenIMServer.msg.DeleteSyncOpt - 35, // 25: OpenIMServer.msg.DeleteMsgsReq.deleteSyncOpt:type_name -> OpenIMServer.msg.DeleteSyncOpt - 56, // 26: OpenIMServer.msg.GetConversationsHasReadAndMaxSeqResp.seqs:type_name -> OpenIMServer.msg.GetConversationsHasReadAndMaxSeqResp.SeqsEntry - 59, // 27: OpenIMServer.msg.ModifyMessageReactionExtensionsReq.ReactionExtensionsEntry.value:type_name -> OpenIMServer.sdkws.KeyValue - 59, // 28: OpenIMServer.msg.SetMessageReactionExtensionsReq.ReactionExtensionsEntry.value:type_name -> OpenIMServer.sdkws.KeyValue - 59, // 29: OpenIMServer.msg.SingleMessageExtensionResult.ReactionExtensionsEntry.value:type_name -> OpenIMServer.sdkws.KeyValue - 23, // 30: OpenIMServer.msg.ExtendMsg.ReactionExtensionsEntry.value:type_name -> OpenIMServer.msg.KeyValueResp - 49, // 31: OpenIMServer.msg.GetConversationsHasReadAndMaxSeqResp.SeqsEntry.value:type_name -> OpenIMServer.msg.Seqs - 60, // 32: OpenIMServer.msg.msg.GetMaxSeq:input_type -> OpenIMServer.sdkws.GetMaxSeqReq - 46, // 33: OpenIMServer.msg.msg.GetConversationMaxSeq:input_type -> OpenIMServer.msg.GetConversationMaxSeqReq - 61, // 34: OpenIMServer.msg.msg.PullMessageBySeqs:input_type -> OpenIMServer.sdkws.PullMessageBySeqsReq - 6, // 35: OpenIMServer.msg.msg.SendMsg:input_type -> OpenIMServer.msg.SendMsgReq - 36, // 36: OpenIMServer.msg.msg.ClearConversationsMsg:input_type -> OpenIMServer.msg.ClearConversationsMsgReq - 38, // 37: OpenIMServer.msg.msg.UserClearAllMsg:input_type -> OpenIMServer.msg.UserClearAllMsgReq - 40, // 38: OpenIMServer.msg.msg.DeleteMsgs:input_type -> OpenIMServer.msg.DeleteMsgsReq - 44, // 39: OpenIMServer.msg.msg.DeleteMsgPhysicalBySeq:input_type -> OpenIMServer.msg.DeleteMsgPhysicalBySeqReq - 42, // 40: OpenIMServer.msg.msg.DeleteMsgPhysical:input_type -> OpenIMServer.msg.DeleteMsgPhysicalReq - 8, // 41: OpenIMServer.msg.msg.SetSendMsgStatus:input_type -> OpenIMServer.msg.SetSendMsgStatusReq - 10, // 42: OpenIMServer.msg.msg.GetSendMsgStatus:input_type -> OpenIMServer.msg.GetSendMsgStatusReq - 27, // 43: OpenIMServer.msg.msg.RevokeMsg:input_type -> OpenIMServer.msg.RevokeMsgReq - 29, // 44: OpenIMServer.msg.msg.MarkMsgsAsRead:input_type -> OpenIMServer.msg.MarkMsgsAsReadReq - 31, // 45: OpenIMServer.msg.msg.MarkConversationAsRead:input_type -> OpenIMServer.msg.MarkConversationAsReadReq - 33, // 46: OpenIMServer.msg.msg.SetConversationHasReadSeq:input_type -> OpenIMServer.msg.SetConversationHasReadSeqReq - 13, // 47: OpenIMServer.msg.msg.SetMessageReactionExtensions:input_type -> OpenIMServer.msg.SetMessageReactionExtensionsReq - 15, // 48: OpenIMServer.msg.msg.GetMessagesReactionExtensions:input_type -> OpenIMServer.msg.GetMessagesReactionExtensionsReq - 12, // 49: OpenIMServer.msg.msg.AddMessageReactionExtensions:input_type -> OpenIMServer.msg.ModifyMessageReactionExtensionsReq - 19, // 50: OpenIMServer.msg.msg.DeleteMessageReactionExtensions:input_type -> OpenIMServer.msg.DeleteMessagesReactionExtensionsReq - 48, // 51: OpenIMServer.msg.msg.GetConversationsHasReadAndMaxSeq:input_type -> OpenIMServer.msg.GetConversationsHasReadAndMaxSeqReq - 62, // 52: OpenIMServer.msg.msg.GetMaxSeq:output_type -> OpenIMServer.sdkws.GetMaxSeqResp - 47, // 53: OpenIMServer.msg.msg.GetConversationMaxSeq:output_type -> OpenIMServer.msg.GetConversationMaxSeqResp - 63, // 54: OpenIMServer.msg.msg.PullMessageBySeqs:output_type -> OpenIMServer.sdkws.PullMessageBySeqsResp - 7, // 55: OpenIMServer.msg.msg.SendMsg:output_type -> OpenIMServer.msg.SendMsgResp - 37, // 56: OpenIMServer.msg.msg.ClearConversationsMsg:output_type -> OpenIMServer.msg.ClearConversationsMsgResp - 39, // 57: OpenIMServer.msg.msg.UserClearAllMsg:output_type -> OpenIMServer.msg.UserClearAllMsgResp - 41, // 58: OpenIMServer.msg.msg.DeleteMsgs:output_type -> OpenIMServer.msg.DeleteMsgsResp - 45, // 59: OpenIMServer.msg.msg.DeleteMsgPhysicalBySeq:output_type -> OpenIMServer.msg.DeleteMsgPhysicalBySeqResp - 43, // 60: OpenIMServer.msg.msg.DeleteMsgPhysical:output_type -> OpenIMServer.msg.DeleteMsgPhysicalResp - 9, // 61: OpenIMServer.msg.msg.SetSendMsgStatus:output_type -> OpenIMServer.msg.SetSendMsgStatusResp - 11, // 62: OpenIMServer.msg.msg.GetSendMsgStatus:output_type -> OpenIMServer.msg.GetSendMsgStatusResp - 28, // 63: OpenIMServer.msg.msg.RevokeMsg:output_type -> OpenIMServer.msg.RevokeMsgResp - 30, // 64: OpenIMServer.msg.msg.MarkMsgsAsRead:output_type -> OpenIMServer.msg.MarkMsgsAsReadResp - 32, // 65: OpenIMServer.msg.msg.MarkConversationAsRead:output_type -> OpenIMServer.msg.MarkConversationAsReadResp - 34, // 66: OpenIMServer.msg.msg.SetConversationHasReadSeq:output_type -> OpenIMServer.msg.SetConversationHasReadSeqResp - 14, // 67: OpenIMServer.msg.msg.SetMessageReactionExtensions:output_type -> OpenIMServer.msg.SetMessageReactionExtensionsResp - 16, // 68: OpenIMServer.msg.msg.GetMessagesReactionExtensions:output_type -> OpenIMServer.msg.GetMessagesReactionExtensionsResp - 18, // 69: OpenIMServer.msg.msg.AddMessageReactionExtensions:output_type -> OpenIMServer.msg.ModifyMessageReactionExtensionsResp - 20, // 70: OpenIMServer.msg.msg.DeleteMessageReactionExtensions:output_type -> OpenIMServer.msg.DeleteMessagesReactionExtensionsResp - 50, // 71: OpenIMServer.msg.msg.GetConversationsHasReadAndMaxSeq:output_type -> OpenIMServer.msg.GetConversationsHasReadAndMaxSeqResp - 52, // [52:72] is the sub-list for method output_type - 32, // [32:52] is the sub-list for method input_type - 32, // [32:32] is the sub-list for extension type_name - 32, // [32:32] is the sub-list for extension extendee - 0, // [0:32] is the sub-list for field type_name + 48, // 0: OpenIMServer.msg.MsgDataToMQ.msgData:type_name -> OpenIMServer.sdkws.MsgData + 48, // 1: OpenIMServer.msg.MsgDataToDB.msgData:type_name -> OpenIMServer.sdkws.MsgData + 48, // 2: OpenIMServer.msg.PushMsgDataToMQ.msgData:type_name -> OpenIMServer.sdkws.MsgData + 48, // 3: OpenIMServer.msg.MsgDataToMongoByMQ.msgData:type_name -> OpenIMServer.sdkws.MsgData + 48, // 4: OpenIMServer.msg.SendMsgReq.msgData:type_name -> OpenIMServer.sdkws.MsgData + 48, // 5: OpenIMServer.msg.MsgDataToModifyByMQ.messages:type_name -> OpenIMServer.sdkws.MsgData + 23, // 6: OpenIMServer.msg.ClearConversationsMsgReq.deleteSyncOpt:type_name -> OpenIMServer.msg.DeleteSyncOpt + 23, // 7: OpenIMServer.msg.UserClearAllMsgReq.deleteSyncOpt:type_name -> OpenIMServer.msg.DeleteSyncOpt + 23, // 8: OpenIMServer.msg.DeleteMsgsReq.deleteSyncOpt:type_name -> OpenIMServer.msg.DeleteSyncOpt + 45, // 9: OpenIMServer.msg.GetConversationsHasReadAndMaxSeqResp.seqs:type_name -> OpenIMServer.msg.GetConversationsHasReadAndMaxSeqResp.SeqsEntry + 49, // 10: OpenIMServer.msg.GetActiveUserReq.pagination:type_name -> OpenIMServer.sdkws.RequestPagination + 50, // 11: OpenIMServer.msg.ActiveUser.user:type_name -> OpenIMServer.sdkws.UserInfo + 46, // 12: OpenIMServer.msg.GetActiveUserResp.dateCount:type_name -> OpenIMServer.msg.GetActiveUserResp.DateCountEntry + 40, // 13: OpenIMServer.msg.GetActiveUserResp.users:type_name -> OpenIMServer.msg.ActiveUser + 49, // 14: OpenIMServer.msg.GetActiveGroupReq.pagination:type_name -> OpenIMServer.sdkws.RequestPagination + 51, // 15: OpenIMServer.msg.ActiveGroup.group:type_name -> OpenIMServer.sdkws.GroupInfo + 47, // 16: OpenIMServer.msg.GetActiveGroupResp.dateCount:type_name -> OpenIMServer.msg.GetActiveGroupResp.DateCountEntry + 43, // 17: OpenIMServer.msg.GetActiveGroupResp.groups:type_name -> OpenIMServer.msg.ActiveGroup + 37, // 18: OpenIMServer.msg.GetConversationsHasReadAndMaxSeqResp.SeqsEntry.value:type_name -> OpenIMServer.msg.Seqs + 52, // 19: OpenIMServer.msg.msg.GetMaxSeq:input_type -> OpenIMServer.sdkws.GetMaxSeqReq + 34, // 20: OpenIMServer.msg.msg.GetConversationMaxSeq:input_type -> OpenIMServer.msg.GetConversationMaxSeqReq + 53, // 21: OpenIMServer.msg.msg.PullMessageBySeqs:input_type -> OpenIMServer.sdkws.PullMessageBySeqsReq + 6, // 22: OpenIMServer.msg.msg.SendMsg:input_type -> OpenIMServer.msg.SendMsgReq + 24, // 23: OpenIMServer.msg.msg.ClearConversationsMsg:input_type -> OpenIMServer.msg.ClearConversationsMsgReq + 26, // 24: OpenIMServer.msg.msg.UserClearAllMsg:input_type -> OpenIMServer.msg.UserClearAllMsgReq + 28, // 25: OpenIMServer.msg.msg.DeleteMsgs:input_type -> OpenIMServer.msg.DeleteMsgsReq + 32, // 26: OpenIMServer.msg.msg.DeleteMsgPhysicalBySeq:input_type -> OpenIMServer.msg.DeleteMsgPhysicalBySeqReq + 30, // 27: OpenIMServer.msg.msg.DeleteMsgPhysical:input_type -> OpenIMServer.msg.DeleteMsgPhysicalReq + 8, // 28: OpenIMServer.msg.msg.SetSendMsgStatus:input_type -> OpenIMServer.msg.SetSendMsgStatusReq + 10, // 29: OpenIMServer.msg.msg.GetSendMsgStatus:input_type -> OpenIMServer.msg.GetSendMsgStatusReq + 15, // 30: OpenIMServer.msg.msg.RevokeMsg:input_type -> OpenIMServer.msg.RevokeMsgReq + 17, // 31: OpenIMServer.msg.msg.MarkMsgsAsRead:input_type -> OpenIMServer.msg.MarkMsgsAsReadReq + 19, // 32: OpenIMServer.msg.msg.MarkConversationAsRead:input_type -> OpenIMServer.msg.MarkConversationAsReadReq + 21, // 33: OpenIMServer.msg.msg.SetConversationHasReadSeq:input_type -> OpenIMServer.msg.SetConversationHasReadSeqReq + 36, // 34: OpenIMServer.msg.msg.GetConversationsHasReadAndMaxSeq:input_type -> OpenIMServer.msg.GetConversationsHasReadAndMaxSeqReq + 39, // 35: OpenIMServer.msg.msg.GetActiveUser:input_type -> OpenIMServer.msg.GetActiveUserReq + 42, // 36: OpenIMServer.msg.msg.GetActiveGroup:input_type -> OpenIMServer.msg.GetActiveGroupReq + 54, // 37: OpenIMServer.msg.msg.GetMaxSeq:output_type -> OpenIMServer.sdkws.GetMaxSeqResp + 35, // 38: OpenIMServer.msg.msg.GetConversationMaxSeq:output_type -> OpenIMServer.msg.GetConversationMaxSeqResp + 55, // 39: OpenIMServer.msg.msg.PullMessageBySeqs:output_type -> OpenIMServer.sdkws.PullMessageBySeqsResp + 7, // 40: OpenIMServer.msg.msg.SendMsg:output_type -> OpenIMServer.msg.SendMsgResp + 25, // 41: OpenIMServer.msg.msg.ClearConversationsMsg:output_type -> OpenIMServer.msg.ClearConversationsMsgResp + 27, // 42: OpenIMServer.msg.msg.UserClearAllMsg:output_type -> OpenIMServer.msg.UserClearAllMsgResp + 29, // 43: OpenIMServer.msg.msg.DeleteMsgs:output_type -> OpenIMServer.msg.DeleteMsgsResp + 33, // 44: OpenIMServer.msg.msg.DeleteMsgPhysicalBySeq:output_type -> OpenIMServer.msg.DeleteMsgPhysicalBySeqResp + 31, // 45: OpenIMServer.msg.msg.DeleteMsgPhysical:output_type -> OpenIMServer.msg.DeleteMsgPhysicalResp + 9, // 46: OpenIMServer.msg.msg.SetSendMsgStatus:output_type -> OpenIMServer.msg.SetSendMsgStatusResp + 11, // 47: OpenIMServer.msg.msg.GetSendMsgStatus:output_type -> OpenIMServer.msg.GetSendMsgStatusResp + 16, // 48: OpenIMServer.msg.msg.RevokeMsg:output_type -> OpenIMServer.msg.RevokeMsgResp + 18, // 49: OpenIMServer.msg.msg.MarkMsgsAsRead:output_type -> OpenIMServer.msg.MarkMsgsAsReadResp + 20, // 50: OpenIMServer.msg.msg.MarkConversationAsRead:output_type -> OpenIMServer.msg.MarkConversationAsReadResp + 22, // 51: OpenIMServer.msg.msg.SetConversationHasReadSeq:output_type -> OpenIMServer.msg.SetConversationHasReadSeqResp + 38, // 52: OpenIMServer.msg.msg.GetConversationsHasReadAndMaxSeq:output_type -> OpenIMServer.msg.GetConversationsHasReadAndMaxSeqResp + 41, // 53: OpenIMServer.msg.msg.GetActiveUser:output_type -> OpenIMServer.msg.GetActiveUserResp + 44, // 54: OpenIMServer.msg.msg.GetActiveGroup:output_type -> OpenIMServer.msg.GetActiveGroupResp + 37, // [37:55] is the sub-list for method output_type + 19, // [19:37] is the sub-list for method input_type + 19, // [19:19] is the sub-list for extension type_name + 19, // [19:19] is the sub-list for extension extendee + 0, // [0:19] is the sub-list for field type_name } func init() { file_msg_msg_proto_init() } @@ -3734,7 +3018,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ModifyMessageReactionExtensionsReq); i { + switch v := v.(*MsgDataToModifyByMQ); i { case 0: return &v.state case 1: @@ -3746,7 +3030,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetMessageReactionExtensionsReq); i { + switch v := v.(*DelMsgsReq); i { case 0: return &v.state case 1: @@ -3758,7 +3042,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetMessageReactionExtensionsResp); i { + switch v := v.(*DelMsgsResp); i { case 0: return &v.state case 1: @@ -3770,7 +3054,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetMessagesReactionExtensionsReq); i { + switch v := v.(*RevokeMsgReq); i { case 0: return &v.state case 1: @@ -3782,7 +3066,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetMessagesReactionExtensionsResp); i { + switch v := v.(*RevokeMsgResp); i { case 0: return &v.state case 1: @@ -3794,7 +3078,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SingleMessageExtensionResult); i { + switch v := v.(*MarkMsgsAsReadReq); i { case 0: return &v.state case 1: @@ -3806,7 +3090,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ModifyMessageReactionExtensionsResp); i { + switch v := v.(*MarkMsgsAsReadResp); i { case 0: return &v.state case 1: @@ -3818,7 +3102,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteMessagesReactionExtensionsReq); i { + switch v := v.(*MarkConversationAsReadReq); i { case 0: return &v.state case 1: @@ -3830,7 +3114,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteMessagesReactionExtensionsResp); i { + switch v := v.(*MarkConversationAsReadResp); i { case 0: return &v.state case 1: @@ -3842,7 +3126,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExtendMsgResp); i { + switch v := v.(*SetConversationHasReadSeqReq); i { case 0: return &v.state case 1: @@ -3854,7 +3138,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExtendMsg); i { + switch v := v.(*SetConversationHasReadSeqResp); i { case 0: return &v.state case 1: @@ -3866,7 +3150,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*KeyValueResp); i { + switch v := v.(*DeleteSyncOpt); i { case 0: return &v.state case 1: @@ -3878,7 +3162,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MsgDataToModifyByMQ); i { + switch v := v.(*ClearConversationsMsgReq); i { case 0: return &v.state case 1: @@ -3890,7 +3174,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DelMsgsReq); i { + switch v := v.(*ClearConversationsMsgResp); i { case 0: return &v.state case 1: @@ -3902,7 +3186,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DelMsgsResp); i { + switch v := v.(*UserClearAllMsgReq); i { case 0: return &v.state case 1: @@ -3914,7 +3198,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RevokeMsgReq); i { + switch v := v.(*UserClearAllMsgResp); i { case 0: return &v.state case 1: @@ -3926,7 +3210,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RevokeMsgResp); i { + switch v := v.(*DeleteMsgsReq); i { case 0: return &v.state case 1: @@ -3938,7 +3222,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MarkMsgsAsReadReq); i { + switch v := v.(*DeleteMsgsResp); i { case 0: return &v.state case 1: @@ -3950,7 +3234,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MarkMsgsAsReadResp); i { + switch v := v.(*DeleteMsgPhysicalReq); i { case 0: return &v.state case 1: @@ -3962,7 +3246,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MarkConversationAsReadReq); i { + switch v := v.(*DeleteMsgPhysicalResp); i { case 0: return &v.state case 1: @@ -3974,7 +3258,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MarkConversationAsReadResp); i { + switch v := v.(*DeleteMsgPhysicalBySeqReq); i { case 0: return &v.state case 1: @@ -3986,7 +3270,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetConversationHasReadSeqReq); i { + switch v := v.(*DeleteMsgPhysicalBySeqResp); i { case 0: return &v.state case 1: @@ -3998,7 +3282,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetConversationHasReadSeqResp); i { + switch v := v.(*GetConversationMaxSeqReq); i { case 0: return &v.state case 1: @@ -4010,7 +3294,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteSyncOpt); i { + switch v := v.(*GetConversationMaxSeqResp); i { case 0: return &v.state case 1: @@ -4022,7 +3306,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ClearConversationsMsgReq); i { + switch v := v.(*GetConversationsHasReadAndMaxSeqReq); i { case 0: return &v.state case 1: @@ -4034,7 +3318,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ClearConversationsMsgResp); i { + switch v := v.(*Seqs); i { case 0: return &v.state case 1: @@ -4046,7 +3330,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UserClearAllMsgReq); i { + switch v := v.(*GetConversationsHasReadAndMaxSeqResp); i { case 0: return &v.state case 1: @@ -4058,7 +3342,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UserClearAllMsgResp); i { + switch v := v.(*GetActiveUserReq); i { case 0: return &v.state case 1: @@ -4070,7 +3354,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteMsgsReq); i { + switch v := v.(*ActiveUser); i { case 0: return &v.state case 1: @@ -4082,7 +3366,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteMsgsResp); i { + switch v := v.(*GetActiveUserResp); i { case 0: return &v.state case 1: @@ -4094,7 +3378,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteMsgPhysicalReq); i { + switch v := v.(*GetActiveGroupReq); i { case 0: return &v.state case 1: @@ -4106,7 +3390,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteMsgPhysicalResp); i { + switch v := v.(*ActiveGroup); i { case 0: return &v.state case 1: @@ -4118,91 +3402,7 @@ func file_msg_msg_proto_init() { } } file_msg_msg_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteMsgPhysicalBySeqReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_msg_msg_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteMsgPhysicalBySeqResp); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_msg_msg_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetConversationMaxSeqReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_msg_msg_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetConversationMaxSeqResp); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_msg_msg_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetConversationsHasReadAndMaxSeqReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_msg_msg_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Seqs); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_msg_msg_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetConversationsHasReadAndMaxSeqResp); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_msg_msg_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetMessagesReactionExtensionsReq_MessageReactionKey); i { + switch v := v.(*GetActiveGroupResp); i { case 0: return &v.state case 1: @@ -4220,7 +3420,7 @@ func file_msg_msg_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_msg_msg_proto_rawDesc, NumEnums: 0, - NumMessages: 57, + NumMessages: 48, NumExtensions: 0, NumServices: 1, }, @@ -4272,12 +3472,9 @@ type MsgClient interface { MarkMsgsAsRead(ctx context.Context, in *MarkMsgsAsReadReq, opts ...grpc.CallOption) (*MarkMsgsAsReadResp, error) MarkConversationAsRead(ctx context.Context, in *MarkConversationAsReadReq, opts ...grpc.CallOption) (*MarkConversationAsReadResp, error) SetConversationHasReadSeq(ctx context.Context, in *SetConversationHasReadSeqReq, opts ...grpc.CallOption) (*SetConversationHasReadSeqResp, error) - // 修改消息 - SetMessageReactionExtensions(ctx context.Context, in *SetMessageReactionExtensionsReq, opts ...grpc.CallOption) (*SetMessageReactionExtensionsResp, error) - GetMessagesReactionExtensions(ctx context.Context, in *GetMessagesReactionExtensionsReq, opts ...grpc.CallOption) (*GetMessagesReactionExtensionsResp, error) - AddMessageReactionExtensions(ctx context.Context, in *ModifyMessageReactionExtensionsReq, opts ...grpc.CallOption) (*ModifyMessageReactionExtensionsResp, error) - DeleteMessageReactionExtensions(ctx context.Context, in *DeleteMessagesReactionExtensionsReq, opts ...grpc.CallOption) (*DeleteMessagesReactionExtensionsResp, error) GetConversationsHasReadAndMaxSeq(ctx context.Context, in *GetConversationsHasReadAndMaxSeqReq, opts ...grpc.CallOption) (*GetConversationsHasReadAndMaxSeqResp, error) + GetActiveUser(ctx context.Context, in *GetActiveUserReq, opts ...grpc.CallOption) (*GetActiveUserResp, error) + GetActiveGroup(ctx context.Context, in *GetActiveGroupReq, opts ...grpc.CallOption) (*GetActiveGroupResp, error) } type msgClient struct { @@ -4423,45 +3620,27 @@ func (c *msgClient) SetConversationHasReadSeq(ctx context.Context, in *SetConver return out, nil } -func (c *msgClient) SetMessageReactionExtensions(ctx context.Context, in *SetMessageReactionExtensionsReq, opts ...grpc.CallOption) (*SetMessageReactionExtensionsResp, error) { - out := new(SetMessageReactionExtensionsResp) - err := c.cc.Invoke(ctx, "/OpenIMServer.msg.msg/SetMessageReactionExtensions", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *msgClient) GetMessagesReactionExtensions(ctx context.Context, in *GetMessagesReactionExtensionsReq, opts ...grpc.CallOption) (*GetMessagesReactionExtensionsResp, error) { - out := new(GetMessagesReactionExtensionsResp) - err := c.cc.Invoke(ctx, "/OpenIMServer.msg.msg/GetMessagesReactionExtensions", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *msgClient) AddMessageReactionExtensions(ctx context.Context, in *ModifyMessageReactionExtensionsReq, opts ...grpc.CallOption) (*ModifyMessageReactionExtensionsResp, error) { - out := new(ModifyMessageReactionExtensionsResp) - err := c.cc.Invoke(ctx, "/OpenIMServer.msg.msg/AddMessageReactionExtensions", in, out, opts...) +func (c *msgClient) GetConversationsHasReadAndMaxSeq(ctx context.Context, in *GetConversationsHasReadAndMaxSeqReq, opts ...grpc.CallOption) (*GetConversationsHasReadAndMaxSeqResp, error) { + out := new(GetConversationsHasReadAndMaxSeqResp) + err := c.cc.Invoke(ctx, "/OpenIMServer.msg.msg/GetConversationsHasReadAndMaxSeq", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *msgClient) DeleteMessageReactionExtensions(ctx context.Context, in *DeleteMessagesReactionExtensionsReq, opts ...grpc.CallOption) (*DeleteMessagesReactionExtensionsResp, error) { - out := new(DeleteMessagesReactionExtensionsResp) - err := c.cc.Invoke(ctx, "/OpenIMServer.msg.msg/DeleteMessageReactionExtensions", in, out, opts...) +func (c *msgClient) GetActiveUser(ctx context.Context, in *GetActiveUserReq, opts ...grpc.CallOption) (*GetActiveUserResp, error) { + out := new(GetActiveUserResp) + err := c.cc.Invoke(ctx, "/OpenIMServer.msg.msg/GetActiveUser", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *msgClient) GetConversationsHasReadAndMaxSeq(ctx context.Context, in *GetConversationsHasReadAndMaxSeqReq, opts ...grpc.CallOption) (*GetConversationsHasReadAndMaxSeqResp, error) { - out := new(GetConversationsHasReadAndMaxSeqResp) - err := c.cc.Invoke(ctx, "/OpenIMServer.msg.msg/GetConversationsHasReadAndMaxSeq", in, out, opts...) +func (c *msgClient) GetActiveGroup(ctx context.Context, in *GetActiveGroupReq, opts ...grpc.CallOption) (*GetActiveGroupResp, error) { + out := new(GetActiveGroupResp) + err := c.cc.Invoke(ctx, "/OpenIMServer.msg.msg/GetActiveGroup", in, out, opts...) if err != nil { return nil, err } @@ -4496,12 +3675,9 @@ type MsgServer interface { MarkMsgsAsRead(context.Context, *MarkMsgsAsReadReq) (*MarkMsgsAsReadResp, error) MarkConversationAsRead(context.Context, *MarkConversationAsReadReq) (*MarkConversationAsReadResp, error) SetConversationHasReadSeq(context.Context, *SetConversationHasReadSeqReq) (*SetConversationHasReadSeqResp, error) - // 修改消息 - SetMessageReactionExtensions(context.Context, *SetMessageReactionExtensionsReq) (*SetMessageReactionExtensionsResp, error) - GetMessagesReactionExtensions(context.Context, *GetMessagesReactionExtensionsReq) (*GetMessagesReactionExtensionsResp, error) - AddMessageReactionExtensions(context.Context, *ModifyMessageReactionExtensionsReq) (*ModifyMessageReactionExtensionsResp, error) - DeleteMessageReactionExtensions(context.Context, *DeleteMessagesReactionExtensionsReq) (*DeleteMessagesReactionExtensionsResp, error) GetConversationsHasReadAndMaxSeq(context.Context, *GetConversationsHasReadAndMaxSeqReq) (*GetConversationsHasReadAndMaxSeqResp, error) + GetActiveUser(context.Context, *GetActiveUserReq) (*GetActiveUserResp, error) + GetActiveGroup(context.Context, *GetActiveGroupReq) (*GetActiveGroupResp, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -4553,21 +3729,15 @@ func (*UnimplementedMsgServer) MarkConversationAsRead(context.Context, *MarkConv func (*UnimplementedMsgServer) SetConversationHasReadSeq(context.Context, *SetConversationHasReadSeqReq) (*SetConversationHasReadSeqResp, error) { return nil, status.Errorf(codes.Unimplemented, "method SetConversationHasReadSeq not implemented") } -func (*UnimplementedMsgServer) SetMessageReactionExtensions(context.Context, *SetMessageReactionExtensionsReq) (*SetMessageReactionExtensionsResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method SetMessageReactionExtensions not implemented") -} -func (*UnimplementedMsgServer) GetMessagesReactionExtensions(context.Context, *GetMessagesReactionExtensionsReq) (*GetMessagesReactionExtensionsResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetMessagesReactionExtensions not implemented") -} -func (*UnimplementedMsgServer) AddMessageReactionExtensions(context.Context, *ModifyMessageReactionExtensionsReq) (*ModifyMessageReactionExtensionsResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method AddMessageReactionExtensions not implemented") -} -func (*UnimplementedMsgServer) DeleteMessageReactionExtensions(context.Context, *DeleteMessagesReactionExtensionsReq) (*DeleteMessagesReactionExtensionsResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method DeleteMessageReactionExtensions not implemented") -} func (*UnimplementedMsgServer) GetConversationsHasReadAndMaxSeq(context.Context, *GetConversationsHasReadAndMaxSeqReq) (*GetConversationsHasReadAndMaxSeqResp, error) { return nil, status.Errorf(codes.Unimplemented, "method GetConversationsHasReadAndMaxSeq not implemented") } +func (*UnimplementedMsgServer) GetActiveUser(context.Context, *GetActiveUserReq) (*GetActiveUserResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetActiveUser not implemented") +} +func (*UnimplementedMsgServer) GetActiveGroup(context.Context, *GetActiveGroupReq) (*GetActiveGroupResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetActiveGroup not implemented") +} func RegisterMsgServer(s *grpc.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -4843,92 +4013,56 @@ func _Msg_SetConversationHasReadSeq_Handler(srv interface{}, ctx context.Context return interceptor(ctx, in, info, handler) } -func _Msg_SetMessageReactionExtensions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SetMessageReactionExtensionsReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MsgServer).SetMessageReactionExtensions(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/OpenIMServer.msg.msg/SetMessageReactionExtensions", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MsgServer).SetMessageReactionExtensions(ctx, req.(*SetMessageReactionExtensionsReq)) - } - return interceptor(ctx, in, info, handler) -} - -func _Msg_GetMessagesReactionExtensions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetMessagesReactionExtensionsReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MsgServer).GetMessagesReactionExtensions(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/OpenIMServer.msg.msg/GetMessagesReactionExtensions", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MsgServer).GetMessagesReactionExtensions(ctx, req.(*GetMessagesReactionExtensionsReq)) - } - return interceptor(ctx, in, info, handler) -} - -func _Msg_AddMessageReactionExtensions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ModifyMessageReactionExtensionsReq) +func _Msg_GetConversationsHasReadAndMaxSeq_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetConversationsHasReadAndMaxSeqReq) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(MsgServer).AddMessageReactionExtensions(ctx, in) + return srv.(MsgServer).GetConversationsHasReadAndMaxSeq(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/OpenIMServer.msg.msg/AddMessageReactionExtensions", + FullMethod: "/OpenIMServer.msg.msg/GetConversationsHasReadAndMaxSeq", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MsgServer).AddMessageReactionExtensions(ctx, req.(*ModifyMessageReactionExtensionsReq)) + return srv.(MsgServer).GetConversationsHasReadAndMaxSeq(ctx, req.(*GetConversationsHasReadAndMaxSeqReq)) } return interceptor(ctx, in, info, handler) } -func _Msg_DeleteMessageReactionExtensions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteMessagesReactionExtensionsReq) +func _Msg_GetActiveUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetActiveUserReq) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(MsgServer).DeleteMessageReactionExtensions(ctx, in) + return srv.(MsgServer).GetActiveUser(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/OpenIMServer.msg.msg/DeleteMessageReactionExtensions", + FullMethod: "/OpenIMServer.msg.msg/GetActiveUser", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MsgServer).DeleteMessageReactionExtensions(ctx, req.(*DeleteMessagesReactionExtensionsReq)) + return srv.(MsgServer).GetActiveUser(ctx, req.(*GetActiveUserReq)) } return interceptor(ctx, in, info, handler) } -func _Msg_GetConversationsHasReadAndMaxSeq_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetConversationsHasReadAndMaxSeqReq) +func _Msg_GetActiveGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetActiveGroupReq) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(MsgServer).GetConversationsHasReadAndMaxSeq(ctx, in) + return srv.(MsgServer).GetActiveGroup(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/OpenIMServer.msg.msg/GetConversationsHasReadAndMaxSeq", + FullMethod: "/OpenIMServer.msg.msg/GetActiveGroup", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MsgServer).GetConversationsHasReadAndMaxSeq(ctx, req.(*GetConversationsHasReadAndMaxSeqReq)) + return srv.(MsgServer).GetActiveGroup(ctx, req.(*GetActiveGroupReq)) } return interceptor(ctx, in, info, handler) } @@ -4998,24 +4132,16 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ Handler: _Msg_SetConversationHasReadSeq_Handler, }, { - MethodName: "SetMessageReactionExtensions", - Handler: _Msg_SetMessageReactionExtensions_Handler, - }, - { - MethodName: "GetMessagesReactionExtensions", - Handler: _Msg_GetMessagesReactionExtensions_Handler, - }, - { - MethodName: "AddMessageReactionExtensions", - Handler: _Msg_AddMessageReactionExtensions_Handler, + MethodName: "GetConversationsHasReadAndMaxSeq", + Handler: _Msg_GetConversationsHasReadAndMaxSeq_Handler, }, { - MethodName: "DeleteMessageReactionExtensions", - Handler: _Msg_DeleteMessageReactionExtensions_Handler, + MethodName: "GetActiveUser", + Handler: _Msg_GetActiveUser_Handler, }, { - MethodName: "GetConversationsHasReadAndMaxSeq", - Handler: _Msg_GetConversationsHasReadAndMaxSeq_Handler, + MethodName: "GetActiveGroup", + Handler: _Msg_GetActiveGroup_Handler, }, }, Streams: []grpc.StreamDesc{}, diff --git a/pkg/proto/msg/msg.proto b/pkg/proto/msg/msg.proto index 9fa093edf..d67195d44 100644 --- a/pkg/proto/msg/msg.proto +++ b/pkg/proto/msg/msg.proto @@ -15,7 +15,7 @@ syntax = "proto3"; package OpenIMServer.msg; import "sdkws/sdkws.proto"; -import "wrapperspb/wrapperspb.proto"; +// import "wrapperspb/wrapperspb.proto"; option go_package = "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg"; message MsgDataToMQ{ @@ -74,93 +74,6 @@ message GetSendMsgStatusResp{ } -message ModifyMessageReactionExtensionsReq { - string conversationID = 1; - int32 sessionType = 2; - map reactionExtensions = 3; - string clientMsgID = 4; - OpenIMServer.protobuf.StringValue ex = 5; - OpenIMServer.protobuf.StringValue attachedInfo = 6; - bool isReact = 7; - bool isExternalExtensions = 8; - int64 msgFirstModifyTime = 9; -} -message SetMessageReactionExtensionsReq { - string conversationID = 1; - int32 sessionType = 2; - map reactionExtensions = 3; - string clientMsgID = 4; - OpenIMServer.protobuf.StringValue ex = 5; - OpenIMServer.protobuf.StringValue attachedInfo = 6; - bool isReact = 7; - bool isExternalExtensions = 8; - int64 msgFirstModifyTime = 9; -} -message SetMessageReactionExtensionsResp { - string clientMsgID = 1; - int64 msgFirstModifyTime = 2; - bool isReact = 3; - repeated KeyValueResp result = 4; -} - -message GetMessagesReactionExtensionsReq { - string conversationID = 1; - int32 sessionType = 2; - message MessageReactionKey { - string clientMsgID = 1; - int64 msgFirstModifyTime = 2; - } - repeated MessageReactionKey messageReactionKeys = 3; - repeated string TypeKeys = 4; -} -message GetMessagesReactionExtensionsResp{ - repeated SingleMessageExtensionResult singleMessageResult = 1; - -} -message SingleMessageExtensionResult { - map reactionExtensions = 1; - string clientMsgID = 2; -} - - -message ModifyMessageReactionExtensionsResp { - repeated ExtendMsgResp successList = 1; - repeated ExtendMsgResp failedList = 2; -} - -message DeleteMessagesReactionExtensionsReq { - string operationID = 1; - string opUserID = 2; - string conversationID = 3; - int32 sessionType = 4; - string clientMsgID = 5; - bool isExternalExtensions = 6; - int64 msgFirstModifyTime = 7; - repeated sdkws.KeyValue reactionExtensions = 8; -} - -message DeleteMessagesReactionExtensionsResp { - repeated KeyValueResp result = 1; -} - -message ExtendMsgResp { - ExtendMsg extendMsg = 1; -} - -message ExtendMsg { - map reactionExtensions = 1; - string clientMsgID = 2; - int64 msgFirstModifyTime = 3; - string attachedInfo = 4; - string ex = 5; -} - -message KeyValueResp { - sdkws.KeyValue keyValue = 1; - int32 errCode = 2; - string errMsg = 3; -} - message MsgDataToModifyByMQ{ repeated sdkws.MsgData messages = 1; string conversationID = 2; @@ -255,7 +168,7 @@ message DeleteMsgPhysicalBySeqReq { } message DeleteMsgPhysicalBySeqResp { - + } message GetConversationMaxSeqReq { @@ -280,6 +193,45 @@ message GetConversationsHasReadAndMaxSeqResp { map seqs = 1; } +message GetActiveUserReq { + int64 start = 1; + int64 end = 2; + bool ase = 3; + bool group = 4; + sdkws.RequestPagination pagination = 5; +} + +message ActiveUser { + sdkws.UserInfo user = 1; + int64 count = 2; +} + +message GetActiveUserResp { + int64 msgCount = 1; + int64 userCount = 2; + map dateCount = 3; + repeated ActiveUser users = 4; +} + +message GetActiveGroupReq { + int64 start = 1; + int64 end = 2; + bool ase = 3; + sdkws.RequestPagination pagination = 4; +} + +message ActiveGroup { + sdkws.GroupInfo group = 1; + int64 count = 2; +} + +message GetActiveGroupResp { + int64 msgCount = 1; + int64 groupCount = 2; + map dateCount = 3; + repeated ActiveGroup groups = 4; +} + service msg { //获取最小最大seq(包括用户的,以及指定群组的) rpc GetMaxSeq(sdkws.GetMaxSeqReq) returns(sdkws.GetMaxSeqResp); @@ -309,11 +261,9 @@ service msg { rpc MarkMsgsAsRead(MarkMsgsAsReadReq) returns(MarkMsgsAsReadResp); rpc MarkConversationAsRead(MarkConversationAsReadReq) returns(MarkConversationAsReadResp); rpc SetConversationHasReadSeq(SetConversationHasReadSeqReq) returns(SetConversationHasReadSeqResp); - // 修改消息 - rpc SetMessageReactionExtensions(SetMessageReactionExtensionsReq) returns(SetMessageReactionExtensionsResp); - rpc GetMessagesReactionExtensions(GetMessagesReactionExtensionsReq) returns(GetMessagesReactionExtensionsResp); - rpc AddMessageReactionExtensions(ModifyMessageReactionExtensionsReq) returns(ModifyMessageReactionExtensionsResp); - rpc DeleteMessageReactionExtensions(DeleteMessagesReactionExtensionsReq) returns(DeleteMessagesReactionExtensionsResp); - + rpc GetConversationsHasReadAndMaxSeq(GetConversationsHasReadAndMaxSeqReq) returns(GetConversationsHasReadAndMaxSeqResp); + + rpc GetActiveUser(GetActiveUserReq) returns(GetActiveUserResp); + rpc GetActiveGroup(GetActiveGroupReq) returns(GetActiveGroupResp); } diff --git a/pkg/proto/msggateway/msggateway.pb.go b/pkg/proto/msggateway/msggateway.pb.go index 51f186c8c..6d3aa576a 100644 --- a/pkg/proto/msggateway/msggateway.pb.go +++ b/pkg/proto/msggateway/msggateway.pb.go @@ -459,6 +459,132 @@ func (x *GetUsersOnlineStatusResp) GetFailedResult() []*GetUsersOnlineStatusResp return nil } +type SingleDetail struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UserID string `protobuf:"bytes,1,opt,name=userID,proto3" json:"userID"` + Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status"` + SinglePlatformToken []*SinglePlatformToken `protobuf:"bytes,3,rep,name=singlePlatformToken,proto3" json:"singlePlatformToken"` +} + +func (x *SingleDetail) Reset() { + *x = SingleDetail{} + if protoimpl.UnsafeEnabled { + mi := &file_msggateway_msggateway_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SingleDetail) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SingleDetail) ProtoMessage() {} + +func (x *SingleDetail) ProtoReflect() protoreflect.Message { + mi := &file_msggateway_msggateway_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SingleDetail.ProtoReflect.Descriptor instead. +func (*SingleDetail) Descriptor() ([]byte, []int) { + return file_msggateway_msggateway_proto_rawDescGZIP(), []int{8} +} + +func (x *SingleDetail) GetUserID() string { + if x != nil { + return x.UserID + } + return "" +} + +func (x *SingleDetail) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *SingleDetail) GetSinglePlatformToken() []*SinglePlatformToken { + if x != nil { + return x.SinglePlatformToken + } + return nil +} + +type SinglePlatformToken struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Platform string `protobuf:"bytes,1,opt,name=platform,proto3" json:"platform"` + Total int32 `protobuf:"varint,2,opt,name=total,proto3" json:"total"` + Token []string `protobuf:"bytes,3,rep,name=token,proto3" json:"token"` +} + +func (x *SinglePlatformToken) Reset() { + *x = SinglePlatformToken{} + if protoimpl.UnsafeEnabled { + mi := &file_msggateway_msggateway_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SinglePlatformToken) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SinglePlatformToken) ProtoMessage() {} + +func (x *SinglePlatformToken) ProtoReflect() protoreflect.Message { + mi := &file_msggateway_msggateway_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SinglePlatformToken.ProtoReflect.Descriptor instead. +func (*SinglePlatformToken) Descriptor() ([]byte, []int) { + return file_msggateway_msggateway_proto_rawDescGZIP(), []int{9} +} + +func (x *SinglePlatformToken) GetPlatform() string { + if x != nil { + return x.Platform + } + return "" +} + +func (x *SinglePlatformToken) GetTotal() int32 { + if x != nil { + return x.Total + } + return 0 +} + +func (x *SinglePlatformToken) GetToken() []string { + if x != nil { + return x.Token + } + return nil +} + type KickUserOfflineReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -471,7 +597,7 @@ type KickUserOfflineReq struct { func (x *KickUserOfflineReq) Reset() { *x = KickUserOfflineReq{} if protoimpl.UnsafeEnabled { - mi := &file_msggateway_msggateway_proto_msgTypes[8] + mi := &file_msggateway_msggateway_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -484,7 +610,7 @@ func (x *KickUserOfflineReq) String() string { func (*KickUserOfflineReq) ProtoMessage() {} func (x *KickUserOfflineReq) ProtoReflect() protoreflect.Message { - mi := &file_msggateway_msggateway_proto_msgTypes[8] + mi := &file_msggateway_msggateway_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -497,7 +623,7 @@ func (x *KickUserOfflineReq) ProtoReflect() protoreflect.Message { // Deprecated: Use KickUserOfflineReq.ProtoReflect.Descriptor instead. func (*KickUserOfflineReq) Descriptor() ([]byte, []int) { - return file_msggateway_msggateway_proto_rawDescGZIP(), []int{8} + return file_msggateway_msggateway_proto_rawDescGZIP(), []int{10} } func (x *KickUserOfflineReq) GetPlatformID() int32 { @@ -523,7 +649,7 @@ type KickUserOfflineResp struct { func (x *KickUserOfflineResp) Reset() { *x = KickUserOfflineResp{} if protoimpl.UnsafeEnabled { - mi := &file_msggateway_msggateway_proto_msgTypes[9] + mi := &file_msggateway_msggateway_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -536,7 +662,7 @@ func (x *KickUserOfflineResp) String() string { func (*KickUserOfflineResp) ProtoMessage() {} func (x *KickUserOfflineResp) ProtoReflect() protoreflect.Message { - mi := &file_msggateway_msggateway_proto_msgTypes[9] + mi := &file_msggateway_msggateway_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -549,7 +675,7 @@ func (x *KickUserOfflineResp) ProtoReflect() protoreflect.Message { // Deprecated: Use KickUserOfflineResp.ProtoReflect.Descriptor instead. func (*KickUserOfflineResp) Descriptor() ([]byte, []int) { - return file_msggateway_msggateway_proto_rawDescGZIP(), []int{9} + return file_msggateway_msggateway_proto_rawDescGZIP(), []int{11} } type MultiTerminalLoginCheckReq struct { @@ -566,7 +692,7 @@ type MultiTerminalLoginCheckReq struct { func (x *MultiTerminalLoginCheckReq) Reset() { *x = MultiTerminalLoginCheckReq{} if protoimpl.UnsafeEnabled { - mi := &file_msggateway_msggateway_proto_msgTypes[10] + mi := &file_msggateway_msggateway_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -579,7 +705,7 @@ func (x *MultiTerminalLoginCheckReq) String() string { func (*MultiTerminalLoginCheckReq) ProtoMessage() {} func (x *MultiTerminalLoginCheckReq) ProtoReflect() protoreflect.Message { - mi := &file_msggateway_msggateway_proto_msgTypes[10] + mi := &file_msggateway_msggateway_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -592,7 +718,7 @@ func (x *MultiTerminalLoginCheckReq) ProtoReflect() protoreflect.Message { // Deprecated: Use MultiTerminalLoginCheckReq.ProtoReflect.Descriptor instead. func (*MultiTerminalLoginCheckReq) Descriptor() ([]byte, []int) { - return file_msggateway_msggateway_proto_rawDescGZIP(), []int{10} + return file_msggateway_msggateway_proto_rawDescGZIP(), []int{12} } func (x *MultiTerminalLoginCheckReq) GetUserID() string { @@ -632,7 +758,7 @@ type MultiTerminalLoginCheckResp struct { func (x *MultiTerminalLoginCheckResp) Reset() { *x = MultiTerminalLoginCheckResp{} if protoimpl.UnsafeEnabled { - mi := &file_msggateway_msggateway_proto_msgTypes[11] + mi := &file_msggateway_msggateway_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -645,7 +771,7 @@ func (x *MultiTerminalLoginCheckResp) String() string { func (*MultiTerminalLoginCheckResp) ProtoMessage() {} func (x *MultiTerminalLoginCheckResp) ProtoReflect() protoreflect.Message { - mi := &file_msggateway_msggateway_proto_msgTypes[11] + mi := &file_msggateway_msggateway_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -658,7 +784,7 @@ func (x *MultiTerminalLoginCheckResp) ProtoReflect() protoreflect.Message { // Deprecated: Use MultiTerminalLoginCheckResp.ProtoReflect.Descriptor instead. func (*MultiTerminalLoginCheckResp) Descriptor() ([]byte, []int) { - return file_msggateway_msggateway_proto_rawDescGZIP(), []int{11} + return file_msggateway_msggateway_proto_rawDescGZIP(), []int{13} } type GetUsersOnlineStatusResp_SuccessDetail struct { @@ -670,12 +796,13 @@ type GetUsersOnlineStatusResp_SuccessDetail struct { Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status"` ConnID string `protobuf:"bytes,3,opt,name=connID,proto3" json:"connID"` IsBackground bool `protobuf:"varint,4,opt,name=isBackground,proto3" json:"isBackground"` + Token string `protobuf:"bytes,5,opt,name=token,proto3" json:"token"` } func (x *GetUsersOnlineStatusResp_SuccessDetail) Reset() { *x = GetUsersOnlineStatusResp_SuccessDetail{} if protoimpl.UnsafeEnabled { - mi := &file_msggateway_msggateway_proto_msgTypes[12] + mi := &file_msggateway_msggateway_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -688,7 +815,7 @@ func (x *GetUsersOnlineStatusResp_SuccessDetail) String() string { func (*GetUsersOnlineStatusResp_SuccessDetail) ProtoMessage() {} func (x *GetUsersOnlineStatusResp_SuccessDetail) ProtoReflect() protoreflect.Message { - mi := &file_msggateway_msggateway_proto_msgTypes[12] + mi := &file_msggateway_msggateway_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -732,6 +859,13 @@ func (x *GetUsersOnlineStatusResp_SuccessDetail) GetIsBackground() bool { return false } +func (x *GetUsersOnlineStatusResp_SuccessDetail) GetToken() string { + if x != nil { + return x.Token + } + return "" +} + type GetUsersOnlineStatusResp_FailedDetail struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -743,7 +877,7 @@ type GetUsersOnlineStatusResp_FailedDetail struct { func (x *GetUsersOnlineStatusResp_FailedDetail) Reset() { *x = GetUsersOnlineStatusResp_FailedDetail{} if protoimpl.UnsafeEnabled { - mi := &file_msggateway_msggateway_proto_msgTypes[13] + mi := &file_msggateway_msggateway_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -756,7 +890,7 @@ func (x *GetUsersOnlineStatusResp_FailedDetail) String() string { func (*GetUsersOnlineStatusResp_FailedDetail) ProtoMessage() {} func (x *GetUsersOnlineStatusResp_FailedDetail) ProtoReflect() protoreflect.Message { - mi := &file_msggateway_msggateway_proto_msgTypes[13] + mi := &file_msggateway_msggateway_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -792,7 +926,7 @@ type GetUsersOnlineStatusResp_SuccessResult struct { func (x *GetUsersOnlineStatusResp_SuccessResult) Reset() { *x = GetUsersOnlineStatusResp_SuccessResult{} if protoimpl.UnsafeEnabled { - mi := &file_msggateway_msggateway_proto_msgTypes[14] + mi := &file_msggateway_msggateway_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -805,7 +939,7 @@ func (x *GetUsersOnlineStatusResp_SuccessResult) String() string { func (*GetUsersOnlineStatusResp_SuccessResult) ProtoMessage() {} func (x *GetUsersOnlineStatusResp_SuccessResult) ProtoReflect() protoreflect.Message { - mi := &file_msggateway_msggateway_proto_msgTypes[14] + mi := &file_msggateway_msggateway_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -898,7 +1032,7 @@ var file_msggateway_msggateway_proto_rawDesc = []byte{ 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x73, 0x22, - 0xc5, 0x04, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x4f, 0x6e, 0x6c, 0x69, + 0xdc, 0x04, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x65, 0x0a, 0x0d, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, @@ -912,99 +1046,116 @@ var file_msggateway_msggateway_proto_rawDesc = []byte{ 0x77, 0x61, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x0c, 0x66, 0x61, 0x69, 0x6c, 0x65, - 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x1a, 0x7f, 0x0a, 0x0d, 0x53, 0x75, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, - 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, - 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, - 0x63, 0x6f, 0x6e, 0x6e, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x6f, - 0x6e, 0x6e, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x73, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, - 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x73, 0x42, 0x61, - 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x1a, 0x26, 0x0a, 0x0c, 0x46, 0x61, 0x69, 0x6c, - 0x65, 0x64, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, - 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, - 0x1a, 0xb4, 0x01, 0x0a, 0x0d, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x73, 0x0a, 0x14, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x50, 0x6c, 0x61, 0x74, - 0x66, 0x6f, 0x72, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x3f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x1a, 0x95, 0x01, 0x0a, 0x0d, 0x53, 0x75, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, + 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, + 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, + 0x06, 0x63, 0x6f, 0x6e, 0x6e, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, + 0x6f, 0x6e, 0x6e, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x73, 0x42, 0x61, 0x63, 0x6b, 0x67, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x73, 0x42, + 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x1a, + 0x26, 0x0a, 0x0c, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, + 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x1a, 0xb4, 0x01, 0x0a, 0x0d, 0x53, 0x75, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, + 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, + 0x44, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x73, 0x0a, 0x14, 0x64, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, + 0x79, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x2e, 0x53, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x14, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x9e, + 0x01, 0x0a, 0x0c, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, + 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x5e, 0x0a, 0x13, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, + 0x6d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x4f, + 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x67, + 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x50, 0x6c, 0x61, + 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x13, 0x73, 0x69, 0x6e, 0x67, + 0x6c, 0x65, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, + 0x5d, 0x0a, 0x13, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, + 0x6d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, + 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, + 0x72, 0x6d, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x5c, + 0x0a, 0x12, 0x4b, 0x69, 0x63, 0x6b, 0x55, 0x73, 0x65, 0x72, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, + 0x65, 0x52, 0x65, 0x71, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, + 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, + 0x72, 0x6d, 0x49, 0x44, 0x12, 0x26, 0x0a, 0x0e, 0x6b, 0x69, 0x63, 0x6b, 0x55, 0x73, 0x65, 0x72, + 0x49, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6b, 0x69, + 0x63, 0x6b, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x15, 0x0a, 0x13, + 0x4b, 0x69, 0x63, 0x6b, 0x55, 0x73, 0x65, 0x72, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x22, 0x8c, 0x01, 0x0a, 0x1a, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x54, 0x65, 0x72, + 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, + 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, + 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, + 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, + 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x44, 0x22, 0x1d, 0x0a, 0x1b, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x54, 0x65, 0x72, 0x6d, 0x69, + 0x6e, 0x61, 0x6c, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, + 0x70, 0x32, 0xf1, 0x05, 0x0a, 0x0a, 0x6d, 0x73, 0x67, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, + 0x12, 0x66, 0x0a, 0x0d, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x75, 0x73, 0x68, 0x4d, 0x73, + 0x67, 0x12, 0x29, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2e, 0x6d, 0x73, 0x67, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4f, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x50, 0x75, 0x73, 0x68, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x71, 0x1a, 0x2a, 0x2e, 0x4f, + 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x67, + 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x75, 0x73, + 0x68, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x73, 0x70, 0x12, 0x7b, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x55, + 0x73, 0x65, 0x72, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x30, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x2e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x52, 0x14, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, - 0x6d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x5c, 0x0a, 0x12, 0x4b, 0x69, 0x63, 0x6b, 0x55, - 0x73, 0x65, 0x72, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x12, 0x1e, 0x0a, - 0x0a, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x49, 0x44, 0x12, 0x26, 0x0a, - 0x0e, 0x6b, 0x69, 0x63, 0x6b, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6b, 0x69, 0x63, 0x6b, 0x55, 0x73, 0x65, 0x72, 0x49, - 0x44, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x15, 0x0a, 0x13, 0x4b, 0x69, 0x63, 0x6b, 0x55, 0x73, 0x65, - 0x72, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x22, 0x8c, 0x01, 0x0a, - 0x1a, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x4c, 0x6f, - 0x67, 0x69, 0x6e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x75, - 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, - 0x72, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x49, - 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, - 0x6d, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x70, 0x65, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x22, 0x1d, 0x0a, 0x1b, 0x4d, - 0x75, 0x6c, 0x74, 0x69, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x4c, 0x6f, 0x67, 0x69, - 0x6e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x32, 0xf1, 0x05, 0x0a, 0x0a, 0x6d, - 0x73, 0x67, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x66, 0x0a, 0x0d, 0x4f, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x50, 0x75, 0x73, 0x68, 0x4d, 0x73, 0x67, 0x12, 0x29, 0x2e, 0x4f, 0x70, 0x65, - 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x67, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x75, 0x73, 0x68, 0x4d, - 0x73, 0x67, 0x52, 0x65, 0x71, 0x1a, 0x2a, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, - 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x75, 0x73, 0x68, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x73, - 0x70, 0x12, 0x7b, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x4f, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x30, 0x2e, 0x4f, 0x70, 0x65, 0x6e, + 0x65, 0x71, 0x1a, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x47, 0x65, 0x74, + 0x55, 0x73, 0x65, 0x72, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x7e, 0x0a, 0x15, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x42, + 0x61, 0x74, 0x63, 0x68, 0x50, 0x75, 0x73, 0x68, 0x4f, 0x6e, 0x65, 0x4d, 0x73, 0x67, 0x12, 0x31, + 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, + 0x67, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x42, + 0x61, 0x74, 0x63, 0x68, 0x50, 0x75, 0x73, 0x68, 0x4f, 0x6e, 0x65, 0x4d, 0x73, 0x67, 0x52, 0x65, + 0x71, 0x1a, 0x32, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2e, 0x6d, 0x73, 0x67, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4f, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x50, 0x75, 0x73, 0x68, 0x4f, 0x6e, 0x65, 0x4d, 0x73, + 0x67, 0x52, 0x65, 0x73, 0x70, 0x12, 0x88, 0x01, 0x0a, 0x1f, 0x53, 0x75, 0x70, 0x65, 0x72, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x50, + 0x75, 0x73, 0x68, 0x4f, 0x6e, 0x65, 0x4d, 0x73, 0x67, 0x12, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x67, 0x61, 0x74, 0x65, - 0x77, 0x61, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x4f, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x31, 0x2e, 0x4f, 0x70, - 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x67, 0x61, - 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x4f, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x7e, - 0x0a, 0x15, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x50, 0x75, 0x73, - 0x68, 0x4f, 0x6e, 0x65, 0x4d, 0x73, 0x67, 0x12, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, - 0x79, 0x2e, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x50, 0x75, 0x73, - 0x68, 0x4f, 0x6e, 0x65, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x71, 0x1a, 0x32, 0x2e, 0x4f, 0x70, 0x65, - 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x67, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, - 0x50, 0x75, 0x73, 0x68, 0x4f, 0x6e, 0x65, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x73, 0x70, 0x12, 0x88, - 0x01, 0x0a, 0x1f, 0x53, 0x75, 0x70, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4f, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x50, 0x75, 0x73, 0x68, 0x4f, 0x6e, 0x65, 0x4d, - 0x73, 0x67, 0x12, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4f, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x50, 0x75, 0x73, 0x68, 0x4f, 0x6e, 0x65, 0x4d, - 0x73, 0x67, 0x52, 0x65, 0x71, 0x1a, 0x32, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, - 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x50, 0x75, 0x73, 0x68, 0x4f, - 0x6e, 0x65, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x73, 0x70, 0x12, 0x6c, 0x0a, 0x0f, 0x4b, 0x69, 0x63, - 0x6b, 0x55, 0x73, 0x65, 0x72, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x2b, 0x2e, 0x4f, + 0x77, 0x61, 0x79, 0x2e, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x50, + 0x75, 0x73, 0x68, 0x4f, 0x6e, 0x65, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x71, 0x1a, 0x32, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x67, - 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4b, 0x69, 0x63, 0x6b, 0x55, 0x73, 0x65, 0x72, 0x4f, - 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x2c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, - 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x67, 0x61, 0x74, 0x65, - 0x77, 0x61, 0x79, 0x2e, 0x4b, 0x69, 0x63, 0x6b, 0x55, 0x73, 0x65, 0x72, 0x4f, 0x66, 0x66, 0x6c, - 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x84, 0x01, 0x0a, 0x17, 0x4d, 0x75, 0x6c, 0x74, - 0x69, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x43, 0x68, - 0x65, 0x63, 0x6b, 0x12, 0x33, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4d, 0x75, - 0x6c, 0x74, 0x69, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x4c, 0x6f, 0x67, 0x69, 0x6e, - 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x1a, 0x34, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, - 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x67, 0x61, 0x74, 0x65, 0x77, - 0x61, 0x79, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, - 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x42, 0x3a, - 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x70, 0x65, - 0x6e, 0x49, 0x4d, 0x53, 0x44, 0x4b, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x2d, 0x49, 0x4d, 0x2d, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x6d, 0x73, 0x67, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x42, 0x61, 0x74, + 0x63, 0x68, 0x50, 0x75, 0x73, 0x68, 0x4f, 0x6e, 0x65, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x73, 0x70, + 0x12, 0x6c, 0x0a, 0x0f, 0x4b, 0x69, 0x63, 0x6b, 0x55, 0x73, 0x65, 0x72, 0x4f, 0x66, 0x66, 0x6c, + 0x69, 0x6e, 0x65, 0x12, 0x2b, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4b, 0x69, + 0x63, 0x6b, 0x55, 0x73, 0x65, 0x72, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, + 0x1a, 0x2c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x6d, 0x73, 0x67, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4b, 0x69, 0x63, 0x6b, 0x55, + 0x73, 0x65, 0x72, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x84, + 0x01, 0x0a, 0x17, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, + 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x33, 0x2e, 0x4f, 0x70, 0x65, + 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, 0x73, 0x67, 0x67, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, + 0x61, 0x6c, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x1a, + 0x34, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x6d, + 0x73, 0x67, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x54, + 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x52, 0x65, 0x73, 0x70, 0x42, 0x3a, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x44, 0x4b, 0x2f, 0x4f, 0x70, + 0x65, 0x6e, 0x2d, 0x49, 0x4d, 0x2d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6d, 0x73, 0x67, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, + 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1019,7 +1170,7 @@ func file_msggateway_msggateway_proto_rawDescGZIP() []byte { return file_msggateway_msggateway_proto_rawDescData } -var file_msggateway_msggateway_proto_msgTypes = make([]protoimpl.MessageInfo, 15) +var file_msggateway_msggateway_proto_msgTypes = make([]protoimpl.MessageInfo, 17) var file_msggateway_msggateway_proto_goTypes = []interface{}{ (*OnlinePushMsgReq)(nil), // 0: OpenIMServer.msggateway.OnlinePushMsgReq (*OnlinePushMsgResp)(nil), // 1: OpenIMServer.msggateway.OnlinePushMsgResp @@ -1029,41 +1180,44 @@ var file_msggateway_msggateway_proto_goTypes = []interface{}{ (*SingleMsgToUserPlatform)(nil), // 5: OpenIMServer.msggateway.SingleMsgToUserPlatform (*GetUsersOnlineStatusReq)(nil), // 6: OpenIMServer.msggateway.GetUsersOnlineStatusReq (*GetUsersOnlineStatusResp)(nil), // 7: OpenIMServer.msggateway.GetUsersOnlineStatusResp - (*KickUserOfflineReq)(nil), // 8: OpenIMServer.msggateway.KickUserOfflineReq - (*KickUserOfflineResp)(nil), // 9: OpenIMServer.msggateway.KickUserOfflineResp - (*MultiTerminalLoginCheckReq)(nil), // 10: OpenIMServer.msggateway.MultiTerminalLoginCheckReq - (*MultiTerminalLoginCheckResp)(nil), // 11: OpenIMServer.msggateway.MultiTerminalLoginCheckResp - (*GetUsersOnlineStatusResp_SuccessDetail)(nil), // 12: OpenIMServer.msggateway.GetUsersOnlineStatusResp.SuccessDetail - (*GetUsersOnlineStatusResp_FailedDetail)(nil), // 13: OpenIMServer.msggateway.GetUsersOnlineStatusResp.FailedDetail - (*GetUsersOnlineStatusResp_SuccessResult)(nil), // 14: OpenIMServer.msggateway.GetUsersOnlineStatusResp.SuccessResult - (*sdkws.MsgData)(nil), // 15: OpenIMServer.sdkws.MsgData + (*SingleDetail)(nil), // 8: OpenIMServer.msggateway.SingleDetail + (*SinglePlatformToken)(nil), // 9: OpenIMServer.msggateway.SinglePlatformToken + (*KickUserOfflineReq)(nil), // 10: OpenIMServer.msggateway.KickUserOfflineReq + (*KickUserOfflineResp)(nil), // 11: OpenIMServer.msggateway.KickUserOfflineResp + (*MultiTerminalLoginCheckReq)(nil), // 12: OpenIMServer.msggateway.MultiTerminalLoginCheckReq + (*MultiTerminalLoginCheckResp)(nil), // 13: OpenIMServer.msggateway.MultiTerminalLoginCheckResp + (*GetUsersOnlineStatusResp_SuccessDetail)(nil), // 14: OpenIMServer.msggateway.GetUsersOnlineStatusResp.SuccessDetail + (*GetUsersOnlineStatusResp_FailedDetail)(nil), // 15: OpenIMServer.msggateway.GetUsersOnlineStatusResp.FailedDetail + (*GetUsersOnlineStatusResp_SuccessResult)(nil), // 16: OpenIMServer.msggateway.GetUsersOnlineStatusResp.SuccessResult + (*sdkws.MsgData)(nil), // 17: OpenIMServer.sdkws.MsgData } var file_msggateway_msggateway_proto_depIdxs = []int32{ - 15, // 0: OpenIMServer.msggateway.OnlinePushMsgReq.msgData:type_name -> OpenIMServer.sdkws.MsgData + 17, // 0: OpenIMServer.msggateway.OnlinePushMsgReq.msgData:type_name -> OpenIMServer.sdkws.MsgData 5, // 1: OpenIMServer.msggateway.OnlinePushMsgResp.resp:type_name -> OpenIMServer.msggateway.SingleMsgToUserPlatform 5, // 2: OpenIMServer.msggateway.SingleMsgToUserResults.resp:type_name -> OpenIMServer.msggateway.SingleMsgToUserPlatform - 15, // 3: OpenIMServer.msggateway.OnlineBatchPushOneMsgReq.msgData:type_name -> OpenIMServer.sdkws.MsgData + 17, // 3: OpenIMServer.msggateway.OnlineBatchPushOneMsgReq.msgData:type_name -> OpenIMServer.sdkws.MsgData 2, // 4: OpenIMServer.msggateway.OnlineBatchPushOneMsgResp.singlePushResult:type_name -> OpenIMServer.msggateway.SingleMsgToUserResults - 14, // 5: OpenIMServer.msggateway.GetUsersOnlineStatusResp.successResult:type_name -> OpenIMServer.msggateway.GetUsersOnlineStatusResp.SuccessResult - 13, // 6: OpenIMServer.msggateway.GetUsersOnlineStatusResp.failedResult:type_name -> OpenIMServer.msggateway.GetUsersOnlineStatusResp.FailedDetail - 12, // 7: OpenIMServer.msggateway.GetUsersOnlineStatusResp.SuccessResult.detailPlatformStatus:type_name -> OpenIMServer.msggateway.GetUsersOnlineStatusResp.SuccessDetail - 0, // 8: OpenIMServer.msggateway.msgGateway.OnlinePushMsg:input_type -> OpenIMServer.msggateway.OnlinePushMsgReq - 6, // 9: OpenIMServer.msggateway.msgGateway.GetUsersOnlineStatus:input_type -> OpenIMServer.msggateway.GetUsersOnlineStatusReq - 3, // 10: OpenIMServer.msggateway.msgGateway.OnlineBatchPushOneMsg:input_type -> OpenIMServer.msggateway.OnlineBatchPushOneMsgReq - 3, // 11: OpenIMServer.msggateway.msgGateway.SuperGroupOnlineBatchPushOneMsg:input_type -> OpenIMServer.msggateway.OnlineBatchPushOneMsgReq - 8, // 12: OpenIMServer.msggateway.msgGateway.KickUserOffline:input_type -> OpenIMServer.msggateway.KickUserOfflineReq - 10, // 13: OpenIMServer.msggateway.msgGateway.MultiTerminalLoginCheck:input_type -> OpenIMServer.msggateway.MultiTerminalLoginCheckReq - 1, // 14: OpenIMServer.msggateway.msgGateway.OnlinePushMsg:output_type -> OpenIMServer.msggateway.OnlinePushMsgResp - 7, // 15: OpenIMServer.msggateway.msgGateway.GetUsersOnlineStatus:output_type -> OpenIMServer.msggateway.GetUsersOnlineStatusResp - 4, // 16: OpenIMServer.msggateway.msgGateway.OnlineBatchPushOneMsg:output_type -> OpenIMServer.msggateway.OnlineBatchPushOneMsgResp - 4, // 17: OpenIMServer.msggateway.msgGateway.SuperGroupOnlineBatchPushOneMsg:output_type -> OpenIMServer.msggateway.OnlineBatchPushOneMsgResp - 9, // 18: OpenIMServer.msggateway.msgGateway.KickUserOffline:output_type -> OpenIMServer.msggateway.KickUserOfflineResp - 11, // 19: OpenIMServer.msggateway.msgGateway.MultiTerminalLoginCheck:output_type -> OpenIMServer.msggateway.MultiTerminalLoginCheckResp - 14, // [14:20] is the sub-list for method output_type - 8, // [8:14] is the sub-list for method input_type - 8, // [8:8] is the sub-list for extension type_name - 8, // [8:8] is the sub-list for extension extendee - 0, // [0:8] is the sub-list for field type_name + 16, // 5: OpenIMServer.msggateway.GetUsersOnlineStatusResp.successResult:type_name -> OpenIMServer.msggateway.GetUsersOnlineStatusResp.SuccessResult + 15, // 6: OpenIMServer.msggateway.GetUsersOnlineStatusResp.failedResult:type_name -> OpenIMServer.msggateway.GetUsersOnlineStatusResp.FailedDetail + 9, // 7: OpenIMServer.msggateway.SingleDetail.singlePlatformToken:type_name -> OpenIMServer.msggateway.SinglePlatformToken + 14, // 8: OpenIMServer.msggateway.GetUsersOnlineStatusResp.SuccessResult.detailPlatformStatus:type_name -> OpenIMServer.msggateway.GetUsersOnlineStatusResp.SuccessDetail + 0, // 9: OpenIMServer.msggateway.msgGateway.OnlinePushMsg:input_type -> OpenIMServer.msggateway.OnlinePushMsgReq + 6, // 10: OpenIMServer.msggateway.msgGateway.GetUsersOnlineStatus:input_type -> OpenIMServer.msggateway.GetUsersOnlineStatusReq + 3, // 11: OpenIMServer.msggateway.msgGateway.OnlineBatchPushOneMsg:input_type -> OpenIMServer.msggateway.OnlineBatchPushOneMsgReq + 3, // 12: OpenIMServer.msggateway.msgGateway.SuperGroupOnlineBatchPushOneMsg:input_type -> OpenIMServer.msggateway.OnlineBatchPushOneMsgReq + 10, // 13: OpenIMServer.msggateway.msgGateway.KickUserOffline:input_type -> OpenIMServer.msggateway.KickUserOfflineReq + 12, // 14: OpenIMServer.msggateway.msgGateway.MultiTerminalLoginCheck:input_type -> OpenIMServer.msggateway.MultiTerminalLoginCheckReq + 1, // 15: OpenIMServer.msggateway.msgGateway.OnlinePushMsg:output_type -> OpenIMServer.msggateway.OnlinePushMsgResp + 7, // 16: OpenIMServer.msggateway.msgGateway.GetUsersOnlineStatus:output_type -> OpenIMServer.msggateway.GetUsersOnlineStatusResp + 4, // 17: OpenIMServer.msggateway.msgGateway.OnlineBatchPushOneMsg:output_type -> OpenIMServer.msggateway.OnlineBatchPushOneMsgResp + 4, // 18: OpenIMServer.msggateway.msgGateway.SuperGroupOnlineBatchPushOneMsg:output_type -> OpenIMServer.msggateway.OnlineBatchPushOneMsgResp + 11, // 19: OpenIMServer.msggateway.msgGateway.KickUserOffline:output_type -> OpenIMServer.msggateway.KickUserOfflineResp + 13, // 20: OpenIMServer.msggateway.msgGateway.MultiTerminalLoginCheck:output_type -> OpenIMServer.msggateway.MultiTerminalLoginCheckResp + 15, // [15:21] is the sub-list for method output_type + 9, // [9:15] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name } func init() { file_msggateway_msggateway_proto_init() } @@ -1169,7 +1323,7 @@ func file_msggateway_msggateway_proto_init() { } } file_msggateway_msggateway_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*KickUserOfflineReq); i { + switch v := v.(*SingleDetail); i { case 0: return &v.state case 1: @@ -1181,7 +1335,7 @@ func file_msggateway_msggateway_proto_init() { } } file_msggateway_msggateway_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*KickUserOfflineResp); i { + switch v := v.(*SinglePlatformToken); i { case 0: return &v.state case 1: @@ -1193,7 +1347,7 @@ func file_msggateway_msggateway_proto_init() { } } file_msggateway_msggateway_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MultiTerminalLoginCheckReq); i { + switch v := v.(*KickUserOfflineReq); i { case 0: return &v.state case 1: @@ -1205,7 +1359,7 @@ func file_msggateway_msggateway_proto_init() { } } file_msggateway_msggateway_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MultiTerminalLoginCheckResp); i { + switch v := v.(*KickUserOfflineResp); i { case 0: return &v.state case 1: @@ -1217,7 +1371,7 @@ func file_msggateway_msggateway_proto_init() { } } file_msggateway_msggateway_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetUsersOnlineStatusResp_SuccessDetail); i { + switch v := v.(*MultiTerminalLoginCheckReq); i { case 0: return &v.state case 1: @@ -1229,7 +1383,7 @@ func file_msggateway_msggateway_proto_init() { } } file_msggateway_msggateway_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetUsersOnlineStatusResp_FailedDetail); i { + switch v := v.(*MultiTerminalLoginCheckResp); i { case 0: return &v.state case 1: @@ -1241,6 +1395,30 @@ func file_msggateway_msggateway_proto_init() { } } file_msggateway_msggateway_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetUsersOnlineStatusResp_SuccessDetail); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_msggateway_msggateway_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetUsersOnlineStatusResp_FailedDetail); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_msggateway_msggateway_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetUsersOnlineStatusResp_SuccessResult); i { case 0: return &v.state @@ -1259,7 +1437,7 @@ func file_msggateway_msggateway_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_msggateway_msggateway_proto_rawDesc, NumEnums: 0, - NumMessages: 15, + NumMessages: 17, NumExtensions: 0, NumServices: 1, }, diff --git a/pkg/proto/msggateway/msggateway.proto b/pkg/proto/msggateway/msggateway.proto index 0f6c0bd5f..77f0fcbf3 100644 --- a/pkg/proto/msggateway/msggateway.proto +++ b/pkg/proto/msggateway/msggateway.proto @@ -70,6 +70,7 @@ message GetUsersOnlineStatusResp{ string status = 2; string connID = 3; bool isBackground = 4; + string token =5; } message FailedDetail{ string userID = 1; @@ -80,6 +81,17 @@ message GetUsersOnlineStatusResp{ repeated SuccessDetail detailPlatformStatus = 3; } } +message SingleDetail{ + string userID = 1; + string status = 2; + repeated SinglePlatformToken singlePlatformToken = 3; +} +message SinglePlatformToken{ + string platform = 1; + int32 total =2; + repeated string token = 3; +} + message KickUserOfflineReq{ int32 platformID = 1; diff --git a/pkg/proto/sdkws/sdkws.pb.go b/pkg/proto/sdkws/sdkws.pb.go index 9a5e4d630..f2038ee5a 100644 --- a/pkg/proto/sdkws/sdkws.pb.go +++ b/pkg/proto/sdkws/sdkws.pb.go @@ -4579,425 +4579,6 @@ func (*SetAppBackgroundStatusResp) Descriptor() ([]byte, []int) { return file_sdkws_sdkws_proto_rawDescGZIP(), []int{61} } -type ExtendMsgSet struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ConversationID string `protobuf:"bytes,1,opt,name=conversationID,proto3" json:"conversationID"` - SessionType int32 `protobuf:"varint,2,opt,name=sessionType,proto3" json:"sessionType"` - ExtendMsgs map[string]*ExtendMsg `protobuf:"bytes,3,rep,name=extendMsgs,proto3" json:"extendMsgs" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - MaxMsgUpdateTime int64 `protobuf:"varint,4,opt,name=MaxMsgUpdateTime,proto3" json:"MaxMsgUpdateTime"` - ExtendMsgNum int32 `protobuf:"varint,5,opt,name=extendMsgNum,proto3" json:"extendMsgNum"` - CreateTime int64 `protobuf:"varint,6,opt,name=createTime,proto3" json:"createTime"` -} - -func (x *ExtendMsgSet) Reset() { - *x = ExtendMsgSet{} - if protoimpl.UnsafeEnabled { - mi := &file_sdkws_sdkws_proto_msgTypes[62] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ExtendMsgSet) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExtendMsgSet) ProtoMessage() {} - -func (x *ExtendMsgSet) ProtoReflect() protoreflect.Message { - mi := &file_sdkws_sdkws_proto_msgTypes[62] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExtendMsgSet.ProtoReflect.Descriptor instead. -func (*ExtendMsgSet) Descriptor() ([]byte, []int) { - return file_sdkws_sdkws_proto_rawDescGZIP(), []int{62} -} - -func (x *ExtendMsgSet) GetConversationID() string { - if x != nil { - return x.ConversationID - } - return "" -} - -func (x *ExtendMsgSet) GetSessionType() int32 { - if x != nil { - return x.SessionType - } - return 0 -} - -func (x *ExtendMsgSet) GetExtendMsgs() map[string]*ExtendMsg { - if x != nil { - return x.ExtendMsgs - } - return nil -} - -func (x *ExtendMsgSet) GetMaxMsgUpdateTime() int64 { - if x != nil { - return x.MaxMsgUpdateTime - } - return 0 -} - -func (x *ExtendMsgSet) GetExtendMsgNum() int32 { - if x != nil { - return x.ExtendMsgNum - } - return 0 -} - -func (x *ExtendMsgSet) GetCreateTime() int64 { - if x != nil { - return x.CreateTime - } - return 0 -} - -type ExtendMsg struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ReactionExtensionList map[string]*KeyValue `protobuf:"bytes,1,rep,name=reactionExtensionList,proto3" json:"reactionExtensionList" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - ClientMsgID string `protobuf:"bytes,2,opt,name=clientMsgID,proto3" json:"clientMsgID"` - MsgFirstModifyTime int64 `protobuf:"varint,3,opt,name=msgFirstModifyTime,proto3" json:"msgFirstModifyTime"` - AttachedInfo string `protobuf:"bytes,4,opt,name=attachedInfo,proto3" json:"attachedInfo"` - Ex string `protobuf:"bytes,5,opt,name=ex,proto3" json:"ex"` -} - -func (x *ExtendMsg) Reset() { - *x = ExtendMsg{} - if protoimpl.UnsafeEnabled { - mi := &file_sdkws_sdkws_proto_msgTypes[63] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ExtendMsg) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExtendMsg) ProtoMessage() {} - -func (x *ExtendMsg) ProtoReflect() protoreflect.Message { - mi := &file_sdkws_sdkws_proto_msgTypes[63] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExtendMsg.ProtoReflect.Descriptor instead. -func (*ExtendMsg) Descriptor() ([]byte, []int) { - return file_sdkws_sdkws_proto_rawDescGZIP(), []int{63} -} - -func (x *ExtendMsg) GetReactionExtensionList() map[string]*KeyValue { - if x != nil { - return x.ReactionExtensionList - } - return nil -} - -func (x *ExtendMsg) GetClientMsgID() string { - if x != nil { - return x.ClientMsgID - } - return "" -} - -func (x *ExtendMsg) GetMsgFirstModifyTime() int64 { - if x != nil { - return x.MsgFirstModifyTime - } - return 0 -} - -func (x *ExtendMsg) GetAttachedInfo() string { - if x != nil { - return x.AttachedInfo - } - return "" -} - -func (x *ExtendMsg) GetEx() string { - if x != nil { - return x.Ex - } - return "" -} - -type KeyValue struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - TypeKey string `protobuf:"bytes,1,opt,name=typeKey,proto3" json:"typeKey"` - Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value"` - LatestUpdateTime int64 `protobuf:"varint,3,opt,name=latestUpdateTime,proto3" json:"latestUpdateTime"` -} - -func (x *KeyValue) Reset() { - *x = KeyValue{} - if protoimpl.UnsafeEnabled { - mi := &file_sdkws_sdkws_proto_msgTypes[64] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *KeyValue) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*KeyValue) ProtoMessage() {} - -func (x *KeyValue) ProtoReflect() protoreflect.Message { - mi := &file_sdkws_sdkws_proto_msgTypes[64] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use KeyValue.ProtoReflect.Descriptor instead. -func (*KeyValue) Descriptor() ([]byte, []int) { - return file_sdkws_sdkws_proto_rawDescGZIP(), []int{64} -} - -func (x *KeyValue) GetTypeKey() string { - if x != nil { - return x.TypeKey - } - return "" -} - -func (x *KeyValue) GetValue() string { - if x != nil { - return x.Value - } - return "" -} - -func (x *KeyValue) GetLatestUpdateTime() int64 { - if x != nil { - return x.LatestUpdateTime - } - return 0 -} - -type ReactionMessageModifierNotification struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ConversationID string `protobuf:"bytes,1,opt,name=conversationID,proto3" json:"conversationID"` - OpUserID string `protobuf:"bytes,2,opt,name=opUserID,proto3" json:"opUserID"` - SessionType int32 `protobuf:"varint,3,opt,name=sessionType,proto3" json:"sessionType"` - SuccessReactionExtensions map[string]*KeyValue `protobuf:"bytes,4,rep,name=successReactionExtensions,proto3" json:"successReactionExtensions" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - ClientMsgID string `protobuf:"bytes,5,opt,name=clientMsgID,proto3" json:"clientMsgID"` - IsReact bool `protobuf:"varint,6,opt,name=isReact,proto3" json:"isReact"` - IsExternalExtensions bool `protobuf:"varint,7,opt,name=isExternalExtensions,proto3" json:"isExternalExtensions"` - MsgFirstModifyTime int64 `protobuf:"varint,8,opt,name=msgFirstModifyTime,proto3" json:"msgFirstModifyTime"` -} - -func (x *ReactionMessageModifierNotification) Reset() { - *x = ReactionMessageModifierNotification{} - if protoimpl.UnsafeEnabled { - mi := &file_sdkws_sdkws_proto_msgTypes[65] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ReactionMessageModifierNotification) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReactionMessageModifierNotification) ProtoMessage() {} - -func (x *ReactionMessageModifierNotification) ProtoReflect() protoreflect.Message { - mi := &file_sdkws_sdkws_proto_msgTypes[65] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReactionMessageModifierNotification.ProtoReflect.Descriptor instead. -func (*ReactionMessageModifierNotification) Descriptor() ([]byte, []int) { - return file_sdkws_sdkws_proto_rawDescGZIP(), []int{65} -} - -func (x *ReactionMessageModifierNotification) GetConversationID() string { - if x != nil { - return x.ConversationID - } - return "" -} - -func (x *ReactionMessageModifierNotification) GetOpUserID() string { - if x != nil { - return x.OpUserID - } - return "" -} - -func (x *ReactionMessageModifierNotification) GetSessionType() int32 { - if x != nil { - return x.SessionType - } - return 0 -} - -func (x *ReactionMessageModifierNotification) GetSuccessReactionExtensions() map[string]*KeyValue { - if x != nil { - return x.SuccessReactionExtensions - } - return nil -} - -func (x *ReactionMessageModifierNotification) GetClientMsgID() string { - if x != nil { - return x.ClientMsgID - } - return "" -} - -func (x *ReactionMessageModifierNotification) GetIsReact() bool { - if x != nil { - return x.IsReact - } - return false -} - -func (x *ReactionMessageModifierNotification) GetIsExternalExtensions() bool { - if x != nil { - return x.IsExternalExtensions - } - return false -} - -func (x *ReactionMessageModifierNotification) GetMsgFirstModifyTime() int64 { - if x != nil { - return x.MsgFirstModifyTime - } - return 0 -} - -type ReactionMessageDeleteNotification struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ConversationID string `protobuf:"bytes,1,opt,name=conversationID,proto3" json:"conversationID"` - OpUserID string `protobuf:"bytes,2,opt,name=opUserID,proto3" json:"opUserID"` - SessionType int32 `protobuf:"varint,3,opt,name=sessionType,proto3" json:"sessionType"` - SuccessReactionExtensions map[string]*KeyValue `protobuf:"bytes,4,rep,name=successReactionExtensions,proto3" json:"successReactionExtensions" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - ClientMsgID string `protobuf:"bytes,5,opt,name=clientMsgID,proto3" json:"clientMsgID"` - MsgFirstModifyTime int64 `protobuf:"varint,6,opt,name=msgFirstModifyTime,proto3" json:"msgFirstModifyTime"` -} - -func (x *ReactionMessageDeleteNotification) Reset() { - *x = ReactionMessageDeleteNotification{} - if protoimpl.UnsafeEnabled { - mi := &file_sdkws_sdkws_proto_msgTypes[66] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ReactionMessageDeleteNotification) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReactionMessageDeleteNotification) ProtoMessage() {} - -func (x *ReactionMessageDeleteNotification) ProtoReflect() protoreflect.Message { - mi := &file_sdkws_sdkws_proto_msgTypes[66] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReactionMessageDeleteNotification.ProtoReflect.Descriptor instead. -func (*ReactionMessageDeleteNotification) Descriptor() ([]byte, []int) { - return file_sdkws_sdkws_proto_rawDescGZIP(), []int{66} -} - -func (x *ReactionMessageDeleteNotification) GetConversationID() string { - if x != nil { - return x.ConversationID - } - return "" -} - -func (x *ReactionMessageDeleteNotification) GetOpUserID() string { - if x != nil { - return x.OpUserID - } - return "" -} - -func (x *ReactionMessageDeleteNotification) GetSessionType() int32 { - if x != nil { - return x.SessionType - } - return 0 -} - -func (x *ReactionMessageDeleteNotification) GetSuccessReactionExtensions() map[string]*KeyValue { - if x != nil { - return x.SuccessReactionExtensions - } - return nil -} - -func (x *ReactionMessageDeleteNotification) GetClientMsgID() string { - if x != nil { - return x.ClientMsgID - } - return "" -} - -func (x *ReactionMessageDeleteNotification) GetMsgFirstModifyTime() int64 { - if x != nil { - return x.MsgFirstModifyTime - } - return 0 -} - type RequestPagination struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -5010,7 +4591,7 @@ type RequestPagination struct { func (x *RequestPagination) Reset() { *x = RequestPagination{} if protoimpl.UnsafeEnabled { - mi := &file_sdkws_sdkws_proto_msgTypes[67] + mi := &file_sdkws_sdkws_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5023,7 +4604,7 @@ func (x *RequestPagination) String() string { func (*RequestPagination) ProtoMessage() {} func (x *RequestPagination) ProtoReflect() protoreflect.Message { - mi := &file_sdkws_sdkws_proto_msgTypes[67] + mi := &file_sdkws_sdkws_proto_msgTypes[62] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5036,7 +4617,7 @@ func (x *RequestPagination) ProtoReflect() protoreflect.Message { // Deprecated: Use RequestPagination.ProtoReflect.Descriptor instead. func (*RequestPagination) Descriptor() ([]byte, []int) { - return file_sdkws_sdkws_proto_rawDescGZIP(), []int{67} + return file_sdkws_sdkws_proto_rawDescGZIP(), []int{62} } func (x *RequestPagination) GetPageNumber() int32 { @@ -5835,137 +5416,19 @@ var file_sdkws_sdkws_proto_rawDesc = []byte{ 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x73, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x1c, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x41, 0x70, 0x70, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0xf8, 0x02, 0x0a, 0x0c, 0x45, - 0x78, 0x74, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x53, 0x65, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x63, - 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x79, - 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x50, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x4d, - 0x73, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x4f, 0x70, 0x65, 0x6e, - 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x45, - 0x78, 0x74, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x53, 0x65, 0x74, 0x2e, 0x45, 0x78, 0x74, 0x65, - 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x65, 0x78, 0x74, - 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x4d, 0x61, 0x78, 0x4d, 0x73, - 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x10, 0x4d, 0x61, 0x78, 0x4d, 0x73, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, - 0x69, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, - 0x4e, 0x75, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x6e, - 0x64, 0x4d, 0x73, 0x67, 0x4e, 0x75, 0x6d, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x1a, 0x5c, 0x0a, 0x0f, 0x45, 0x78, 0x74, 0x65, 0x6e, - 0x64, 0x4d, 0x73, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x33, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x4f, 0x70, - 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, - 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xe9, 0x02, 0x0a, 0x09, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, - 0x4d, 0x73, 0x67, 0x12, 0x6e, 0x0a, 0x15, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, - 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x4d, 0x73, - 0x67, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, - 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x15, 0x72, 0x65, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4c, - 0x69, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x73, 0x67, - 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x4d, 0x73, 0x67, 0x49, 0x44, 0x12, 0x2e, 0x0a, 0x12, 0x6d, 0x73, 0x67, 0x46, 0x69, 0x72, 0x73, - 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x12, 0x6d, 0x73, 0x67, 0x46, 0x69, 0x72, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, - 0x79, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, - 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x74, 0x74, - 0x61, 0x63, 0x68, 0x65, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x65, 0x78, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x65, 0x78, 0x1a, 0x66, 0x0a, 0x1a, 0x52, 0x65, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, - 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, - 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x4b, 0x65, - 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0x66, 0x0a, 0x08, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, - 0x07, 0x74, 0x79, 0x70, 0x65, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x74, 0x79, 0x70, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2a, 0x0a, - 0x10, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xae, 0x04, 0x0a, 0x23, 0x52, 0x65, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4d, 0x6f, 0x64, - 0x69, 0x66, 0x69, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, - 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x55, - 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x70, 0x55, - 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x94, 0x01, 0x0a, 0x19, 0x73, 0x75, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x56, 0x2e, 0x4f, 0x70, - 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, - 0x2e, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x19, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x20, - 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x73, 0x67, 0x49, 0x44, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x73, 0x67, 0x49, 0x44, - 0x12, 0x18, 0x0a, 0x07, 0x69, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x07, 0x69, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, 0x12, 0x32, 0x0a, 0x14, 0x69, 0x73, - 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x69, 0x73, 0x45, 0x78, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2e, - 0x0a, 0x12, 0x6d, 0x73, 0x67, 0x46, 0x69, 0x72, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, - 0x54, 0x69, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6d, 0x73, 0x67, 0x46, - 0x69, 0x72, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x1a, 0x6a, - 0x0a, 0x1e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x32, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xdc, 0x03, 0x0a, 0x21, 0x52, - 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, - 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x55, 0x73, - 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x70, 0x55, 0x73, - 0x65, 0x72, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, - 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x92, 0x01, 0x0a, 0x19, 0x73, 0x75, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x54, 0x2e, 0x4f, 0x70, 0x65, - 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, - 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x2e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x19, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x73, 0x67, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x73, 0x67, 0x49, 0x44, 0x12, 0x2e, 0x0a, - 0x12, 0x6d, 0x73, 0x67, 0x46, 0x69, 0x72, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x54, - 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6d, 0x73, 0x67, 0x46, 0x69, - 0x72, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x1a, 0x6a, 0x0a, - 0x1e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x32, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, - 0x73, 0x64, 0x6b, 0x77, 0x73, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x53, 0x0a, 0x11, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, - 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1e, - 0x0a, 0x0a, 0x73, 0x68, 0x6f, 0x77, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0a, 0x73, 0x68, 0x6f, 0x77, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x2a, 0x30, - 0x0a, 0x09, 0x50, 0x75, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x0c, 0x50, - 0x75, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x41, 0x73, 0x63, 0x10, 0x00, 0x12, 0x11, 0x0a, - 0x0d, 0x50, 0x75, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x73, 0x63, 0x10, 0x01, - 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, - 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x44, 0x4b, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x2d, 0x49, 0x4d, - 0x2d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2f, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x53, 0x0a, 0x11, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x1e, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, + 0x1e, 0x0a, 0x0a, 0x73, 0x68, 0x6f, 0x77, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x68, 0x6f, 0x77, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x2a, + 0x30, 0x0a, 0x09, 0x50, 0x75, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x0c, + 0x50, 0x75, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x41, 0x73, 0x63, 0x10, 0x00, 0x12, 0x11, + 0x0a, 0x0d, 0x50, 0x75, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x73, 0x63, 0x10, + 0x01, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x44, 0x4b, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x2d, 0x49, + 0x4d, 0x2d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x73, 0x64, 0x6b, 0x77, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -5981,94 +5444,85 @@ func file_sdkws_sdkws_proto_rawDescGZIP() []byte { } var file_sdkws_sdkws_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_sdkws_sdkws_proto_msgTypes = make([]protoimpl.MessageInfo, 79) +var file_sdkws_sdkws_proto_msgTypes = make([]protoimpl.MessageInfo, 70) var file_sdkws_sdkws_proto_goTypes = []interface{}{ - (PullOrder)(0), // 0: OpenIMServer.sdkws.PullOrder - (*GroupInfo)(nil), // 1: OpenIMServer.sdkws.GroupInfo - (*GroupInfoForSet)(nil), // 2: OpenIMServer.sdkws.GroupInfoForSet - (*GroupMemberFullInfo)(nil), // 3: OpenIMServer.sdkws.GroupMemberFullInfo - (*PublicUserInfo)(nil), // 4: OpenIMServer.sdkws.PublicUserInfo - (*UserInfo)(nil), // 5: OpenIMServer.sdkws.UserInfo - (*FriendInfo)(nil), // 6: OpenIMServer.sdkws.FriendInfo - (*BlackInfo)(nil), // 7: OpenIMServer.sdkws.BlackInfo - (*GroupRequest)(nil), // 8: OpenIMServer.sdkws.GroupRequest - (*FriendRequest)(nil), // 9: OpenIMServer.sdkws.FriendRequest - (*PullMessageBySeqsReq)(nil), // 10: OpenIMServer.sdkws.PullMessageBySeqsReq - (*SeqRange)(nil), // 11: OpenIMServer.sdkws.SeqRange - (*PullMsgs)(nil), // 12: OpenIMServer.sdkws.PullMsgs - (*PullMessageBySeqsResp)(nil), // 13: OpenIMServer.sdkws.PullMessageBySeqsResp - (*GetMaxSeqReq)(nil), // 14: OpenIMServer.sdkws.GetMaxSeqReq - (*GetMaxSeqResp)(nil), // 15: OpenIMServer.sdkws.GetMaxSeqResp - (*UserSendMsgResp)(nil), // 16: OpenIMServer.sdkws.UserSendMsgResp - (*MsgData)(nil), // 17: OpenIMServer.sdkws.MsgData - (*PushMessages)(nil), // 18: OpenIMServer.sdkws.PushMessages - (*OfflinePushInfo)(nil), // 19: OpenIMServer.sdkws.OfflinePushInfo - (*TipsComm)(nil), // 20: OpenIMServer.sdkws.TipsComm - (*GroupCreatedTips)(nil), // 21: OpenIMServer.sdkws.GroupCreatedTips - (*GroupInfoSetTips)(nil), // 22: OpenIMServer.sdkws.GroupInfoSetTips - (*GroupInfoSetNameTips)(nil), // 23: OpenIMServer.sdkws.GroupInfoSetNameTips - (*GroupInfoSetAnnouncementTips)(nil), // 24: OpenIMServer.sdkws.GroupInfoSetAnnouncementTips - (*JoinGroupApplicationTips)(nil), // 25: OpenIMServer.sdkws.JoinGroupApplicationTips - (*MemberQuitTips)(nil), // 26: OpenIMServer.sdkws.MemberQuitTips - (*GroupApplicationAcceptedTips)(nil), // 27: OpenIMServer.sdkws.GroupApplicationAcceptedTips - (*GroupApplicationRejectedTips)(nil), // 28: OpenIMServer.sdkws.GroupApplicationRejectedTips - (*GroupOwnerTransferredTips)(nil), // 29: OpenIMServer.sdkws.GroupOwnerTransferredTips - (*MemberKickedTips)(nil), // 30: OpenIMServer.sdkws.MemberKickedTips - (*MemberInvitedTips)(nil), // 31: OpenIMServer.sdkws.MemberInvitedTips - (*MemberEnterTips)(nil), // 32: OpenIMServer.sdkws.MemberEnterTips - (*GroupDismissedTips)(nil), // 33: OpenIMServer.sdkws.GroupDismissedTips - (*GroupMemberMutedTips)(nil), // 34: OpenIMServer.sdkws.GroupMemberMutedTips - (*GroupMemberCancelMutedTips)(nil), // 35: OpenIMServer.sdkws.GroupMemberCancelMutedTips - (*GroupMutedTips)(nil), // 36: OpenIMServer.sdkws.GroupMutedTips - (*GroupCancelMutedTips)(nil), // 37: OpenIMServer.sdkws.GroupCancelMutedTips - (*GroupMemberInfoSetTips)(nil), // 38: OpenIMServer.sdkws.GroupMemberInfoSetTips - (*FriendApplication)(nil), // 39: OpenIMServer.sdkws.FriendApplication - (*FromToUserID)(nil), // 40: OpenIMServer.sdkws.FromToUserID - (*FriendApplicationTips)(nil), // 41: OpenIMServer.sdkws.FriendApplicationTips - (*FriendApplicationApprovedTips)(nil), // 42: OpenIMServer.sdkws.FriendApplicationApprovedTips - (*FriendApplicationRejectedTips)(nil), // 43: OpenIMServer.sdkws.FriendApplicationRejectedTips - (*FriendAddedTips)(nil), // 44: OpenIMServer.sdkws.FriendAddedTips - (*FriendDeletedTips)(nil), // 45: OpenIMServer.sdkws.FriendDeletedTips - (*BlackAddedTips)(nil), // 46: OpenIMServer.sdkws.BlackAddedTips - (*BlackDeletedTips)(nil), // 47: OpenIMServer.sdkws.BlackDeletedTips - (*FriendInfoChangedTips)(nil), // 48: OpenIMServer.sdkws.FriendInfoChangedTips - (*UserInfoUpdatedTips)(nil), // 49: OpenIMServer.sdkws.UserInfoUpdatedTips - (*ConversationUpdateTips)(nil), // 50: OpenIMServer.sdkws.ConversationUpdateTips - (*ConversationSetPrivateTips)(nil), // 51: OpenIMServer.sdkws.ConversationSetPrivateTips - (*ConversationHasReadTips)(nil), // 52: OpenIMServer.sdkws.ConversationHasReadTips - (*NotificationElem)(nil), // 53: OpenIMServer.sdkws.NotificationElem - (*Seqs)(nil), // 54: OpenIMServer.sdkws.seqs - (*DeleteMessageTips)(nil), // 55: OpenIMServer.sdkws.DeleteMessageTips - (*RevokeMsgTips)(nil), // 56: OpenIMServer.sdkws.RevokeMsgTips - (*MessageRevokedContent)(nil), // 57: OpenIMServer.sdkws.MessageRevokedContent - (*ClearConversationTips)(nil), // 58: OpenIMServer.sdkws.ClearConversationTips - (*DeleteMsgsTips)(nil), // 59: OpenIMServer.sdkws.DeleteMsgsTips - (*MarkAsReadTips)(nil), // 60: OpenIMServer.sdkws.MarkAsReadTips - (*SetAppBackgroundStatusReq)(nil), // 61: OpenIMServer.sdkws.SetAppBackgroundStatusReq - (*SetAppBackgroundStatusResp)(nil), // 62: OpenIMServer.sdkws.SetAppBackgroundStatusResp - (*ExtendMsgSet)(nil), // 63: OpenIMServer.sdkws.ExtendMsgSet - (*ExtendMsg)(nil), // 64: OpenIMServer.sdkws.ExtendMsg - (*KeyValue)(nil), // 65: OpenIMServer.sdkws.KeyValue - (*ReactionMessageModifierNotification)(nil), // 66: OpenIMServer.sdkws.ReactionMessageModifierNotification - (*ReactionMessageDeleteNotification)(nil), // 67: OpenIMServer.sdkws.ReactionMessageDeleteNotification - (*RequestPagination)(nil), // 68: OpenIMServer.sdkws.RequestPagination - nil, // 69: OpenIMServer.sdkws.PullMessageBySeqsResp.MsgsEntry - nil, // 70: OpenIMServer.sdkws.PullMessageBySeqsResp.NotificationMsgsEntry - nil, // 71: OpenIMServer.sdkws.GetMaxSeqResp.MaxSeqsEntry - nil, // 72: OpenIMServer.sdkws.GetMaxSeqResp.MinSeqsEntry - nil, // 73: OpenIMServer.sdkws.MsgData.OptionsEntry - nil, // 74: OpenIMServer.sdkws.PushMessages.MsgsEntry - nil, // 75: OpenIMServer.sdkws.PushMessages.NotificationMsgsEntry - nil, // 76: OpenIMServer.sdkws.ExtendMsgSet.ExtendMsgsEntry - nil, // 77: OpenIMServer.sdkws.ExtendMsg.ReactionExtensionListEntry - nil, // 78: OpenIMServer.sdkws.ReactionMessageModifierNotification.SuccessReactionExtensionsEntry - nil, // 79: OpenIMServer.sdkws.ReactionMessageDeleteNotification.SuccessReactionExtensionsEntry - (*wrapperspb.Int32Value)(nil), // 80: OpenIMServer.protobuf.Int32Value + (PullOrder)(0), // 0: OpenIMServer.sdkws.PullOrder + (*GroupInfo)(nil), // 1: OpenIMServer.sdkws.GroupInfo + (*GroupInfoForSet)(nil), // 2: OpenIMServer.sdkws.GroupInfoForSet + (*GroupMemberFullInfo)(nil), // 3: OpenIMServer.sdkws.GroupMemberFullInfo + (*PublicUserInfo)(nil), // 4: OpenIMServer.sdkws.PublicUserInfo + (*UserInfo)(nil), // 5: OpenIMServer.sdkws.UserInfo + (*FriendInfo)(nil), // 6: OpenIMServer.sdkws.FriendInfo + (*BlackInfo)(nil), // 7: OpenIMServer.sdkws.BlackInfo + (*GroupRequest)(nil), // 8: OpenIMServer.sdkws.GroupRequest + (*FriendRequest)(nil), // 9: OpenIMServer.sdkws.FriendRequest + (*PullMessageBySeqsReq)(nil), // 10: OpenIMServer.sdkws.PullMessageBySeqsReq + (*SeqRange)(nil), // 11: OpenIMServer.sdkws.SeqRange + (*PullMsgs)(nil), // 12: OpenIMServer.sdkws.PullMsgs + (*PullMessageBySeqsResp)(nil), // 13: OpenIMServer.sdkws.PullMessageBySeqsResp + (*GetMaxSeqReq)(nil), // 14: OpenIMServer.sdkws.GetMaxSeqReq + (*GetMaxSeqResp)(nil), // 15: OpenIMServer.sdkws.GetMaxSeqResp + (*UserSendMsgResp)(nil), // 16: OpenIMServer.sdkws.UserSendMsgResp + (*MsgData)(nil), // 17: OpenIMServer.sdkws.MsgData + (*PushMessages)(nil), // 18: OpenIMServer.sdkws.PushMessages + (*OfflinePushInfo)(nil), // 19: OpenIMServer.sdkws.OfflinePushInfo + (*TipsComm)(nil), // 20: OpenIMServer.sdkws.TipsComm + (*GroupCreatedTips)(nil), // 21: OpenIMServer.sdkws.GroupCreatedTips + (*GroupInfoSetTips)(nil), // 22: OpenIMServer.sdkws.GroupInfoSetTips + (*GroupInfoSetNameTips)(nil), // 23: OpenIMServer.sdkws.GroupInfoSetNameTips + (*GroupInfoSetAnnouncementTips)(nil), // 24: OpenIMServer.sdkws.GroupInfoSetAnnouncementTips + (*JoinGroupApplicationTips)(nil), // 25: OpenIMServer.sdkws.JoinGroupApplicationTips + (*MemberQuitTips)(nil), // 26: OpenIMServer.sdkws.MemberQuitTips + (*GroupApplicationAcceptedTips)(nil), // 27: OpenIMServer.sdkws.GroupApplicationAcceptedTips + (*GroupApplicationRejectedTips)(nil), // 28: OpenIMServer.sdkws.GroupApplicationRejectedTips + (*GroupOwnerTransferredTips)(nil), // 29: OpenIMServer.sdkws.GroupOwnerTransferredTips + (*MemberKickedTips)(nil), // 30: OpenIMServer.sdkws.MemberKickedTips + (*MemberInvitedTips)(nil), // 31: OpenIMServer.sdkws.MemberInvitedTips + (*MemberEnterTips)(nil), // 32: OpenIMServer.sdkws.MemberEnterTips + (*GroupDismissedTips)(nil), // 33: OpenIMServer.sdkws.GroupDismissedTips + (*GroupMemberMutedTips)(nil), // 34: OpenIMServer.sdkws.GroupMemberMutedTips + (*GroupMemberCancelMutedTips)(nil), // 35: OpenIMServer.sdkws.GroupMemberCancelMutedTips + (*GroupMutedTips)(nil), // 36: OpenIMServer.sdkws.GroupMutedTips + (*GroupCancelMutedTips)(nil), // 37: OpenIMServer.sdkws.GroupCancelMutedTips + (*GroupMemberInfoSetTips)(nil), // 38: OpenIMServer.sdkws.GroupMemberInfoSetTips + (*FriendApplication)(nil), // 39: OpenIMServer.sdkws.FriendApplication + (*FromToUserID)(nil), // 40: OpenIMServer.sdkws.FromToUserID + (*FriendApplicationTips)(nil), // 41: OpenIMServer.sdkws.FriendApplicationTips + (*FriendApplicationApprovedTips)(nil), // 42: OpenIMServer.sdkws.FriendApplicationApprovedTips + (*FriendApplicationRejectedTips)(nil), // 43: OpenIMServer.sdkws.FriendApplicationRejectedTips + (*FriendAddedTips)(nil), // 44: OpenIMServer.sdkws.FriendAddedTips + (*FriendDeletedTips)(nil), // 45: OpenIMServer.sdkws.FriendDeletedTips + (*BlackAddedTips)(nil), // 46: OpenIMServer.sdkws.BlackAddedTips + (*BlackDeletedTips)(nil), // 47: OpenIMServer.sdkws.BlackDeletedTips + (*FriendInfoChangedTips)(nil), // 48: OpenIMServer.sdkws.FriendInfoChangedTips + (*UserInfoUpdatedTips)(nil), // 49: OpenIMServer.sdkws.UserInfoUpdatedTips + (*ConversationUpdateTips)(nil), // 50: OpenIMServer.sdkws.ConversationUpdateTips + (*ConversationSetPrivateTips)(nil), // 51: OpenIMServer.sdkws.ConversationSetPrivateTips + (*ConversationHasReadTips)(nil), // 52: OpenIMServer.sdkws.ConversationHasReadTips + (*NotificationElem)(nil), // 53: OpenIMServer.sdkws.NotificationElem + (*Seqs)(nil), // 54: OpenIMServer.sdkws.seqs + (*DeleteMessageTips)(nil), // 55: OpenIMServer.sdkws.DeleteMessageTips + (*RevokeMsgTips)(nil), // 56: OpenIMServer.sdkws.RevokeMsgTips + (*MessageRevokedContent)(nil), // 57: OpenIMServer.sdkws.MessageRevokedContent + (*ClearConversationTips)(nil), // 58: OpenIMServer.sdkws.ClearConversationTips + (*DeleteMsgsTips)(nil), // 59: OpenIMServer.sdkws.DeleteMsgsTips + (*MarkAsReadTips)(nil), // 60: OpenIMServer.sdkws.MarkAsReadTips + (*SetAppBackgroundStatusReq)(nil), // 61: OpenIMServer.sdkws.SetAppBackgroundStatusReq + (*SetAppBackgroundStatusResp)(nil), // 62: OpenIMServer.sdkws.SetAppBackgroundStatusResp + (*RequestPagination)(nil), // 63: OpenIMServer.sdkws.RequestPagination + nil, // 64: OpenIMServer.sdkws.PullMessageBySeqsResp.MsgsEntry + nil, // 65: OpenIMServer.sdkws.PullMessageBySeqsResp.NotificationMsgsEntry + nil, // 66: OpenIMServer.sdkws.GetMaxSeqResp.MaxSeqsEntry + nil, // 67: OpenIMServer.sdkws.GetMaxSeqResp.MinSeqsEntry + nil, // 68: OpenIMServer.sdkws.MsgData.OptionsEntry + nil, // 69: OpenIMServer.sdkws.PushMessages.MsgsEntry + nil, // 70: OpenIMServer.sdkws.PushMessages.NotificationMsgsEntry + (*wrapperspb.Int32Value)(nil), // 71: OpenIMServer.protobuf.Int32Value } var file_sdkws_sdkws_proto_depIdxs = []int32{ - 80, // 0: OpenIMServer.sdkws.GroupInfoForSet.needVerification:type_name -> OpenIMServer.protobuf.Int32Value - 80, // 1: OpenIMServer.sdkws.GroupInfoForSet.lookMemberInfo:type_name -> OpenIMServer.protobuf.Int32Value - 80, // 2: OpenIMServer.sdkws.GroupInfoForSet.applyMemberFriend:type_name -> OpenIMServer.protobuf.Int32Value + 71, // 0: OpenIMServer.sdkws.GroupInfoForSet.needVerification:type_name -> OpenIMServer.protobuf.Int32Value + 71, // 1: OpenIMServer.sdkws.GroupInfoForSet.lookMemberInfo:type_name -> OpenIMServer.protobuf.Int32Value + 71, // 2: OpenIMServer.sdkws.GroupInfoForSet.applyMemberFriend:type_name -> OpenIMServer.protobuf.Int32Value 5, // 3: OpenIMServer.sdkws.FriendInfo.friendUser:type_name -> OpenIMServer.sdkws.UserInfo 4, // 4: OpenIMServer.sdkws.BlackInfo.blackUserInfo:type_name -> OpenIMServer.sdkws.PublicUserInfo 4, // 5: OpenIMServer.sdkws.GroupRequest.userInfo:type_name -> OpenIMServer.sdkws.PublicUserInfo @@ -6076,14 +5530,14 @@ var file_sdkws_sdkws_proto_depIdxs = []int32{ 11, // 7: OpenIMServer.sdkws.PullMessageBySeqsReq.seqRanges:type_name -> OpenIMServer.sdkws.SeqRange 0, // 8: OpenIMServer.sdkws.PullMessageBySeqsReq.order:type_name -> OpenIMServer.sdkws.PullOrder 17, // 9: OpenIMServer.sdkws.PullMsgs.Msgs:type_name -> OpenIMServer.sdkws.MsgData - 69, // 10: OpenIMServer.sdkws.PullMessageBySeqsResp.msgs:type_name -> OpenIMServer.sdkws.PullMessageBySeqsResp.MsgsEntry - 70, // 11: OpenIMServer.sdkws.PullMessageBySeqsResp.notificationMsgs:type_name -> OpenIMServer.sdkws.PullMessageBySeqsResp.NotificationMsgsEntry - 71, // 12: OpenIMServer.sdkws.GetMaxSeqResp.maxSeqs:type_name -> OpenIMServer.sdkws.GetMaxSeqResp.MaxSeqsEntry - 72, // 13: OpenIMServer.sdkws.GetMaxSeqResp.minSeqs:type_name -> OpenIMServer.sdkws.GetMaxSeqResp.MinSeqsEntry - 73, // 14: OpenIMServer.sdkws.MsgData.options:type_name -> OpenIMServer.sdkws.MsgData.OptionsEntry + 64, // 10: OpenIMServer.sdkws.PullMessageBySeqsResp.msgs:type_name -> OpenIMServer.sdkws.PullMessageBySeqsResp.MsgsEntry + 65, // 11: OpenIMServer.sdkws.PullMessageBySeqsResp.notificationMsgs:type_name -> OpenIMServer.sdkws.PullMessageBySeqsResp.NotificationMsgsEntry + 66, // 12: OpenIMServer.sdkws.GetMaxSeqResp.maxSeqs:type_name -> OpenIMServer.sdkws.GetMaxSeqResp.MaxSeqsEntry + 67, // 13: OpenIMServer.sdkws.GetMaxSeqResp.minSeqs:type_name -> OpenIMServer.sdkws.GetMaxSeqResp.MinSeqsEntry + 68, // 14: OpenIMServer.sdkws.MsgData.options:type_name -> OpenIMServer.sdkws.MsgData.OptionsEntry 19, // 15: OpenIMServer.sdkws.MsgData.offlinePushInfo:type_name -> OpenIMServer.sdkws.OfflinePushInfo - 74, // 16: OpenIMServer.sdkws.PushMessages.msgs:type_name -> OpenIMServer.sdkws.PushMessages.MsgsEntry - 75, // 17: OpenIMServer.sdkws.PushMessages.notificationMsgs:type_name -> OpenIMServer.sdkws.PushMessages.NotificationMsgsEntry + 69, // 16: OpenIMServer.sdkws.PushMessages.msgs:type_name -> OpenIMServer.sdkws.PushMessages.MsgsEntry + 70, // 17: OpenIMServer.sdkws.PushMessages.notificationMsgs:type_name -> OpenIMServer.sdkws.PushMessages.NotificationMsgsEntry 1, // 18: OpenIMServer.sdkws.GroupCreatedTips.group:type_name -> OpenIMServer.sdkws.GroupInfo 3, // 19: OpenIMServer.sdkws.GroupCreatedTips.opUser:type_name -> OpenIMServer.sdkws.GroupMemberFullInfo 3, // 20: OpenIMServer.sdkws.GroupCreatedTips.memberList:type_name -> OpenIMServer.sdkws.GroupMemberFullInfo @@ -6137,23 +5591,15 @@ var file_sdkws_sdkws_proto_depIdxs = []int32{ 40, // 68: OpenIMServer.sdkws.BlackAddedTips.fromToUserID:type_name -> OpenIMServer.sdkws.FromToUserID 40, // 69: OpenIMServer.sdkws.BlackDeletedTips.fromToUserID:type_name -> OpenIMServer.sdkws.FromToUserID 40, // 70: OpenIMServer.sdkws.FriendInfoChangedTips.fromToUserID:type_name -> OpenIMServer.sdkws.FromToUserID - 76, // 71: OpenIMServer.sdkws.ExtendMsgSet.extendMsgs:type_name -> OpenIMServer.sdkws.ExtendMsgSet.ExtendMsgsEntry - 77, // 72: OpenIMServer.sdkws.ExtendMsg.reactionExtensionList:type_name -> OpenIMServer.sdkws.ExtendMsg.ReactionExtensionListEntry - 78, // 73: OpenIMServer.sdkws.ReactionMessageModifierNotification.successReactionExtensions:type_name -> OpenIMServer.sdkws.ReactionMessageModifierNotification.SuccessReactionExtensionsEntry - 79, // 74: OpenIMServer.sdkws.ReactionMessageDeleteNotification.successReactionExtensions:type_name -> OpenIMServer.sdkws.ReactionMessageDeleteNotification.SuccessReactionExtensionsEntry - 12, // 75: OpenIMServer.sdkws.PullMessageBySeqsResp.MsgsEntry.value:type_name -> OpenIMServer.sdkws.PullMsgs - 12, // 76: OpenIMServer.sdkws.PullMessageBySeqsResp.NotificationMsgsEntry.value:type_name -> OpenIMServer.sdkws.PullMsgs - 12, // 77: OpenIMServer.sdkws.PushMessages.MsgsEntry.value:type_name -> OpenIMServer.sdkws.PullMsgs - 12, // 78: OpenIMServer.sdkws.PushMessages.NotificationMsgsEntry.value:type_name -> OpenIMServer.sdkws.PullMsgs - 64, // 79: OpenIMServer.sdkws.ExtendMsgSet.ExtendMsgsEntry.value:type_name -> OpenIMServer.sdkws.ExtendMsg - 65, // 80: OpenIMServer.sdkws.ExtendMsg.ReactionExtensionListEntry.value:type_name -> OpenIMServer.sdkws.KeyValue - 65, // 81: OpenIMServer.sdkws.ReactionMessageModifierNotification.SuccessReactionExtensionsEntry.value:type_name -> OpenIMServer.sdkws.KeyValue - 65, // 82: OpenIMServer.sdkws.ReactionMessageDeleteNotification.SuccessReactionExtensionsEntry.value:type_name -> OpenIMServer.sdkws.KeyValue - 83, // [83:83] is the sub-list for method output_type - 83, // [83:83] is the sub-list for method input_type - 83, // [83:83] is the sub-list for extension type_name - 83, // [83:83] is the sub-list for extension extendee - 0, // [0:83] is the sub-list for field type_name + 12, // 71: OpenIMServer.sdkws.PullMessageBySeqsResp.MsgsEntry.value:type_name -> OpenIMServer.sdkws.PullMsgs + 12, // 72: OpenIMServer.sdkws.PullMessageBySeqsResp.NotificationMsgsEntry.value:type_name -> OpenIMServer.sdkws.PullMsgs + 12, // 73: OpenIMServer.sdkws.PushMessages.MsgsEntry.value:type_name -> OpenIMServer.sdkws.PullMsgs + 12, // 74: OpenIMServer.sdkws.PushMessages.NotificationMsgsEntry.value:type_name -> OpenIMServer.sdkws.PullMsgs + 75, // [75:75] is the sub-list for method output_type + 75, // [75:75] is the sub-list for method input_type + 75, // [75:75] is the sub-list for extension type_name + 75, // [75:75] is the sub-list for extension extendee + 0, // [0:75] is the sub-list for field type_name } func init() { file_sdkws_sdkws_proto_init() } @@ -6907,66 +6353,6 @@ func file_sdkws_sdkws_proto_init() { } } file_sdkws_sdkws_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExtendMsgSet); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_sdkws_sdkws_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExtendMsg); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_sdkws_sdkws_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*KeyValue); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_sdkws_sdkws_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReactionMessageModifierNotification); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_sdkws_sdkws_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReactionMessageDeleteNotification); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_sdkws_sdkws_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RequestPagination); i { case 0: return &v.state @@ -6985,7 +6371,7 @@ func file_sdkws_sdkws_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_sdkws_sdkws_proto_rawDesc, NumEnums: 1, - NumMessages: 79, + NumMessages: 70, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/proto/sdkws/sdkws.proto b/pkg/proto/sdkws/sdkws.proto index 4e81502d3..225172fe2 100644 --- a/pkg/proto/sdkws/sdkws.proto +++ b/pkg/proto/sdkws/sdkws.proto @@ -510,48 +510,6 @@ message SetAppBackgroundStatusReq { message SetAppBackgroundStatusResp { } -message ExtendMsgSet { - string conversationID = 1; - int32 sessionType = 2; - map extendMsgs = 3; - int64 MaxMsgUpdateTime = 4; - int32 extendMsgNum = 5; - int64 createTime = 6; -} - -message ExtendMsg { - map reactionExtensionList = 1; - string clientMsgID = 2; - int64 msgFirstModifyTime = 3; - string attachedInfo = 4; - string ex = 5; -} - -message KeyValue { - string typeKey = 1; - string value = 2; - int64 latestUpdateTime = 3; -} - -message ReactionMessageModifierNotification { - string conversationID = 1; - string opUserID = 2; - int32 sessionType = 3; - map successReactionExtensions = 4; - string clientMsgID = 5; - bool isReact = 6; - bool isExternalExtensions = 7; - int64 msgFirstModifyTime = 8; -} - -message ReactionMessageDeleteNotification { - string conversationID = 1; - string opUserID = 2; - int32 sessionType = 3; - map successReactionExtensions = 4; - string clientMsgID = 5; - int64 msgFirstModifyTime = 6; -} message RequestPagination { int32 pageNumber = 1; diff --git a/pkg/proto/third/third.pb.go b/pkg/proto/third/third.pb.go index a2512db38..1ba6c3a05 100644 --- a/pkg/proto/third/third.pb.go +++ b/pkg/proto/third/third.pb.go @@ -25,22 +25,17 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type ApplyPutReq struct { +type KeyValues struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - PutID string `protobuf:"bytes,1,opt,name=putID,proto3" json:"putID"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name"` - Size int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size"` - Hash string `protobuf:"bytes,4,opt,name=hash,proto3" json:"hash"` - ContentType string `protobuf:"bytes,5,opt,name=contentType,proto3" json:"contentType"` - FragmentSize int64 `protobuf:"varint,6,opt,name=fragmentSize,proto3" json:"fragmentSize"` - ValidTime int64 `protobuf:"varint,7,opt,name=validTime,proto3" json:"validTime"` // 文件有效时间 + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key"` + Values []string `protobuf:"bytes,2,rep,name=values,proto3" json:"values"` } -func (x *ApplyPutReq) Reset() { - *x = ApplyPutReq{} +func (x *KeyValues) Reset() { + *x = KeyValues{} if protoimpl.UnsafeEnabled { mi := &file_third_third_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -48,13 +43,13 @@ func (x *ApplyPutReq) Reset() { } } -func (x *ApplyPutReq) String() string { +func (x *KeyValues) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ApplyPutReq) ProtoMessage() {} +func (*KeyValues) ProtoMessage() {} -func (x *ApplyPutReq) ProtoReflect() protoreflect.Message { +func (x *KeyValues) ProtoReflect() protoreflect.Message { mi := &file_third_third_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -66,90 +61,124 @@ func (x *ApplyPutReq) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ApplyPutReq.ProtoReflect.Descriptor instead. -func (*ApplyPutReq) Descriptor() ([]byte, []int) { +// Deprecated: Use KeyValues.ProtoReflect.Descriptor instead. +func (*KeyValues) Descriptor() ([]byte, []int) { return file_third_third_proto_rawDescGZIP(), []int{0} } -func (x *ApplyPutReq) GetPutID() string { +func (x *KeyValues) GetKey() string { if x != nil { - return x.PutID + return x.Key } return "" } -func (x *ApplyPutReq) GetName() string { +func (x *KeyValues) GetValues() []string { if x != nil { - return x.Name + return x.Values } - return "" + return nil } -func (x *ApplyPutReq) GetSize() int64 { - if x != nil { - return x.Size +type SignPart struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PartNumber int32 `protobuf:"varint,1,opt,name=partNumber,proto3" json:"partNumber"` + Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url"` + Query []*KeyValues `protobuf:"bytes,3,rep,name=query,proto3" json:"query"` + Header []*KeyValues `protobuf:"bytes,4,rep,name=header,proto3" json:"header"` +} + +func (x *SignPart) Reset() { + *x = SignPart{} + if protoimpl.UnsafeEnabled { + mi := &file_third_third_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return 0 } -func (x *ApplyPutReq) GetHash() string { +func (x *SignPart) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SignPart) ProtoMessage() {} + +func (x *SignPart) ProtoReflect() protoreflect.Message { + mi := &file_third_third_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SignPart.ProtoReflect.Descriptor instead. +func (*SignPart) Descriptor() ([]byte, []int) { + return file_third_third_proto_rawDescGZIP(), []int{1} +} + +func (x *SignPart) GetPartNumber() int32 { if x != nil { - return x.Hash + return x.PartNumber } - return "" + return 0 } -func (x *ApplyPutReq) GetContentType() string { +func (x *SignPart) GetUrl() string { if x != nil { - return x.ContentType + return x.Url } return "" } -func (x *ApplyPutReq) GetFragmentSize() int64 { +func (x *SignPart) GetQuery() []*KeyValues { if x != nil { - return x.FragmentSize + return x.Query } - return 0 + return nil } -func (x *ApplyPutReq) GetValidTime() int64 { +func (x *SignPart) GetHeader() []*KeyValues { if x != nil { - return x.ValidTime + return x.Header } - return 0 + return nil } -type ApplyPutResp struct { +type AuthSignParts struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url"` - PutID string `protobuf:"bytes,2,opt,name=putID,proto3" json:"putID"` - FragmentSize int64 `protobuf:"varint,3,opt,name=fragmentSize,proto3" json:"fragmentSize"` - ValidTime int64 `protobuf:"varint,4,opt,name=validTime,proto3" json:"validTime"` // 上传地址的有效时间 - PutURLsHash string `protobuf:"bytes,5,opt,name=putURLsHash,proto3" json:"putURLsHash"` - PutURLs []string `protobuf:"bytes,6,rep,name=putURLs,proto3" json:"putURLs"` + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url"` + Query []*KeyValues `protobuf:"bytes,2,rep,name=query,proto3" json:"query"` + Header []*KeyValues `protobuf:"bytes,3,rep,name=header,proto3" json:"header"` + Parts []*SignPart `protobuf:"bytes,4,rep,name=parts,proto3" json:"parts"` } -func (x *ApplyPutResp) Reset() { - *x = ApplyPutResp{} +func (x *AuthSignParts) Reset() { + *x = AuthSignParts{} if protoimpl.UnsafeEnabled { - mi := &file_third_third_proto_msgTypes[1] + mi := &file_third_third_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ApplyPutResp) String() string { +func (x *AuthSignParts) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ApplyPutResp) ProtoMessage() {} +func (*AuthSignParts) ProtoMessage() {} -func (x *ApplyPutResp) ProtoReflect() protoreflect.Message { - mi := &file_third_third_proto_msgTypes[1] +func (x *AuthSignParts) ProtoReflect() protoreflect.Message { + mi := &file_third_third_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -160,78 +189,165 @@ func (x *ApplyPutResp) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ApplyPutResp.ProtoReflect.Descriptor instead. -func (*ApplyPutResp) Descriptor() ([]byte, []int) { - return file_third_third_proto_rawDescGZIP(), []int{1} +// Deprecated: Use AuthSignParts.ProtoReflect.Descriptor instead. +func (*AuthSignParts) Descriptor() ([]byte, []int) { + return file_third_third_proto_rawDescGZIP(), []int{2} } -func (x *ApplyPutResp) GetUrl() string { +func (x *AuthSignParts) GetUrl() string { if x != nil { return x.Url } return "" } -func (x *ApplyPutResp) GetPutID() string { +func (x *AuthSignParts) GetQuery() []*KeyValues { if x != nil { - return x.PutID + return x.Query } - return "" + return nil } -func (x *ApplyPutResp) GetFragmentSize() int64 { +func (x *AuthSignParts) GetHeader() []*KeyValues { if x != nil { - return x.FragmentSize + return x.Header } - return 0 + return nil +} + +func (x *AuthSignParts) GetParts() []*SignPart { + if x != nil { + return x.Parts + } + return nil +} + +type PartLimitReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *PartLimitReq) Reset() { + *x = PartLimitReq{} + if protoimpl.UnsafeEnabled { + mi := &file_third_third_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PartLimitReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PartLimitReq) ProtoMessage() {} + +func (x *PartLimitReq) ProtoReflect() protoreflect.Message { + mi := &file_third_third_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PartLimitReq.ProtoReflect.Descriptor instead. +func (*PartLimitReq) Descriptor() ([]byte, []int) { + return file_third_third_proto_rawDescGZIP(), []int{3} +} + +type PartLimitResp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MinPartSize int64 `protobuf:"varint,1,opt,name=minPartSize,proto3" json:"minPartSize"` + MaxPartSize int64 `protobuf:"varint,2,opt,name=maxPartSize,proto3" json:"maxPartSize"` + MaxNumSize int32 `protobuf:"varint,3,opt,name=maxNumSize,proto3" json:"maxNumSize"` +} + +func (x *PartLimitResp) Reset() { + *x = PartLimitResp{} + if protoimpl.UnsafeEnabled { + mi := &file_third_third_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PartLimitResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PartLimitResp) ProtoMessage() {} + +func (x *PartLimitResp) ProtoReflect() protoreflect.Message { + mi := &file_third_third_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (x *ApplyPutResp) GetValidTime() int64 { +// Deprecated: Use PartLimitResp.ProtoReflect.Descriptor instead. +func (*PartLimitResp) Descriptor() ([]byte, []int) { + return file_third_third_proto_rawDescGZIP(), []int{4} +} + +func (x *PartLimitResp) GetMinPartSize() int64 { if x != nil { - return x.ValidTime + return x.MinPartSize } return 0 } -func (x *ApplyPutResp) GetPutURLsHash() string { +func (x *PartLimitResp) GetMaxPartSize() int64 { if x != nil { - return x.PutURLsHash + return x.MaxPartSize } - return "" + return 0 } -func (x *ApplyPutResp) GetPutURLs() []string { +func (x *PartLimitResp) GetMaxNumSize() int32 { if x != nil { - return x.PutURLs + return x.MaxNumSize } - return nil + return 0 } -type ConfirmPutReq struct { +type PartSizeReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - PutID string `protobuf:"bytes,1,opt,name=putID,proto3" json:"putID"` + Size int64 `protobuf:"varint,1,opt,name=size,proto3" json:"size"` } -func (x *ConfirmPutReq) Reset() { - *x = ConfirmPutReq{} +func (x *PartSizeReq) Reset() { + *x = PartSizeReq{} if protoimpl.UnsafeEnabled { - mi := &file_third_third_proto_msgTypes[2] + mi := &file_third_third_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ConfirmPutReq) String() string { +func (x *PartSizeReq) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ConfirmPutReq) ProtoMessage() {} +func (*PartSizeReq) ProtoMessage() {} -func (x *ConfirmPutReq) ProtoReflect() protoreflect.Message { - mi := &file_third_third_proto_msgTypes[2] +func (x *PartSizeReq) ProtoReflect() protoreflect.Message { + mi := &file_third_third_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -242,43 +358,43 @@ func (x *ConfirmPutReq) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ConfirmPutReq.ProtoReflect.Descriptor instead. -func (*ConfirmPutReq) Descriptor() ([]byte, []int) { - return file_third_third_proto_rawDescGZIP(), []int{2} +// Deprecated: Use PartSizeReq.ProtoReflect.Descriptor instead. +func (*PartSizeReq) Descriptor() ([]byte, []int) { + return file_third_third_proto_rawDescGZIP(), []int{5} } -func (x *ConfirmPutReq) GetPutID() string { +func (x *PartSizeReq) GetSize() int64 { if x != nil { - return x.PutID + return x.Size } - return "" + return 0 } -type ConfirmPutResp struct { +type PartSizeResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url"` + Size int64 `protobuf:"varint,2,opt,name=size,proto3" json:"size"` } -func (x *ConfirmPutResp) Reset() { - *x = ConfirmPutResp{} +func (x *PartSizeResp) Reset() { + *x = PartSizeResp{} if protoimpl.UnsafeEnabled { - mi := &file_third_third_proto_msgTypes[3] + mi := &file_third_third_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ConfirmPutResp) String() string { +func (x *PartSizeResp) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ConfirmPutResp) ProtoMessage() {} +func (*PartSizeResp) ProtoMessage() {} -func (x *ConfirmPutResp) ProtoReflect() protoreflect.Message { - mi := &file_third_third_proto_msgTypes[3] +func (x *PartSizeResp) ProtoReflect() protoreflect.Message { + mi := &file_third_third_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -289,45 +405,49 @@ func (x *ConfirmPutResp) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ConfirmPutResp.ProtoReflect.Descriptor instead. -func (*ConfirmPutResp) Descriptor() ([]byte, []int) { - return file_third_third_proto_rawDescGZIP(), []int{3} +// Deprecated: Use PartSizeResp.ProtoReflect.Descriptor instead. +func (*PartSizeResp) Descriptor() ([]byte, []int) { + return file_third_third_proto_rawDescGZIP(), []int{6} } -func (x *ConfirmPutResp) GetUrl() string { +func (x *PartSizeResp) GetSize() int64 { if x != nil { - return x.Url + return x.Size } - return "" + return 0 } -type GetUrlReq struct { +type InitiateMultipartUploadReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name"` // 文件名 - Expires int64 `protobuf:"varint,2,opt,name=expires,proto3" json:"expires"` // url有效时间 - Attachment bool `protobuf:"varint,3,opt,name=attachment,proto3" json:"attachment"` // 是否是附件 + Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash"` + Size int64 `protobuf:"varint,2,opt,name=size,proto3" json:"size"` + PartSize int64 `protobuf:"varint,3,opt,name=partSize,proto3" json:"partSize"` + MaxParts int32 `protobuf:"varint,4,opt,name=maxParts,proto3" json:"maxParts"` + Cause string `protobuf:"bytes,5,opt,name=cause,proto3" json:"cause"` + Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name"` + ContentType string `protobuf:"bytes,7,opt,name=contentType,proto3" json:"contentType"` } -func (x *GetUrlReq) Reset() { - *x = GetUrlReq{} +func (x *InitiateMultipartUploadReq) Reset() { + *x = InitiateMultipartUploadReq{} if protoimpl.UnsafeEnabled { - mi := &file_third_third_proto_msgTypes[4] + mi := &file_third_third_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetUrlReq) String() string { +func (x *InitiateMultipartUploadReq) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetUrlReq) ProtoMessage() {} +func (*InitiateMultipartUploadReq) ProtoMessage() {} -func (x *GetUrlReq) ProtoReflect() protoreflect.Message { - mi := &file_third_third_proto_msgTypes[4] +func (x *InitiateMultipartUploadReq) ProtoReflect() protoreflect.Message { + mi := &file_third_third_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -338,59 +458,88 @@ func (x *GetUrlReq) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetUrlReq.ProtoReflect.Descriptor instead. -func (*GetUrlReq) Descriptor() ([]byte, []int) { - return file_third_third_proto_rawDescGZIP(), []int{4} +// Deprecated: Use InitiateMultipartUploadReq.ProtoReflect.Descriptor instead. +func (*InitiateMultipartUploadReq) Descriptor() ([]byte, []int) { + return file_third_third_proto_rawDescGZIP(), []int{7} } -func (x *GetUrlReq) GetName() string { +func (x *InitiateMultipartUploadReq) GetHash() string { if x != nil { - return x.Name + return x.Hash } return "" } -func (x *GetUrlReq) GetExpires() int64 { +func (x *InitiateMultipartUploadReq) GetSize() int64 { if x != nil { - return x.Expires + return x.Size } return 0 } -func (x *GetUrlReq) GetAttachment() bool { +func (x *InitiateMultipartUploadReq) GetPartSize() int64 { if x != nil { - return x.Attachment + return x.PartSize } - return false + return 0 } -type GetUrlResp struct { +func (x *InitiateMultipartUploadReq) GetMaxParts() int32 { + if x != nil { + return x.MaxParts + } + return 0 +} + +func (x *InitiateMultipartUploadReq) GetCause() string { + if x != nil { + return x.Cause + } + return "" +} + +func (x *InitiateMultipartUploadReq) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *InitiateMultipartUploadReq) GetContentType() string { + if x != nil { + return x.ContentType + } + return "" +} + +type UploadInfo struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url"` - Size int64 `protobuf:"varint,2,opt,name=size,proto3" json:"size"` - Hash string `protobuf:"bytes,3,opt,name=hash,proto3" json:"hash"` + UploadID string `protobuf:"bytes,1,opt,name=uploadID,proto3" json:"uploadID"` + PartSize int64 `protobuf:"varint,2,opt,name=partSize,proto3" json:"partSize"` + Sign *AuthSignParts `protobuf:"bytes,3,opt,name=sign,proto3" json:"sign"` + ExpireTime int64 `protobuf:"varint,4,opt,name=expireTime,proto3" json:"expireTime"` } -func (x *GetUrlResp) Reset() { - *x = GetUrlResp{} +func (x *UploadInfo) Reset() { + *x = UploadInfo{} if protoimpl.UnsafeEnabled { - mi := &file_third_third_proto_msgTypes[5] + mi := &file_third_third_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetUrlResp) String() string { +func (x *UploadInfo) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetUrlResp) ProtoMessage() {} +func (*UploadInfo) ProtoMessage() {} -func (x *GetUrlResp) ProtoReflect() protoreflect.Message { - mi := &file_third_third_proto_msgTypes[5] +func (x *UploadInfo) ProtoReflect() protoreflect.Message { + mi := &file_third_third_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -401,57 +550,65 @@ func (x *GetUrlResp) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetUrlResp.ProtoReflect.Descriptor instead. -func (*GetUrlResp) Descriptor() ([]byte, []int) { - return file_third_third_proto_rawDescGZIP(), []int{5} +// Deprecated: Use UploadInfo.ProtoReflect.Descriptor instead. +func (*UploadInfo) Descriptor() ([]byte, []int) { + return file_third_third_proto_rawDescGZIP(), []int{8} } -func (x *GetUrlResp) GetUrl() string { +func (x *UploadInfo) GetUploadID() string { if x != nil { - return x.Url + return x.UploadID } return "" } -func (x *GetUrlResp) GetSize() int64 { +func (x *UploadInfo) GetPartSize() int64 { if x != nil { - return x.Size + return x.PartSize } return 0 } -func (x *GetUrlResp) GetHash() string { +func (x *UploadInfo) GetSign() *AuthSignParts { if x != nil { - return x.Hash + return x.Sign } - return "" + return nil } -type GetPutReq struct { +func (x *UploadInfo) GetExpireTime() int64 { + if x != nil { + return x.ExpireTime + } + return 0 +} + +type InitiateMultipartUploadResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - PutID string `protobuf:"bytes,1,opt,name=putID,proto3" json:"putID"` + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url"` + Upload *UploadInfo `protobuf:"bytes,2,opt,name=upload,proto3" json:"upload"` } -func (x *GetPutReq) Reset() { - *x = GetPutReq{} +func (x *InitiateMultipartUploadResp) Reset() { + *x = InitiateMultipartUploadResp{} if protoimpl.UnsafeEnabled { - mi := &file_third_third_proto_msgTypes[6] + mi := &file_third_third_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetPutReq) String() string { +func (x *InitiateMultipartUploadResp) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetPutReq) ProtoMessage() {} +func (*InitiateMultipartUploadResp) ProtoMessage() {} -func (x *GetPutReq) ProtoReflect() protoreflect.Message { - mi := &file_third_third_proto_msgTypes[6] +func (x *InitiateMultipartUploadResp) ProtoReflect() protoreflect.Message { + mi := &file_third_third_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -462,45 +619,51 @@ func (x *GetPutReq) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetPutReq.ProtoReflect.Descriptor instead. -func (*GetPutReq) Descriptor() ([]byte, []int) { - return file_third_third_proto_rawDescGZIP(), []int{6} +// Deprecated: Use InitiateMultipartUploadResp.ProtoReflect.Descriptor instead. +func (*InitiateMultipartUploadResp) Descriptor() ([]byte, []int) { + return file_third_third_proto_rawDescGZIP(), []int{9} } -func (x *GetPutReq) GetPutID() string { +func (x *InitiateMultipartUploadResp) GetUrl() string { if x != nil { - return x.PutID + return x.Url } return "" } -type GetPutFragment struct { +func (x *InitiateMultipartUploadResp) GetUpload() *UploadInfo { + if x != nil { + return x.Upload + } + return nil +} + +type AuthSignReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Size int64 `protobuf:"varint,1,opt,name=size,proto3" json:"size"` - Hash string `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash"` - Url string `protobuf:"bytes,3,opt,name=url,proto3" json:"url"` + UploadID string `protobuf:"bytes,1,opt,name=uploadID,proto3" json:"uploadID"` + PartNumbers []int32 `protobuf:"varint,2,rep,packed,name=partNumbers,proto3" json:"partNumbers"` } -func (x *GetPutFragment) Reset() { - *x = GetPutFragment{} +func (x *AuthSignReq) Reset() { + *x = AuthSignReq{} if protoimpl.UnsafeEnabled { - mi := &file_third_third_proto_msgTypes[7] + mi := &file_third_third_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetPutFragment) String() string { +func (x *AuthSignReq) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetPutFragment) ProtoMessage() {} +func (*AuthSignReq) ProtoMessage() {} -func (x *GetPutFragment) ProtoReflect() protoreflect.Message { - mi := &file_third_third_proto_msgTypes[7] +func (x *AuthSignReq) ProtoReflect() protoreflect.Message { + mi := &file_third_third_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -511,69 +674,125 @@ func (x *GetPutFragment) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetPutFragment.ProtoReflect.Descriptor instead. -func (*GetPutFragment) Descriptor() ([]byte, []int) { - return file_third_third_proto_rawDescGZIP(), []int{7} +// Deprecated: Use AuthSignReq.ProtoReflect.Descriptor instead. +func (*AuthSignReq) Descriptor() ([]byte, []int) { + return file_third_third_proto_rawDescGZIP(), []int{10} } -func (x *GetPutFragment) GetSize() int64 { +func (x *AuthSignReq) GetUploadID() string { if x != nil { - return x.Size + return x.UploadID } - return 0 + return "" } -func (x *GetPutFragment) GetHash() string { +func (x *AuthSignReq) GetPartNumbers() []int32 { if x != nil { - return x.Hash + return x.PartNumbers } - return "" + return nil +} + +type AuthSignResp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url"` + Query []*KeyValues `protobuf:"bytes,2,rep,name=query,proto3" json:"query"` + Header []*KeyValues `protobuf:"bytes,3,rep,name=header,proto3" json:"header"` + Parts []*SignPart `protobuf:"bytes,4,rep,name=parts,proto3" json:"parts"` +} + +func (x *AuthSignResp) Reset() { + *x = AuthSignResp{} + if protoimpl.UnsafeEnabled { + mi := &file_third_third_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AuthSignResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AuthSignResp) ProtoMessage() {} + +func (x *AuthSignResp) ProtoReflect() protoreflect.Message { + mi := &file_third_third_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (x *GetPutFragment) GetUrl() string { +// Deprecated: Use AuthSignResp.ProtoReflect.Descriptor instead. +func (*AuthSignResp) Descriptor() ([]byte, []int) { + return file_third_third_proto_rawDescGZIP(), []int{11} +} + +func (x *AuthSignResp) GetUrl() string { if x != nil { return x.Url } return "" } -type GetPutResp struct { +func (x *AuthSignResp) GetQuery() []*KeyValues { + if x != nil { + return x.Query + } + return nil +} + +func (x *AuthSignResp) GetHeader() []*KeyValues { + if x != nil { + return x.Header + } + return nil +} + +func (x *AuthSignResp) GetParts() []*SignPart { + if x != nil { + return x.Parts + } + return nil +} + +type CompleteMultipartUploadReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name"` - Size int64 `protobuf:"varint,2,opt,name=size,proto3" json:"size"` - Hash string `protobuf:"bytes,3,opt,name=hash,proto3" json:"hash"` - FragmentSize int64 `protobuf:"varint,4,opt,name=fragmentSize,proto3" json:"fragmentSize"` - ContentType string `protobuf:"bytes,5,opt,name=contentType,proto3" json:"contentType"` - ValidTime int64 `protobuf:"varint,6,opt,name=validTime,proto3" json:"validTime"` // 上传地址的有效时间 - // repeated GetPutFragment fragments = 7; - // string putURLsHash = 8; - // string putID = 2; - // int64 fragmentSize = 3; - // int64 validTime = 4;// 上传地址的有效时间 - PutURLsHash string `protobuf:"bytes,7,opt,name=putURLsHash,proto3" json:"putURLsHash"` - Fragments []*GetPutFragment `protobuf:"bytes,8,rep,name=fragments,proto3" json:"fragments"` -} - -func (x *GetPutResp) Reset() { - *x = GetPutResp{} + UploadID string `protobuf:"bytes,1,opt,name=uploadID,proto3" json:"uploadID"` + Parts []string `protobuf:"bytes,2,rep,name=parts,proto3" json:"parts"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name"` + ContentType string `protobuf:"bytes,4,opt,name=contentType,proto3" json:"contentType"` + Cause string `protobuf:"bytes,5,opt,name=cause,proto3" json:"cause"` +} + +func (x *CompleteMultipartUploadReq) Reset() { + *x = CompleteMultipartUploadReq{} if protoimpl.UnsafeEnabled { - mi := &file_third_third_proto_msgTypes[8] + mi := &file_third_third_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetPutResp) String() string { +func (x *CompleteMultipartUploadReq) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetPutResp) ProtoMessage() {} +func (*CompleteMultipartUploadReq) ProtoMessage() {} -func (x *GetPutResp) ProtoReflect() protoreflect.Message { - mi := &file_third_third_proto_msgTypes[8] +func (x *CompleteMultipartUploadReq) ProtoReflect() protoreflect.Message { + mi := &file_third_third_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -584,92 +803,118 @@ func (x *GetPutResp) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetPutResp.ProtoReflect.Descriptor instead. -func (*GetPutResp) Descriptor() ([]byte, []int) { - return file_third_third_proto_rawDescGZIP(), []int{8} +// Deprecated: Use CompleteMultipartUploadReq.ProtoReflect.Descriptor instead. +func (*CompleteMultipartUploadReq) Descriptor() ([]byte, []int) { + return file_third_third_proto_rawDescGZIP(), []int{12} } -func (x *GetPutResp) GetName() string { +func (x *CompleteMultipartUploadReq) GetUploadID() string { if x != nil { - return x.Name + return x.UploadID } return "" } -func (x *GetPutResp) GetSize() int64 { +func (x *CompleteMultipartUploadReq) GetParts() []string { if x != nil { - return x.Size + return x.Parts } - return 0 + return nil } -func (x *GetPutResp) GetHash() string { +func (x *CompleteMultipartUploadReq) GetName() string { if x != nil { - return x.Hash + return x.Name } return "" } -func (x *GetPutResp) GetFragmentSize() int64 { +func (x *CompleteMultipartUploadReq) GetContentType() string { if x != nil { - return x.FragmentSize + return x.ContentType } - return 0 + return "" } -func (x *GetPutResp) GetContentType() string { +func (x *CompleteMultipartUploadReq) GetCause() string { if x != nil { - return x.ContentType + return x.Cause } return "" } -func (x *GetPutResp) GetValidTime() int64 { - if x != nil { - return x.ValidTime +type CompleteMultipartUploadResp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url"` +} + +func (x *CompleteMultipartUploadResp) Reset() { + *x = CompleteMultipartUploadResp{} + if protoimpl.UnsafeEnabled { + mi := &file_third_third_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return 0 } -func (x *GetPutResp) GetPutURLsHash() string { - if x != nil { - return x.PutURLsHash +func (x *CompleteMultipartUploadResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CompleteMultipartUploadResp) ProtoMessage() {} + +func (x *CompleteMultipartUploadResp) ProtoReflect() protoreflect.Message { + mi := &file_third_third_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) +} + +// Deprecated: Use CompleteMultipartUploadResp.ProtoReflect.Descriptor instead. +func (*CompleteMultipartUploadResp) Descriptor() ([]byte, []int) { + return file_third_third_proto_rawDescGZIP(), []int{13} } -func (x *GetPutResp) GetFragments() []*GetPutFragment { +func (x *CompleteMultipartUploadResp) GetUrl() string { if x != nil { - return x.Fragments + return x.Url } - return nil + return "" } -type GetHashInfoReq struct { +type AccessURLReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name"` } -func (x *GetHashInfoReq) Reset() { - *x = GetHashInfoReq{} +func (x *AccessURLReq) Reset() { + *x = AccessURLReq{} if protoimpl.UnsafeEnabled { - mi := &file_third_third_proto_msgTypes[9] + mi := &file_third_third_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetHashInfoReq) String() string { +func (x *AccessURLReq) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetHashInfoReq) ProtoMessage() {} +func (*AccessURLReq) ProtoMessage() {} -func (x *GetHashInfoReq) ProtoReflect() protoreflect.Message { - mi := &file_third_third_proto_msgTypes[9] +func (x *AccessURLReq) ProtoReflect() protoreflect.Message { + mi := &file_third_third_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -680,44 +925,44 @@ func (x *GetHashInfoReq) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetHashInfoReq.ProtoReflect.Descriptor instead. -func (*GetHashInfoReq) Descriptor() ([]byte, []int) { - return file_third_third_proto_rawDescGZIP(), []int{9} +// Deprecated: Use AccessURLReq.ProtoReflect.Descriptor instead. +func (*AccessURLReq) Descriptor() ([]byte, []int) { + return file_third_third_proto_rawDescGZIP(), []int{14} } -func (x *GetHashInfoReq) GetHash() string { +func (x *AccessURLReq) GetName() string { if x != nil { - return x.Hash + return x.Name } return "" } -type GetHashInfoResp struct { +type AccessURLResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash"` - Size int64 `protobuf:"varint,2,opt,name=size,proto3" json:"size"` + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url"` + ExpireTime int64 `protobuf:"varint,2,opt,name=expireTime,proto3" json:"expireTime"` } -func (x *GetHashInfoResp) Reset() { - *x = GetHashInfoResp{} +func (x *AccessURLResp) Reset() { + *x = AccessURLResp{} if protoimpl.UnsafeEnabled { - mi := &file_third_third_proto_msgTypes[10] + mi := &file_third_third_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *GetHashInfoResp) String() string { +func (x *AccessURLResp) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetHashInfoResp) ProtoMessage() {} +func (*AccessURLResp) ProtoMessage() {} -func (x *GetHashInfoResp) ProtoReflect() protoreflect.Message { - mi := &file_third_third_proto_msgTypes[10] +func (x *AccessURLResp) ProtoReflect() protoreflect.Message { + mi := &file_third_third_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -728,21 +973,21 @@ func (x *GetHashInfoResp) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetHashInfoResp.ProtoReflect.Descriptor instead. -func (*GetHashInfoResp) Descriptor() ([]byte, []int) { - return file_third_third_proto_rawDescGZIP(), []int{10} +// Deprecated: Use AccessURLResp.ProtoReflect.Descriptor instead. +func (*AccessURLResp) Descriptor() ([]byte, []int) { + return file_third_third_proto_rawDescGZIP(), []int{15} } -func (x *GetHashInfoResp) GetHash() string { +func (x *AccessURLResp) GetUrl() string { if x != nil { - return x.Hash + return x.Url } return "" } -func (x *GetHashInfoResp) GetSize() int64 { +func (x *AccessURLResp) GetExpireTime() int64 { if x != nil { - return x.Size + return x.ExpireTime } return 0 } @@ -761,7 +1006,7 @@ type FcmUpdateTokenReq struct { func (x *FcmUpdateTokenReq) Reset() { *x = FcmUpdateTokenReq{} if protoimpl.UnsafeEnabled { - mi := &file_third_third_proto_msgTypes[11] + mi := &file_third_third_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -774,7 +1019,7 @@ func (x *FcmUpdateTokenReq) String() string { func (*FcmUpdateTokenReq) ProtoMessage() {} func (x *FcmUpdateTokenReq) ProtoReflect() protoreflect.Message { - mi := &file_third_third_proto_msgTypes[11] + mi := &file_third_third_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -787,7 +1032,7 @@ func (x *FcmUpdateTokenReq) ProtoReflect() protoreflect.Message { // Deprecated: Use FcmUpdateTokenReq.ProtoReflect.Descriptor instead. func (*FcmUpdateTokenReq) Descriptor() ([]byte, []int) { - return file_third_third_proto_rawDescGZIP(), []int{11} + return file_third_third_proto_rawDescGZIP(), []int{16} } func (x *FcmUpdateTokenReq) GetPlatformID() int32 { @@ -827,7 +1072,7 @@ type FcmUpdateTokenResp struct { func (x *FcmUpdateTokenResp) Reset() { *x = FcmUpdateTokenResp{} if protoimpl.UnsafeEnabled { - mi := &file_third_third_proto_msgTypes[12] + mi := &file_third_third_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -840,7 +1085,7 @@ func (x *FcmUpdateTokenResp) String() string { func (*FcmUpdateTokenResp) ProtoMessage() {} func (x *FcmUpdateTokenResp) ProtoReflect() protoreflect.Message { - mi := &file_third_third_proto_msgTypes[12] + mi := &file_third_third_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -853,7 +1098,7 @@ func (x *FcmUpdateTokenResp) ProtoReflect() protoreflect.Message { // Deprecated: Use FcmUpdateTokenResp.ProtoReflect.Descriptor instead. func (*FcmUpdateTokenResp) Descriptor() ([]byte, []int) { - return file_third_third_proto_rawDescGZIP(), []int{12} + return file_third_third_proto_rawDescGZIP(), []int{17} } type SetAppBadgeReq struct { @@ -868,7 +1113,7 @@ type SetAppBadgeReq struct { func (x *SetAppBadgeReq) Reset() { *x = SetAppBadgeReq{} if protoimpl.UnsafeEnabled { - mi := &file_third_third_proto_msgTypes[13] + mi := &file_third_third_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -881,7 +1126,7 @@ func (x *SetAppBadgeReq) String() string { func (*SetAppBadgeReq) ProtoMessage() {} func (x *SetAppBadgeReq) ProtoReflect() protoreflect.Message { - mi := &file_third_third_proto_msgTypes[13] + mi := &file_third_third_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -894,7 +1139,7 @@ func (x *SetAppBadgeReq) ProtoReflect() protoreflect.Message { // Deprecated: Use SetAppBadgeReq.ProtoReflect.Descriptor instead. func (*SetAppBadgeReq) Descriptor() ([]byte, []int) { - return file_third_third_proto_rawDescGZIP(), []int{13} + return file_third_third_proto_rawDescGZIP(), []int{18} } func (x *SetAppBadgeReq) GetUserID() string { @@ -920,7 +1165,7 @@ type SetAppBadgeResp struct { func (x *SetAppBadgeResp) Reset() { *x = SetAppBadgeResp{} if protoimpl.UnsafeEnabled { - mi := &file_third_third_proto_msgTypes[14] + mi := &file_third_third_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -933,7 +1178,7 @@ func (x *SetAppBadgeResp) String() string { func (*SetAppBadgeResp) ProtoMessage() {} func (x *SetAppBadgeResp) ProtoReflect() protoreflect.Message { - mi := &file_third_third_proto_msgTypes[14] + mi := &file_third_third_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -946,7 +1191,7 @@ func (x *SetAppBadgeResp) ProtoReflect() protoreflect.Message { // Deprecated: Use SetAppBadgeResp.ProtoReflect.Descriptor instead. func (*SetAppBadgeResp) Descriptor() ([]byte, []int) { - return file_third_third_proto_rawDescGZIP(), []int{14} + return file_third_third_proto_rawDescGZIP(), []int{19} } var File_third_third_proto protoreflect.FileDescriptor @@ -954,133 +1199,180 @@ var File_third_third_proto protoreflect.FileDescriptor var file_third_third_proto_rawDesc = []byte{ 0x0a, 0x11, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2f, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x22, 0xc3, 0x01, 0x0a, 0x0b, 0x41, 0x70, 0x70, 0x6c, - 0x79, 0x50, 0x75, 0x74, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x75, 0x74, 0x49, 0x44, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x75, 0x74, 0x49, 0x44, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x66, - 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x0c, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x12, - 0x1c, 0x0a, 0x09, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xb4, 0x01, - 0x0a, 0x0c, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10, - 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, - 0x12, 0x14, 0x0a, 0x05, 0x70, 0x75, 0x74, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x70, 0x75, 0x74, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, - 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x66, 0x72, - 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x75, 0x74, 0x55, - 0x52, 0x4c, 0x73, 0x48, 0x61, 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, - 0x75, 0x74, 0x55, 0x52, 0x4c, 0x73, 0x48, 0x61, 0x73, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x75, - 0x74, 0x55, 0x52, 0x4c, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x70, 0x75, 0x74, - 0x55, 0x52, 0x4c, 0x73, 0x22, 0x25, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x50, - 0x75, 0x74, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x75, 0x74, 0x49, 0x44, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x75, 0x74, 0x49, 0x44, 0x22, 0x22, 0x0a, 0x0e, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x50, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, - 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, - 0x59, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x55, 0x72, 0x6c, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x74, - 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, - 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x46, 0x0a, 0x0a, 0x47, 0x65, - 0x74, 0x55, 0x72, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, - 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, - 0x73, 0x68, 0x22, 0x21, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x50, 0x75, 0x74, 0x52, 0x65, 0x71, 0x12, - 0x14, 0x0a, 0x05, 0x70, 0x75, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x70, 0x75, 0x74, 0x49, 0x44, 0x22, 0x4a, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x50, 0x75, 0x74, 0x46, - 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, - 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, - 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, - 0x6c, 0x22, 0x90, 0x02, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x50, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x22, 0x0a, 0x0c, - 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0c, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65, + 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x22, 0x35, 0x0a, 0x09, 0x4b, 0x65, 0x79, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0xa8, + 0x01, 0x0a, 0x08, 0x53, 0x69, 0x67, 0x6e, 0x50, 0x61, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x70, + 0x61, 0x72, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0a, 0x70, 0x61, 0x72, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x75, + 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x33, 0x0a, + 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x4f, + 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, + 0x64, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x05, 0x71, 0x75, 0x65, + 0x72, 0x79, 0x12, 0x35, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x22, 0xc1, 0x01, 0x0a, 0x0d, 0x41, 0x75, + 0x74, 0x68, 0x53, 0x69, 0x67, 0x6e, 0x50, 0x61, 0x72, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x75, + 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x33, 0x0a, + 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x4f, + 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, + 0x64, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x05, 0x71, 0x75, 0x65, + 0x72, 0x79, 0x12, 0x35, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x32, 0x0a, 0x05, 0x70, 0x61, 0x72, + 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, + 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x53, 0x69, + 0x67, 0x6e, 0x50, 0x61, 0x72, 0x74, 0x52, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x22, 0x0e, 0x0a, + 0x0c, 0x50, 0x61, 0x72, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x22, 0x73, 0x0a, + 0x0d, 0x50, 0x61, 0x72, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x20, + 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x50, 0x61, 0x72, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6d, 0x69, 0x6e, 0x50, 0x61, 0x72, 0x74, 0x53, 0x69, 0x7a, 0x65, + 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x50, 0x61, 0x72, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x50, 0x61, 0x72, 0x74, 0x53, 0x69, + 0x7a, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x4e, 0x75, 0x6d, 0x53, 0x69, 0x7a, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x4e, 0x75, 0x6d, 0x53, 0x69, + 0x7a, 0x65, 0x22, 0x21, 0x0a, 0x0b, 0x50, 0x61, 0x72, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x52, 0x65, + 0x71, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x22, 0x0a, 0x0c, 0x50, 0x61, 0x72, 0x74, 0x53, 0x69, 0x7a, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xc8, 0x01, 0x0a, 0x1a, 0x49, 0x6e, + 0x69, 0x74, 0x69, 0x61, 0x74, 0x65, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x61, 0x72, 0x74, 0x55, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, + 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, + 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x72, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x08, 0x70, 0x61, 0x72, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x6d, 0x61, 0x78, 0x50, 0x61, 0x72, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, + 0x6d, 0x61, 0x78, 0x50, 0x61, 0x72, 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x61, 0x75, 0x73, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x61, 0x75, 0x73, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x22, 0x9b, 0x01, 0x0a, 0x0a, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x44, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x44, 0x12, + 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x72, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x08, 0x70, 0x61, 0x72, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x73, + 0x69, 0x67, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x6e, + 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x41, + 0x75, 0x74, 0x68, 0x53, 0x69, 0x67, 0x6e, 0x50, 0x61, 0x72, 0x74, 0x73, 0x52, 0x04, 0x73, 0x69, + 0x67, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x54, 0x69, 0x6d, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x54, 0x69, + 0x6d, 0x65, 0x22, 0x67, 0x0a, 0x1b, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x65, 0x4d, 0x75, + 0x6c, 0x74, 0x69, 0x70, 0x61, 0x72, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, + 0x70, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x75, 0x72, 0x6c, 0x12, 0x36, 0x0a, 0x06, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x4b, 0x0a, 0x0b, 0x41, + 0x75, 0x74, 0x68, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x4e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x05, 0x52, 0x0b, 0x70, 0x61, 0x72, + 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x22, 0xc0, 0x01, 0x0a, 0x0c, 0x41, 0x75, 0x74, + 0x68, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x33, 0x0a, 0x05, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x4f, 0x70, 0x65, + 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, + 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, + 0x12, 0x35, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1d, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, + 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x32, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x53, 0x69, 0x67, 0x6e, + 0x50, 0x61, 0x72, 0x74, 0x52, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x22, 0x9a, 0x01, 0x0a, 0x1a, + 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x61, 0x72, + 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x54, 0x69, 0x6d, 0x65, - 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x75, 0x74, 0x55, 0x52, 0x4c, 0x73, 0x48, 0x61, 0x73, 0x68, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x75, 0x74, 0x55, 0x52, 0x4c, 0x73, 0x48, 0x61, - 0x73, 0x68, 0x12, 0x40, 0x0a, 0x09, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, - 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x75, - 0x74, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x09, 0x66, 0x72, 0x61, 0x67, 0x6d, - 0x65, 0x6e, 0x74, 0x73, 0x22, 0x24, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x48, 0x61, 0x73, 0x68, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x22, 0x39, 0x0a, 0x0f, 0x47, 0x65, - 0x74, 0x48, 0x61, 0x73, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x12, 0x12, 0x0a, - 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, - 0x68, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x89, 0x01, 0x0a, 0x11, 0x46, 0x63, 0x6d, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x12, 0x1e, 0x0a, 0x0a, 0x70, - 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x0a, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x66, - 0x63, 0x6d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, - 0x63, 0x6d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x54, 0x69, 0x6d, - 0x65, 0x22, 0x14, 0x0a, 0x12, 0x46, 0x63, 0x6d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x22, 0x50, 0x0a, 0x0e, 0x53, 0x65, 0x74, 0x41, 0x70, - 0x70, 0x42, 0x61, 0x64, 0x67, 0x65, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, - 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, - 0x44, 0x12, 0x26, 0x0a, 0x0e, 0x61, 0x70, 0x70, 0x55, 0x6e, 0x72, 0x65, 0x61, 0x64, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x61, 0x70, 0x70, 0x55, 0x6e, - 0x72, 0x65, 0x61, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x11, 0x0a, 0x0f, 0x53, 0x65, 0x74, - 0x41, 0x70, 0x70, 0x42, 0x61, 0x64, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x32, 0xce, 0x04, 0x0a, - 0x05, 0x74, 0x68, 0x69, 0x72, 0x64, 0x12, 0x4d, 0x0a, 0x08, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, - 0x75, 0x74, 0x12, 0x1f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x75, 0x74, - 0x52, 0x65, 0x71, 0x1a, 0x20, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x75, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x47, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x50, 0x75, 0x74, 0x12, - 0x1d, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, - 0x68, 0x69, 0x72, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x75, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x1e, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x63, 0x61, 0x75, 0x73, 0x65, 0x22, 0x2f, 0x0a, 0x1b, 0x43, 0x6f, 0x6d, 0x70, + 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x61, 0x72, 0x74, 0x55, 0x70, 0x6c, + 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x22, 0x0a, 0x0c, 0x41, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x41, 0x0a, + 0x0d, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10, + 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, + 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x54, 0x69, 0x6d, 0x65, + 0x22, 0x89, 0x01, 0x0a, 0x11, 0x46, 0x63, 0x6d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, + 0x72, 0x6d, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x74, + 0x66, 0x6f, 0x72, 0x6d, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x63, 0x6d, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x63, 0x6d, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, + 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x14, 0x0a, 0x12, + 0x46, 0x63, 0x6d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x22, 0x50, 0x0a, 0x0e, 0x53, 0x65, 0x74, 0x41, 0x70, 0x70, 0x42, 0x61, 0x64, 0x67, + 0x65, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x26, 0x0a, 0x0e, + 0x61, 0x70, 0x70, 0x55, 0x6e, 0x72, 0x65, 0x61, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x61, 0x70, 0x70, 0x55, 0x6e, 0x72, 0x65, 0x61, 0x64, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x11, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x41, 0x70, 0x70, 0x42, 0x61, + 0x64, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x32, 0xfa, 0x05, 0x0a, 0x05, 0x74, 0x68, 0x69, 0x72, + 0x64, 0x12, 0x50, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x20, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, - 0x69, 0x72, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x53, - 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x50, 0x75, 0x74, 0x12, 0x21, 0x2e, 0x4f, - 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, - 0x64, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x50, 0x75, 0x74, 0x52, 0x65, 0x71, 0x1a, - 0x22, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, - 0x68, 0x69, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x50, 0x75, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x12, 0x47, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x55, 0x72, 0x6c, 0x12, 0x1d, 0x2e, + 0x69, 0x72, 0x64, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, + 0x1a, 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x12, 0x4d, 0x0a, 0x08, 0x50, 0x61, 0x72, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x12, + 0x1f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, + 0x68, 0x69, 0x72, 0x64, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x71, + 0x1a, 0x20, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x12, 0x7a, 0x0a, 0x17, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x65, 0x4d, 0x75, + 0x6c, 0x74, 0x69, 0x70, 0x61, 0x72, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x2e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, - 0x72, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x72, 0x6c, 0x52, 0x65, 0x71, 0x1a, 0x1e, 0x2e, 0x4f, - 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, - 0x64, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x72, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x12, 0x56, 0x0a, 0x0b, - 0x47, 0x65, 0x74, 0x48, 0x61, 0x73, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, 0x4f, 0x70, + 0x72, 0x64, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x65, 0x4d, 0x75, 0x6c, 0x74, 0x69, + 0x70, 0x61, 0x72, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x2f, 0x2e, + 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, + 0x72, 0x64, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x65, 0x4d, 0x75, 0x6c, 0x74, 0x69, + 0x70, 0x61, 0x72, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12, 0x4d, + 0x0a, 0x08, 0x41, 0x75, 0x74, 0x68, 0x53, 0x69, 0x67, 0x6e, 0x12, 0x1f, 0x2e, 0x4f, 0x70, 0x65, + 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, + 0x41, 0x75, 0x74, 0x68, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x20, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, - 0x2e, 0x47, 0x65, 0x74, 0x48, 0x61, 0x73, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x1a, - 0x23, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, - 0x68, 0x69, 0x72, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x61, 0x73, 0x68, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x73, 0x70, 0x12, 0x5f, 0x0a, 0x0e, 0x46, 0x63, 0x6d, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x25, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x46, 0x63, 0x6d, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x26, 0x2e, + 0x2e, 0x41, 0x75, 0x74, 0x68, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x7a, 0x0a, + 0x17, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x61, + 0x72, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x2e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, + 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x43, 0x6f, + 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x61, 0x72, 0x74, 0x55, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x2f, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, + 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x43, 0x6f, + 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x61, 0x72, 0x74, 0x55, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12, 0x50, 0x0a, 0x09, 0x41, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x55, 0x52, 0x4c, 0x12, 0x20, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x41, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x1a, 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, + 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x41, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x12, 0x5f, 0x0a, 0x0e, 0x46, + 0x63, 0x6d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x25, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x46, 0x63, 0x6d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x56, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x41, 0x70, 0x70, 0x42, - 0x61, 0x64, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x70, 0x70, - 0x42, 0x61, 0x64, 0x67, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x23, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, - 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x53, 0x65, - 0x74, 0x41, 0x70, 0x70, 0x42, 0x61, 0x64, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x42, 0x35, 0x5a, - 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x70, 0x65, 0x6e, - 0x49, 0x4d, 0x53, 0x44, 0x4b, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x2d, 0x49, 0x4d, 0x2d, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, - 0x68, 0x69, 0x72, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x26, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, 0x2e, 0x46, 0x63, 0x6d, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x56, 0x0a, 0x0b, + 0x53, 0x65, 0x74, 0x41, 0x70, 0x70, 0x42, 0x61, 0x64, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x4f, 0x70, + 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, 0x68, 0x69, 0x72, 0x64, + 0x2e, 0x53, 0x65, 0x74, 0x41, 0x70, 0x70, 0x42, 0x61, 0x64, 0x67, 0x65, 0x52, 0x65, 0x71, 0x1a, + 0x23, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x74, + 0x68, 0x69, 0x72, 0x64, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x70, 0x70, 0x42, 0x61, 0x64, 0x67, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x44, 0x4b, 0x2f, 0x4f, 0x70, 0x65, + 0x6e, 0x2d, 0x49, 0x4d, 0x2d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x68, 0x69, 0x72, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -1095,45 +1387,61 @@ func file_third_third_proto_rawDescGZIP() []byte { return file_third_third_proto_rawDescData } -var file_third_third_proto_msgTypes = make([]protoimpl.MessageInfo, 15) +var file_third_third_proto_msgTypes = make([]protoimpl.MessageInfo, 20) var file_third_third_proto_goTypes = []interface{}{ - (*ApplyPutReq)(nil), // 0: OpenIMServer.third.ApplyPutReq - (*ApplyPutResp)(nil), // 1: OpenIMServer.third.ApplyPutResp - (*ConfirmPutReq)(nil), // 2: OpenIMServer.third.ConfirmPutReq - (*ConfirmPutResp)(nil), // 3: OpenIMServer.third.ConfirmPutResp - (*GetUrlReq)(nil), // 4: OpenIMServer.third.GetUrlReq - (*GetUrlResp)(nil), // 5: OpenIMServer.third.GetUrlResp - (*GetPutReq)(nil), // 6: OpenIMServer.third.GetPutReq - (*GetPutFragment)(nil), // 7: OpenIMServer.third.GetPutFragment - (*GetPutResp)(nil), // 8: OpenIMServer.third.GetPutResp - (*GetHashInfoReq)(nil), // 9: OpenIMServer.third.GetHashInfoReq - (*GetHashInfoResp)(nil), // 10: OpenIMServer.third.GetHashInfoResp - (*FcmUpdateTokenReq)(nil), // 11: OpenIMServer.third.FcmUpdateTokenReq - (*FcmUpdateTokenResp)(nil), // 12: OpenIMServer.third.FcmUpdateTokenResp - (*SetAppBadgeReq)(nil), // 13: OpenIMServer.third.SetAppBadgeReq - (*SetAppBadgeResp)(nil), // 14: OpenIMServer.third.SetAppBadgeResp + (*KeyValues)(nil), // 0: OpenIMServer.third.KeyValues + (*SignPart)(nil), // 1: OpenIMServer.third.SignPart + (*AuthSignParts)(nil), // 2: OpenIMServer.third.AuthSignParts + (*PartLimitReq)(nil), // 3: OpenIMServer.third.PartLimitReq + (*PartLimitResp)(nil), // 4: OpenIMServer.third.PartLimitResp + (*PartSizeReq)(nil), // 5: OpenIMServer.third.PartSizeReq + (*PartSizeResp)(nil), // 6: OpenIMServer.third.PartSizeResp + (*InitiateMultipartUploadReq)(nil), // 7: OpenIMServer.third.InitiateMultipartUploadReq + (*UploadInfo)(nil), // 8: OpenIMServer.third.UploadInfo + (*InitiateMultipartUploadResp)(nil), // 9: OpenIMServer.third.InitiateMultipartUploadResp + (*AuthSignReq)(nil), // 10: OpenIMServer.third.AuthSignReq + (*AuthSignResp)(nil), // 11: OpenIMServer.third.AuthSignResp + (*CompleteMultipartUploadReq)(nil), // 12: OpenIMServer.third.CompleteMultipartUploadReq + (*CompleteMultipartUploadResp)(nil), // 13: OpenIMServer.third.CompleteMultipartUploadResp + (*AccessURLReq)(nil), // 14: OpenIMServer.third.AccessURLReq + (*AccessURLResp)(nil), // 15: OpenIMServer.third.AccessURLResp + (*FcmUpdateTokenReq)(nil), // 16: OpenIMServer.third.FcmUpdateTokenReq + (*FcmUpdateTokenResp)(nil), // 17: OpenIMServer.third.FcmUpdateTokenResp + (*SetAppBadgeReq)(nil), // 18: OpenIMServer.third.SetAppBadgeReq + (*SetAppBadgeResp)(nil), // 19: OpenIMServer.third.SetAppBadgeResp } var file_third_third_proto_depIdxs = []int32{ - 7, // 0: OpenIMServer.third.GetPutResp.fragments:type_name -> OpenIMServer.third.GetPutFragment - 0, // 1: OpenIMServer.third.third.ApplyPut:input_type -> OpenIMServer.third.ApplyPutReq - 6, // 2: OpenIMServer.third.third.GetPut:input_type -> OpenIMServer.third.GetPutReq - 2, // 3: OpenIMServer.third.third.ConfirmPut:input_type -> OpenIMServer.third.ConfirmPutReq - 4, // 4: OpenIMServer.third.third.GetUrl:input_type -> OpenIMServer.third.GetUrlReq - 9, // 5: OpenIMServer.third.third.GetHashInfo:input_type -> OpenIMServer.third.GetHashInfoReq - 11, // 6: OpenIMServer.third.third.FcmUpdateToken:input_type -> OpenIMServer.third.FcmUpdateTokenReq - 13, // 7: OpenIMServer.third.third.SetAppBadge:input_type -> OpenIMServer.third.SetAppBadgeReq - 1, // 8: OpenIMServer.third.third.ApplyPut:output_type -> OpenIMServer.third.ApplyPutResp - 8, // 9: OpenIMServer.third.third.GetPut:output_type -> OpenIMServer.third.GetPutResp - 3, // 10: OpenIMServer.third.third.ConfirmPut:output_type -> OpenIMServer.third.ConfirmPutResp - 5, // 11: OpenIMServer.third.third.GetUrl:output_type -> OpenIMServer.third.GetUrlResp - 10, // 12: OpenIMServer.third.third.GetHashInfo:output_type -> OpenIMServer.third.GetHashInfoResp - 12, // 13: OpenIMServer.third.third.FcmUpdateToken:output_type -> OpenIMServer.third.FcmUpdateTokenResp - 14, // 14: OpenIMServer.third.third.SetAppBadge:output_type -> OpenIMServer.third.SetAppBadgeResp - 8, // [8:15] is the sub-list for method output_type - 1, // [1:8] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 0, // 0: OpenIMServer.third.SignPart.query:type_name -> OpenIMServer.third.KeyValues + 0, // 1: OpenIMServer.third.SignPart.header:type_name -> OpenIMServer.third.KeyValues + 0, // 2: OpenIMServer.third.AuthSignParts.query:type_name -> OpenIMServer.third.KeyValues + 0, // 3: OpenIMServer.third.AuthSignParts.header:type_name -> OpenIMServer.third.KeyValues + 1, // 4: OpenIMServer.third.AuthSignParts.parts:type_name -> OpenIMServer.third.SignPart + 2, // 5: OpenIMServer.third.UploadInfo.sign:type_name -> OpenIMServer.third.AuthSignParts + 8, // 6: OpenIMServer.third.InitiateMultipartUploadResp.upload:type_name -> OpenIMServer.third.UploadInfo + 0, // 7: OpenIMServer.third.AuthSignResp.query:type_name -> OpenIMServer.third.KeyValues + 0, // 8: OpenIMServer.third.AuthSignResp.header:type_name -> OpenIMServer.third.KeyValues + 1, // 9: OpenIMServer.third.AuthSignResp.parts:type_name -> OpenIMServer.third.SignPart + 3, // 10: OpenIMServer.third.third.PartLimit:input_type -> OpenIMServer.third.PartLimitReq + 5, // 11: OpenIMServer.third.third.PartSize:input_type -> OpenIMServer.third.PartSizeReq + 7, // 12: OpenIMServer.third.third.InitiateMultipartUpload:input_type -> OpenIMServer.third.InitiateMultipartUploadReq + 10, // 13: OpenIMServer.third.third.AuthSign:input_type -> OpenIMServer.third.AuthSignReq + 12, // 14: OpenIMServer.third.third.CompleteMultipartUpload:input_type -> OpenIMServer.third.CompleteMultipartUploadReq + 14, // 15: OpenIMServer.third.third.AccessURL:input_type -> OpenIMServer.third.AccessURLReq + 16, // 16: OpenIMServer.third.third.FcmUpdateToken:input_type -> OpenIMServer.third.FcmUpdateTokenReq + 18, // 17: OpenIMServer.third.third.SetAppBadge:input_type -> OpenIMServer.third.SetAppBadgeReq + 4, // 18: OpenIMServer.third.third.PartLimit:output_type -> OpenIMServer.third.PartLimitResp + 6, // 19: OpenIMServer.third.third.PartSize:output_type -> OpenIMServer.third.PartSizeResp + 9, // 20: OpenIMServer.third.third.InitiateMultipartUpload:output_type -> OpenIMServer.third.InitiateMultipartUploadResp + 11, // 21: OpenIMServer.third.third.AuthSign:output_type -> OpenIMServer.third.AuthSignResp + 13, // 22: OpenIMServer.third.third.CompleteMultipartUpload:output_type -> OpenIMServer.third.CompleteMultipartUploadResp + 15, // 23: OpenIMServer.third.third.AccessURL:output_type -> OpenIMServer.third.AccessURLResp + 17, // 24: OpenIMServer.third.third.FcmUpdateToken:output_type -> OpenIMServer.third.FcmUpdateTokenResp + 19, // 25: OpenIMServer.third.third.SetAppBadge:output_type -> OpenIMServer.third.SetAppBadgeResp + 18, // [18:26] is the sub-list for method output_type + 10, // [10:18] is the sub-list for method input_type + 10, // [10:10] is the sub-list for extension type_name + 10, // [10:10] is the sub-list for extension extendee + 0, // [0:10] is the sub-list for field type_name } func init() { file_third_third_proto_init() } @@ -1143,7 +1451,7 @@ func file_third_third_proto_init() { } if !protoimpl.UnsafeEnabled { file_third_third_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ApplyPutReq); i { + switch v := v.(*KeyValues); i { case 0: return &v.state case 1: @@ -1155,7 +1463,7 @@ func file_third_third_proto_init() { } } file_third_third_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ApplyPutResp); i { + switch v := v.(*SignPart); i { case 0: return &v.state case 1: @@ -1167,7 +1475,7 @@ func file_third_third_proto_init() { } } file_third_third_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ConfirmPutReq); i { + switch v := v.(*AuthSignParts); i { case 0: return &v.state case 1: @@ -1179,7 +1487,7 @@ func file_third_third_proto_init() { } } file_third_third_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ConfirmPutResp); i { + switch v := v.(*PartLimitReq); i { case 0: return &v.state case 1: @@ -1191,7 +1499,7 @@ func file_third_third_proto_init() { } } file_third_third_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetUrlReq); i { + switch v := v.(*PartLimitResp); i { case 0: return &v.state case 1: @@ -1203,7 +1511,7 @@ func file_third_third_proto_init() { } } file_third_third_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetUrlResp); i { + switch v := v.(*PartSizeReq); i { case 0: return &v.state case 1: @@ -1215,7 +1523,7 @@ func file_third_third_proto_init() { } } file_third_third_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetPutReq); i { + switch v := v.(*PartSizeResp); i { case 0: return &v.state case 1: @@ -1227,7 +1535,7 @@ func file_third_third_proto_init() { } } file_third_third_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetPutFragment); i { + switch v := v.(*InitiateMultipartUploadReq); i { case 0: return &v.state case 1: @@ -1239,7 +1547,7 @@ func file_third_third_proto_init() { } } file_third_third_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetPutResp); i { + switch v := v.(*UploadInfo); i { case 0: return &v.state case 1: @@ -1251,7 +1559,7 @@ func file_third_third_proto_init() { } } file_third_third_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetHashInfoReq); i { + switch v := v.(*InitiateMultipartUploadResp); i { case 0: return &v.state case 1: @@ -1263,7 +1571,7 @@ func file_third_third_proto_init() { } } file_third_third_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetHashInfoResp); i { + switch v := v.(*AuthSignReq); i { case 0: return &v.state case 1: @@ -1275,7 +1583,7 @@ func file_third_third_proto_init() { } } file_third_third_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FcmUpdateTokenReq); i { + switch v := v.(*AuthSignResp); i { case 0: return &v.state case 1: @@ -1287,7 +1595,7 @@ func file_third_third_proto_init() { } } file_third_third_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FcmUpdateTokenResp); i { + switch v := v.(*CompleteMultipartUploadReq); i { case 0: return &v.state case 1: @@ -1299,7 +1607,7 @@ func file_third_third_proto_init() { } } file_third_third_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetAppBadgeReq); i { + switch v := v.(*CompleteMultipartUploadResp); i { case 0: return &v.state case 1: @@ -1311,6 +1619,66 @@ func file_third_third_proto_init() { } } file_third_third_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AccessURLReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_third_third_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AccessURLResp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_third_third_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FcmUpdateTokenReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_third_third_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FcmUpdateTokenResp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_third_third_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetAppBadgeReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_third_third_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SetAppBadgeResp); i { case 0: return &v.state @@ -1329,7 +1697,7 @@ func file_third_third_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_third_third_proto_rawDesc, NumEnums: 0, - NumMessages: 15, + NumMessages: 20, NumExtensions: 0, NumServices: 1, }, @@ -1355,11 +1723,12 @@ const _ = grpc.SupportPackageIsVersion6 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type ThirdClient interface { - ApplyPut(ctx context.Context, in *ApplyPutReq, opts ...grpc.CallOption) (*ApplyPutResp, error) - GetPut(ctx context.Context, in *GetPutReq, opts ...grpc.CallOption) (*GetPutResp, error) - ConfirmPut(ctx context.Context, in *ConfirmPutReq, opts ...grpc.CallOption) (*ConfirmPutResp, error) - GetUrl(ctx context.Context, in *GetUrlReq, opts ...grpc.CallOption) (*GetUrlResp, error) - GetHashInfo(ctx context.Context, in *GetHashInfoReq, opts ...grpc.CallOption) (*GetHashInfoResp, error) + PartLimit(ctx context.Context, in *PartLimitReq, opts ...grpc.CallOption) (*PartLimitResp, error) + PartSize(ctx context.Context, in *PartSizeReq, opts ...grpc.CallOption) (*PartSizeResp, error) + InitiateMultipartUpload(ctx context.Context, in *InitiateMultipartUploadReq, opts ...grpc.CallOption) (*InitiateMultipartUploadResp, error) + AuthSign(ctx context.Context, in *AuthSignReq, opts ...grpc.CallOption) (*AuthSignResp, error) + CompleteMultipartUpload(ctx context.Context, in *CompleteMultipartUploadReq, opts ...grpc.CallOption) (*CompleteMultipartUploadResp, error) + AccessURL(ctx context.Context, in *AccessURLReq, opts ...grpc.CallOption) (*AccessURLResp, error) FcmUpdateToken(ctx context.Context, in *FcmUpdateTokenReq, opts ...grpc.CallOption) (*FcmUpdateTokenResp, error) SetAppBadge(ctx context.Context, in *SetAppBadgeReq, opts ...grpc.CallOption) (*SetAppBadgeResp, error) } @@ -1372,45 +1741,54 @@ func NewThirdClient(cc grpc.ClientConnInterface) ThirdClient { return &thirdClient{cc} } -func (c *thirdClient) ApplyPut(ctx context.Context, in *ApplyPutReq, opts ...grpc.CallOption) (*ApplyPutResp, error) { - out := new(ApplyPutResp) - err := c.cc.Invoke(ctx, "/OpenIMServer.third.third/ApplyPut", in, out, opts...) +func (c *thirdClient) PartLimit(ctx context.Context, in *PartLimitReq, opts ...grpc.CallOption) (*PartLimitResp, error) { + out := new(PartLimitResp) + err := c.cc.Invoke(ctx, "/OpenIMServer.third.third/PartLimit", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *thirdClient) GetPut(ctx context.Context, in *GetPutReq, opts ...grpc.CallOption) (*GetPutResp, error) { - out := new(GetPutResp) - err := c.cc.Invoke(ctx, "/OpenIMServer.third.third/GetPut", in, out, opts...) +func (c *thirdClient) PartSize(ctx context.Context, in *PartSizeReq, opts ...grpc.CallOption) (*PartSizeResp, error) { + out := new(PartSizeResp) + err := c.cc.Invoke(ctx, "/OpenIMServer.third.third/PartSize", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *thirdClient) ConfirmPut(ctx context.Context, in *ConfirmPutReq, opts ...grpc.CallOption) (*ConfirmPutResp, error) { - out := new(ConfirmPutResp) - err := c.cc.Invoke(ctx, "/OpenIMServer.third.third/ConfirmPut", in, out, opts...) +func (c *thirdClient) InitiateMultipartUpload(ctx context.Context, in *InitiateMultipartUploadReq, opts ...grpc.CallOption) (*InitiateMultipartUploadResp, error) { + out := new(InitiateMultipartUploadResp) + err := c.cc.Invoke(ctx, "/OpenIMServer.third.third/InitiateMultipartUpload", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *thirdClient) GetUrl(ctx context.Context, in *GetUrlReq, opts ...grpc.CallOption) (*GetUrlResp, error) { - out := new(GetUrlResp) - err := c.cc.Invoke(ctx, "/OpenIMServer.third.third/GetUrl", in, out, opts...) +func (c *thirdClient) AuthSign(ctx context.Context, in *AuthSignReq, opts ...grpc.CallOption) (*AuthSignResp, error) { + out := new(AuthSignResp) + err := c.cc.Invoke(ctx, "/OpenIMServer.third.third/AuthSign", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *thirdClient) GetHashInfo(ctx context.Context, in *GetHashInfoReq, opts ...grpc.CallOption) (*GetHashInfoResp, error) { - out := new(GetHashInfoResp) - err := c.cc.Invoke(ctx, "/OpenIMServer.third.third/GetHashInfo", in, out, opts...) +func (c *thirdClient) CompleteMultipartUpload(ctx context.Context, in *CompleteMultipartUploadReq, opts ...grpc.CallOption) (*CompleteMultipartUploadResp, error) { + out := new(CompleteMultipartUploadResp) + err := c.cc.Invoke(ctx, "/OpenIMServer.third.third/CompleteMultipartUpload", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *thirdClient) AccessURL(ctx context.Context, in *AccessURLReq, opts ...grpc.CallOption) (*AccessURLResp, error) { + out := new(AccessURLResp) + err := c.cc.Invoke(ctx, "/OpenIMServer.third.third/AccessURL", in, out, opts...) if err != nil { return nil, err } @@ -1437,11 +1815,12 @@ func (c *thirdClient) SetAppBadge(ctx context.Context, in *SetAppBadgeReq, opts // ThirdServer is the server API for Third service. type ThirdServer interface { - ApplyPut(context.Context, *ApplyPutReq) (*ApplyPutResp, error) - GetPut(context.Context, *GetPutReq) (*GetPutResp, error) - ConfirmPut(context.Context, *ConfirmPutReq) (*ConfirmPutResp, error) - GetUrl(context.Context, *GetUrlReq) (*GetUrlResp, error) - GetHashInfo(context.Context, *GetHashInfoReq) (*GetHashInfoResp, error) + PartLimit(context.Context, *PartLimitReq) (*PartLimitResp, error) + PartSize(context.Context, *PartSizeReq) (*PartSizeResp, error) + InitiateMultipartUpload(context.Context, *InitiateMultipartUploadReq) (*InitiateMultipartUploadResp, error) + AuthSign(context.Context, *AuthSignReq) (*AuthSignResp, error) + CompleteMultipartUpload(context.Context, *CompleteMultipartUploadReq) (*CompleteMultipartUploadResp, error) + AccessURL(context.Context, *AccessURLReq) (*AccessURLResp, error) FcmUpdateToken(context.Context, *FcmUpdateTokenReq) (*FcmUpdateTokenResp, error) SetAppBadge(context.Context, *SetAppBadgeReq) (*SetAppBadgeResp, error) } @@ -1450,20 +1829,23 @@ type ThirdServer interface { type UnimplementedThirdServer struct { } -func (*UnimplementedThirdServer) ApplyPut(context.Context, *ApplyPutReq) (*ApplyPutResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method ApplyPut not implemented") +func (*UnimplementedThirdServer) PartLimit(context.Context, *PartLimitReq) (*PartLimitResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method PartLimit not implemented") } -func (*UnimplementedThirdServer) GetPut(context.Context, *GetPutReq) (*GetPutResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetPut not implemented") +func (*UnimplementedThirdServer) PartSize(context.Context, *PartSizeReq) (*PartSizeResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method PartSize not implemented") } -func (*UnimplementedThirdServer) ConfirmPut(context.Context, *ConfirmPutReq) (*ConfirmPutResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method ConfirmPut not implemented") +func (*UnimplementedThirdServer) InitiateMultipartUpload(context.Context, *InitiateMultipartUploadReq) (*InitiateMultipartUploadResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method InitiateMultipartUpload not implemented") } -func (*UnimplementedThirdServer) GetUrl(context.Context, *GetUrlReq) (*GetUrlResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetUrl not implemented") +func (*UnimplementedThirdServer) AuthSign(context.Context, *AuthSignReq) (*AuthSignResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method AuthSign not implemented") } -func (*UnimplementedThirdServer) GetHashInfo(context.Context, *GetHashInfoReq) (*GetHashInfoResp, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetHashInfo not implemented") +func (*UnimplementedThirdServer) CompleteMultipartUpload(context.Context, *CompleteMultipartUploadReq) (*CompleteMultipartUploadResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method CompleteMultipartUpload not implemented") +} +func (*UnimplementedThirdServer) AccessURL(context.Context, *AccessURLReq) (*AccessURLResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method AccessURL not implemented") } func (*UnimplementedThirdServer) FcmUpdateToken(context.Context, *FcmUpdateTokenReq) (*FcmUpdateTokenResp, error) { return nil, status.Errorf(codes.Unimplemented, "method FcmUpdateToken not implemented") @@ -1476,92 +1858,110 @@ func RegisterThirdServer(s *grpc.Server, srv ThirdServer) { s.RegisterService(&_Third_serviceDesc, srv) } -func _Third_ApplyPut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ApplyPutReq) +func _Third_PartLimit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PartLimitReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ThirdServer).PartLimit(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/OpenIMServer.third.third/PartLimit", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ThirdServer).PartLimit(ctx, req.(*PartLimitReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Third_PartSize_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PartSizeReq) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(ThirdServer).ApplyPut(ctx, in) + return srv.(ThirdServer).PartSize(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/OpenIMServer.third.third/ApplyPut", + FullMethod: "/OpenIMServer.third.third/PartSize", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ThirdServer).ApplyPut(ctx, req.(*ApplyPutReq)) + return srv.(ThirdServer).PartSize(ctx, req.(*PartSizeReq)) } return interceptor(ctx, in, info, handler) } -func _Third_GetPut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetPutReq) +func _Third_InitiateMultipartUpload_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InitiateMultipartUploadReq) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(ThirdServer).GetPut(ctx, in) + return srv.(ThirdServer).InitiateMultipartUpload(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/OpenIMServer.third.third/GetPut", + FullMethod: "/OpenIMServer.third.third/InitiateMultipartUpload", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ThirdServer).GetPut(ctx, req.(*GetPutReq)) + return srv.(ThirdServer).InitiateMultipartUpload(ctx, req.(*InitiateMultipartUploadReq)) } return interceptor(ctx, in, info, handler) } -func _Third_ConfirmPut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ConfirmPutReq) +func _Third_AuthSign_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AuthSignReq) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(ThirdServer).ConfirmPut(ctx, in) + return srv.(ThirdServer).AuthSign(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/OpenIMServer.third.third/ConfirmPut", + FullMethod: "/OpenIMServer.third.third/AuthSign", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ThirdServer).ConfirmPut(ctx, req.(*ConfirmPutReq)) + return srv.(ThirdServer).AuthSign(ctx, req.(*AuthSignReq)) } return interceptor(ctx, in, info, handler) } -func _Third_GetUrl_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetUrlReq) +func _Third_CompleteMultipartUpload_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CompleteMultipartUploadReq) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(ThirdServer).GetUrl(ctx, in) + return srv.(ThirdServer).CompleteMultipartUpload(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/OpenIMServer.third.third/GetUrl", + FullMethod: "/OpenIMServer.third.third/CompleteMultipartUpload", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ThirdServer).GetUrl(ctx, req.(*GetUrlReq)) + return srv.(ThirdServer).CompleteMultipartUpload(ctx, req.(*CompleteMultipartUploadReq)) } return interceptor(ctx, in, info, handler) } -func _Third_GetHashInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetHashInfoReq) +func _Third_AccessURL_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AccessURLReq) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(ThirdServer).GetHashInfo(ctx, in) + return srv.(ThirdServer).AccessURL(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/OpenIMServer.third.third/GetHashInfo", + FullMethod: "/OpenIMServer.third.third/AccessURL", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ThirdServer).GetHashInfo(ctx, req.(*GetHashInfoReq)) + return srv.(ThirdServer).AccessURL(ctx, req.(*AccessURLReq)) } return interceptor(ctx, in, info, handler) } @@ -1607,24 +2007,28 @@ var _Third_serviceDesc = grpc.ServiceDesc{ HandlerType: (*ThirdServer)(nil), Methods: []grpc.MethodDesc{ { - MethodName: "ApplyPut", - Handler: _Third_ApplyPut_Handler, + MethodName: "PartLimit", + Handler: _Third_PartLimit_Handler, + }, + { + MethodName: "PartSize", + Handler: _Third_PartSize_Handler, }, { - MethodName: "GetPut", - Handler: _Third_GetPut_Handler, + MethodName: "InitiateMultipartUpload", + Handler: _Third_InitiateMultipartUpload_Handler, }, { - MethodName: "ConfirmPut", - Handler: _Third_ConfirmPut_Handler, + MethodName: "AuthSign", + Handler: _Third_AuthSign_Handler, }, { - MethodName: "GetUrl", - Handler: _Third_GetUrl_Handler, + MethodName: "CompleteMultipartUpload", + Handler: _Third_CompleteMultipartUpload_Handler, }, { - MethodName: "GetHashInfo", - Handler: _Third_GetHashInfo_Handler, + MethodName: "AccessURL", + Handler: _Third_AccessURL_Handler, }, { MethodName: "FcmUpdateToken", diff --git a/pkg/proto/third/third.proto b/pkg/proto/third/third.proto index 8cdad7562..3b2456983 100644 --- a/pkg/proto/third/third.proto +++ b/pkg/proto/third/third.proto @@ -16,80 +16,95 @@ syntax = "proto3"; package OpenIMServer.third; option go_package = "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/third"; -message ApplyPutReq { - string putID = 1; - string name = 2; - int64 size = 3; - string hash = 4; - string contentType = 5; - int64 fragmentSize = 6; - int64 validTime = 7; // 文件有效时间 +message KeyValues { + string key = 1; + repeated string values = 2; } -message ApplyPutResp { +message SignPart { + int32 partNumber = 1; + string url = 2; + repeated KeyValues query = 3; + repeated KeyValues header = 4; +} + +message AuthSignParts { string url = 1; - string putID = 2; - int64 fragmentSize = 3; - int64 validTime = 4;// 上传地址的有效时间 - string putURLsHash = 5; - repeated string putURLs = 6; + repeated KeyValues query = 2; + repeated KeyValues header = 3; + repeated SignPart parts = 4; +} + +message PartLimitReq { +} + +message PartLimitResp { + int64 minPartSize = 1; + int64 maxPartSize = 2; + int32 maxNumSize = 3; +} + +message PartSizeReq { + int64 size = 1; +} + +message PartSizeResp { + int64 size = 2; +} + +message InitiateMultipartUploadReq { + string hash = 1; + int64 size = 2; + int64 partSize = 3; + int32 maxParts = 4; + string cause = 5; + string name = 6; + string contentType = 7; } -message ConfirmPutReq { - string putID = 1; +message UploadInfo { + string uploadID = 1; + int64 partSize = 2; + AuthSignParts sign = 3; + int64 expireTime = 4; } -message ConfirmPutResp { +message InitiateMultipartUploadResp { string url = 1; + UploadInfo upload = 2; } -message GetUrlReq { - string name = 1; // 文件名 - int64 expires = 2; // url有效时间 - bool attachment = 3;// 是否是附件 +message AuthSignReq { + string uploadID = 1; + repeated int32 partNumbers = 2; } -message GetUrlResp { +message AuthSignResp { string url = 1; - int64 size = 2; - string hash = 3; + repeated KeyValues query = 2; + repeated KeyValues header = 3; + repeated SignPart parts = 4; } -message GetPutReq { - string putID = 1; +message CompleteMultipartUploadReq { + string uploadID = 1; + repeated string parts = 2; + string name = 3; + string contentType = 4; + string cause = 5; } -message GetPutFragment{ - int64 size = 1; - string hash = 2; - string url = 3; +message CompleteMultipartUploadResp { + string url = 1; } -message GetPutResp { +message AccessURLReq { string name = 1; - int64 size = 2; - string hash = 3; - int64 fragmentSize = 4; - string contentType = 5; - int64 validTime = 6; // 上传地址的有效时间 - // repeated GetPutFragment fragments = 7; - // string putURLsHash = 8; - // string putID = 2; - // int64 fragmentSize = 3; - // int64 validTime = 4;// 上传地址的有效时间 - string putURLsHash = 7; - repeated GetPutFragment fragments = 8; - // repeated string putURLs = 6; - // repeated GetPutFragment fragments = 7; -} - -message GetHashInfoReq { - string hash = 1; } -message GetHashInfoResp { - string hash = 1; - int64 size = 2; +message AccessURLResp { + string url = 1; + int64 expireTime = 2; } message FcmUpdateTokenReq { @@ -111,11 +126,13 @@ message SetAppBadgeResp { } service third { - rpc ApplyPut(ApplyPutReq) returns(ApplyPutResp); - rpc GetPut(GetPutReq) returns(GetPutResp); - rpc ConfirmPut(ConfirmPutReq) returns(ConfirmPutResp); - rpc GetUrl(GetUrlReq) returns(GetUrlResp); - rpc GetHashInfo(GetHashInfoReq) returns(GetHashInfoResp); + rpc PartLimit(PartLimitReq) returns(PartLimitResp); + rpc PartSize(PartSizeReq) returns(PartSizeResp); + rpc InitiateMultipartUpload(InitiateMultipartUploadReq) returns(InitiateMultipartUploadResp); + rpc AuthSign(AuthSignReq) returns(AuthSignResp); + rpc CompleteMultipartUpload(CompleteMultipartUploadReq) returns(CompleteMultipartUploadResp); + rpc AccessURL(AccessURLReq) returns(AccessURLResp); + rpc FcmUpdateToken(FcmUpdateTokenReq) returns(FcmUpdateTokenResp); rpc SetAppBadge(SetAppBadgeReq) returns(SetAppBadgeResp); } diff --git a/pkg/proto/user/user.pb.go b/pkg/proto/user/user.pb.go index 86113ec05..c96acb4a4 100644 --- a/pkg/proto/user/user.pb.go +++ b/pkg/proto/user/user.pb.go @@ -1511,8 +1511,9 @@ type UserRegisterCountResp struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Total int64 `protobuf:"varint,1,opt,name=total,proto3" json:"total"` - Count map[string]int64 `protobuf:"bytes,2,rep,name=count,proto3" json:"count" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + Total int64 `protobuf:"varint,1,opt,name=total,proto3" json:"total"` + Before int64 `protobuf:"varint,2,opt,name=before,proto3" json:"before"` + Count map[string]int64 `protobuf:"bytes,3,rep,name=count,proto3" json:"count" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` } func (x *UserRegisterCountResp) Reset() { @@ -1554,6 +1555,13 @@ func (x *UserRegisterCountResp) GetTotal() int64 { return 0 } +func (x *UserRegisterCountResp) GetBefore() int64 { + if x != nil { + return x.Before + } + return 0 +} + func (x *UserRegisterCountResp) GetCount() map[string]int64 { if x != nil { return x.Count @@ -1790,80 +1798,82 @@ var file_user_user_proto_rawDesc = []byte{ 0x73, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0xb2, 0x01, 0x0a, 0x15, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0xca, 0x01, 0x0a, 0x15, 0x75, 0x73, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x49, 0x0a, 0x05, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x4f, 0x70, 0x65, - 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x75, - 0x73, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x38, 0x0a, 0x0a, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x32, 0x9f, 0x07, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x66, 0x0a, 0x11, 0x67, 0x65, 0x74, - 0x44, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x27, - 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, - 0x65, 0x72, 0x2e, 0x67, 0x65, 0x74, 0x44, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x65, 0x55, - 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x28, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x67, 0x65, 0x74, 0x44, - 0x65, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x12, 0x5d, 0x0a, 0x0e, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x49, - 0x6e, 0x66, 0x6f, 0x12, 0x24, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, - 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x1a, 0x25, 0x2e, 0x4f, 0x70, 0x65, 0x6e, - 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, - 0x12, 0x78, 0x0a, 0x17, 0x73, 0x65, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x65, 0x63, - 0x76, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x12, 0x2d, 0x2e, 0x4f, 0x70, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x62, + 0x65, 0x66, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x62, 0x65, 0x66, + 0x6f, 0x72, 0x65, 0x12, 0x49, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x65, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x2e, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x38, + 0x0a, 0x0a, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0x9f, 0x07, 0x0a, 0x04, 0x75, 0x73, 0x65, + 0x72, 0x12, 0x66, 0x0a, 0x11, 0x67, 0x65, 0x74, 0x44, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x27, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x67, 0x65, 0x74, 0x44, 0x65, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x1a, + 0x28, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, + 0x73, 0x65, 0x72, 0x2e, 0x67, 0x65, 0x74, 0x44, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x65, + 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x5d, 0x0a, 0x0e, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x24, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, - 0x73, 0x65, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x65, 0x63, 0x76, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x2e, 0x2e, 0x4f, 0x70, 0x65, - 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x73, - 0x65, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x65, 0x63, 0x76, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x78, 0x0a, 0x17, 0x67, 0x65, - 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x65, 0x63, 0x76, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x4f, 0x70, 0x74, 0x12, 0x2d, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x67, 0x65, 0x74, 0x47, 0x6c, 0x6f, - 0x62, 0x61, 0x6c, 0x52, 0x65, 0x63, 0x76, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, - 0x74, 0x52, 0x65, 0x71, 0x1a, 0x2e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x67, 0x65, 0x74, 0x47, 0x6c, 0x6f, 0x62, - 0x61, 0x6c, 0x52, 0x65, 0x63, 0x76, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x12, 0x57, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x12, 0x22, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x1a, 0x23, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, - 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x61, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x12, 0x69, 0x0a, - 0x12, 0x67, 0x65, 0x74, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x73, - 0x65, 0x72, 0x73, 0x12, 0x28, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x67, 0x65, 0x74, 0x50, 0x61, 0x67, 0x69, 0x6e, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x29, 0x2e, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x71, 0x1a, 0x25, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x12, 0x78, 0x0a, 0x17, 0x73, 0x65, 0x74, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x65, 0x63, 0x76, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x4f, 0x70, 0x74, 0x12, 0x2d, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x52, 0x65, 0x63, 0x76, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x52, + 0x65, 0x71, 0x1a, 0x2e, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, + 0x52, 0x65, 0x63, 0x76, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x12, 0x78, 0x0a, 0x17, 0x67, 0x65, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, + 0x65, 0x63, 0x76, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x12, 0x2d, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, - 0x72, 0x2e, 0x67, 0x65, 0x74, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, - 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x57, 0x0a, 0x0c, 0x75, 0x73, 0x65, 0x72, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x22, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, - 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, - 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x23, 0x2e, 0x4f, + 0x72, 0x2e, 0x67, 0x65, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x65, 0x63, 0x76, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x2e, 0x2e, 0x4f, + 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, + 0x2e, 0x67, 0x65, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x65, 0x63, 0x76, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x57, 0x0a, 0x0c, + 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x22, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, - 0x2e, 0x75, 0x73, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x12, 0x57, 0x0a, 0x0c, 0x67, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x49, - 0x44, 0x12, 0x22, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x67, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x55, 0x73, 0x65, 0x72, - 0x49, 0x44, 0x52, 0x65, 0x71, 0x1a, 0x23, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x67, 0x65, 0x74, 0x41, 0x6c, 0x6c, - 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x12, 0x66, 0x0a, 0x11, 0x75, 0x73, - 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, - 0x27, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, - 0x73, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x28, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, - 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, - 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x44, 0x4b, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x2d, - 0x49, 0x4d, 0x2d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, + 0x1a, 0x23, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x75, 0x73, 0x65, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x52, 0x65, 0x73, 0x70, 0x12, 0x69, 0x0a, 0x12, 0x67, 0x65, 0x74, 0x50, 0x61, 0x67, 0x69, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x28, 0x2e, 0x4f, 0x70, + 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, + 0x67, 0x65, 0x74, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x73, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x29, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x67, 0x65, 0x74, 0x50, 0x61, 0x67, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x12, 0x57, 0x0a, 0x0c, 0x75, 0x73, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x12, 0x22, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x75, 0x73, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x1a, 0x23, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x12, 0x57, 0x0a, 0x0c, 0x67, 0x65, 0x74, + 0x41, 0x6c, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x4f, 0x70, 0x65, 0x6e, + 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x67, 0x65, + 0x74, 0x41, 0x6c, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x52, 0x65, 0x71, 0x1a, 0x23, 0x2e, + 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, + 0x72, 0x2e, 0x67, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x52, 0x65, + 0x73, 0x70, 0x12, 0x66, 0x0a, 0x11, 0x75, 0x73, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x27, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, + 0x1a, 0x28, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x75, 0x73, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x4d, 0x53, + 0x44, 0x4b, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x2d, 0x49, 0x4d, 0x2d, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x75, 0x73, 0x65, 0x72, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pkg/proto/user/user.proto b/pkg/proto/user/user.proto index e63959aff..10035642c 100644 --- a/pkg/proto/user/user.proto +++ b/pkg/proto/user/user.proto @@ -54,7 +54,7 @@ message updateUserInfoResp{ message setGlobalRecvMessageOptReq{ string userID = 1; - int32 globalRecvMsgOpt = 3; + int32 globalRecvMsgOpt = 3; } message setGlobalRecvMessageOptResp{ } @@ -155,7 +155,8 @@ message userRegisterCountReq { message userRegisterCountResp { int64 total = 1; - map count = 2; + int64 before = 2; + map count = 3; } service user { diff --git a/pkg/rpcclient/conversation.go b/pkg/rpcclient/conversation.go index 5cab1d689..617446852 100644 --- a/pkg/rpcclient/conversation.go +++ b/pkg/rpcclient/conversation.go @@ -1,29 +1,14 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package rpcclient import ( "context" "fmt" - "google.golang.org/grpc" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" pbConversation "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/conversation" + "google.golang.org/grpc" ) type Conversation struct { @@ -47,18 +32,7 @@ func NewConversationRpcClient(discov discoveryregistry.SvcDiscoveryRegistry) Con return ConversationRpcClient(*NewConversation(discov)) } -func (c *ConversationRpcClient) ModifyConversationField( - ctx context.Context, - req *pbConversation.ModifyConversationFieldReq, -) error { - _, err := c.Client.ModifyConversationField(ctx, req) - return err -} - -func (c *ConversationRpcClient) GetSingleConversationRecvMsgOpt( - ctx context.Context, - userID, conversationID string, -) (int32, error) { +func (c *ConversationRpcClient) GetSingleConversationRecvMsgOpt(ctx context.Context, userID, conversationID string) (int32, error) { var req pbConversation.GetConversationReq req.OwnerUserID = userID req.ConversationID = conversationID @@ -70,51 +44,21 @@ func (c *ConversationRpcClient) GetSingleConversationRecvMsgOpt( } func (c *ConversationRpcClient) SingleChatFirstCreateConversation(ctx context.Context, recvID, sendID string) error { - _, err := c.Client.CreateSingleChatConversations( - ctx, - &pbConversation.CreateSingleChatConversationsReq{RecvID: recvID, SendID: sendID}, - ) + _, err := c.Client.CreateSingleChatConversations(ctx, &pbConversation.CreateSingleChatConversationsReq{RecvID: recvID, SendID: sendID}) return err } -func (c *ConversationRpcClient) GroupChatFirstCreateConversation( - ctx context.Context, - groupID string, - userIDs []string, -) error { - _, err := c.Client.CreateGroupChatConversations( - ctx, - &pbConversation.CreateGroupChatConversationsReq{UserIDs: userIDs, GroupID: groupID}, - ) +func (c *ConversationRpcClient) GroupChatFirstCreateConversation(ctx context.Context, groupID string, userIDs []string) error { + _, err := c.Client.CreateGroupChatConversations(ctx, &pbConversation.CreateGroupChatConversationsReq{UserIDs: userIDs, GroupID: groupID}) return err } -func (c *ConversationRpcClient) SetConversationMaxSeq( - ctx context.Context, - ownerUserIDs []string, - conversationID string, - maxSeq int64, -) error { - _, err := c.Client.SetConversationMaxSeq( - ctx, - &pbConversation.SetConversationMaxSeqReq{ - OwnerUserID: ownerUserIDs, - ConversationID: conversationID, - MaxSeq: maxSeq, - }, - ) +func (c *ConversationRpcClient) SetConversationMaxSeq(ctx context.Context, ownerUserIDs []string, conversationID string, maxSeq int64) error { + _, err := c.Client.SetConversationMaxSeq(ctx, &pbConversation.SetConversationMaxSeqReq{OwnerUserID: ownerUserIDs, ConversationID: conversationID, MaxSeq: maxSeq}) return err } - -func (c *ConversationRpcClient) SetConversations( - ctx context.Context, - userIDs []string, - conversation *pbConversation.ConversationReq, -) error { - _, err := c.Client.SetConversations( - ctx, - &pbConversation.SetConversationsReq{UserIDs: userIDs, Conversation: conversation}, - ) +func (c *ConversationRpcClient) SetConversations(ctx context.Context, userIDs []string, conversation *pbConversation.ConversationReq) error { + _, err := c.Client.SetConversations(ctx, &pbConversation.SetConversationsReq{UserIDs: userIDs, Conversation: conversation}) return err } @@ -126,28 +70,16 @@ func (c *ConversationRpcClient) GetConversationIDs(ctx context.Context, ownerUse return resp.ConversationIDs, nil } -func (c *ConversationRpcClient) GetConversation( - ctx context.Context, - ownerUserID, conversationID string, -) (*pbConversation.Conversation, error) { - resp, err := c.Client.GetConversation( - ctx, - &pbConversation.GetConversationReq{OwnerUserID: ownerUserID, ConversationID: conversationID}, - ) +func (c *ConversationRpcClient) GetConversation(ctx context.Context, ownerUserID, conversationID string) (*pbConversation.Conversation, error) { + resp, err := c.Client.GetConversation(ctx, &pbConversation.GetConversationReq{OwnerUserID: ownerUserID, ConversationID: conversationID}) if err != nil { return nil, err } return resp.Conversation, nil } -func (c *ConversationRpcClient) GetConversationsByConversationID( - ctx context.Context, - conversationIDs []string, -) ([]*pbConversation.Conversation, error) { - resp, err := c.Client.GetConversationsByConversationID( - ctx, - &pbConversation.GetConversationsByConversationIDReq{ConversationIDs: conversationIDs}, - ) +func (c *ConversationRpcClient) GetConversationsByConversationID(ctx context.Context, conversationIDs []string) ([]*pbConversation.Conversation, error) { + resp, err := c.Client.GetConversationsByConversationID(ctx, &pbConversation.GetConversationsByConversationIDReq{ConversationIDs: conversationIDs}) if err != nil { return nil, err } @@ -157,15 +89,8 @@ func (c *ConversationRpcClient) GetConversationsByConversationID( return resp.Conversations, nil } -func (c *ConversationRpcClient) GetConversations( - ctx context.Context, - ownerUserID string, - conversationIDs []string, -) ([]*pbConversation.Conversation, error) { - resp, err := c.Client.GetConversations( - ctx, - &pbConversation.GetConversationsReq{OwnerUserID: ownerUserID, ConversationIDs: conversationIDs}, - ) +func (c *ConversationRpcClient) GetConversations(ctx context.Context, ownerUserID string, conversationIDs []string) ([]*pbConversation.Conversation, error) { + resp, err := c.Client.GetConversations(ctx, &pbConversation.GetConversationsReq{OwnerUserID: ownerUserID, ConversationIDs: conversationIDs}) if err != nil { return nil, err } diff --git a/pkg/rpcclient/msg.go b/pkg/rpcclient/msg.go index aabc3768a..ca02ecc90 100644 --- a/pkg/rpcclient/msg.go +++ b/pkg/rpcclient/msg.go @@ -1,26 +1,9 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package rpcclient import ( "context" "encoding/json" - "google.golang.org/grpc" - "google.golang.org/protobuf/proto" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" "github.com/OpenIMSDK/Open-IM-Server/pkg/common/log" @@ -28,6 +11,8 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" + "google.golang.org/grpc" + "google.golang.org/protobuf/proto" // "google.golang.org/protobuf/proto" ) @@ -115,6 +100,8 @@ func newSessionTypeConf() map[int32]int32 { constant.ConversationChangeNotification: constant.SingleChatType, constant.ConversationUnreadNotification: constant.SingleChatType, constant.ConversationPrivateChatNotification: constant.SingleChatType, + // delete + constant.MsgDeleteNotification: constant.SingleChatType, } } @@ -149,10 +136,7 @@ func (m *MessageRpcClient) GetMaxSeq(ctx context.Context, req *sdkws.GetMaxSeqRe return resp, err } -func (m *MessageRpcClient) PullMessageBySeqList( - ctx context.Context, - req *sdkws.PullMessageBySeqsReq, -) (*sdkws.PullMessageBySeqsResp, error) { +func (m *MessageRpcClient) PullMessageBySeqList(ctx context.Context, req *sdkws.PullMessageBySeqsReq) (*sdkws.PullMessageBySeqsResp, error) { resp, err := m.Client.PullMessageBySeqs(ctx, req) return resp, err } @@ -169,62 +153,71 @@ type NotificationSender struct { contentTypeConf map[int32]config.NotificationConf sessionTypeConf map[int32]int32 sendMsg func(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error) + getUserInfo func(ctx context.Context, userID string) (*sdkws.UserInfo, error) } -type NewNotificationSenderOptions func(*NotificationSender) +type NotificationSenderOptions func(*NotificationSender) -func WithLocalSendMsg( - sendMsg func(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error), -) NewNotificationSenderOptions { +func WithLocalSendMsg(sendMsg func(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error)) NotificationSenderOptions { return func(s *NotificationSender) { s.sendMsg = sendMsg } } -func WithRpcClient(msgRpcClient *MessageRpcClient) NewNotificationSenderOptions { +func WithRpcClient(msgRpcClient *MessageRpcClient) NotificationSenderOptions { return func(s *NotificationSender) { s.sendMsg = msgRpcClient.SendMsg } } -func NewNotificationSender(opts ...NewNotificationSenderOptions) *NotificationSender { - notificationSender := &NotificationSender{ - contentTypeConf: newContentTypeConf(), - sessionTypeConf: newSessionTypeConf(), +func WithUserRpcClient(userRpcClient *UserRpcClient) NotificationSenderOptions { + return func(s *NotificationSender) { + s.getUserInfo = userRpcClient.GetUserInfo } +} + +func NewNotificationSender(opts ...NotificationSenderOptions) *NotificationSender { + notificationSender := &NotificationSender{contentTypeConf: newContentTypeConf(), sessionTypeConf: newSessionTypeConf()} for _, opt := range opts { opt(notificationSender) } return notificationSender } -func (s *NotificationSender) NotificationWithSesstionType( - ctx context.Context, - sendID, recvID string, - contentType, sesstionType int32, - m proto.Message, - opts ...utils.OptionsOpt, -) (err error) { +type notificationOpt struct { + WithRpcGetUsername bool +} + +type NotificationOptions func(*notificationOpt) + +func WithRpcGetUserName() NotificationOptions { + return func(opt *notificationOpt) { + opt.WithRpcGetUsername = true + } +} + +func (s *NotificationSender) NotificationWithSesstionType(ctx context.Context, sendID, recvID string, contentType, sesstionType int32, m proto.Message, opts ...NotificationOptions) (err error) { n := sdkws.NotificationElem{Detail: utils.StructToJsonString(m)} content, err := json.Marshal(&n) if err != nil { - log.ZError( - ctx, - "MsgClient Notification json.Marshal failed", - err, - "sendID", - sendID, - "recvID", - recvID, - "contentType", - contentType, - "msg", - m, - ) + log.ZError(ctx, "MsgClient Notification json.Marshal failed", err, "sendID", sendID, "recvID", recvID, "contentType", contentType, "msg", m) return err } + notificationOpt := ¬ificationOpt{} + for _, opt := range opts { + opt(notificationOpt) + } var req msg.SendMsgReq var msg sdkws.MsgData + if notificationOpt.WithRpcGetUsername && s.getUserInfo != nil { + userInfo, err := s.getUserInfo(ctx, sendID) + if err != nil { + log.ZWarn(ctx, "getUserInfo failed", err, "sendID", sendID) + } else { + msg.SenderNickname = userInfo.Nickname + msg.SenderFaceURL = userInfo.FaceURL + } + } var offlineInfo sdkws.OfflinePushInfo var title, desc, ex string msg.SendID = sendID @@ -239,7 +232,6 @@ func (s *NotificationSender) NotificationWithSesstionType( msg.CreateTime = utils.GetCurrentTimestampByMill() msg.ClientMsgID = utils.GetMsgID(sendID) options := config.GetOptionsByNotification(s.contentTypeConf[contentType]) - options = utils.WithOptions(options, opts...) msg.Options = options offlineInfo.Title = title offlineInfo.Desc = desc @@ -255,12 +247,6 @@ func (s *NotificationSender) NotificationWithSesstionType( return err } -func (s *NotificationSender) Notification( - ctx context.Context, - sendID, recvID string, - contentType int32, - m proto.Message, - opts ...utils.OptionsOpt, -) error { +func (s *NotificationSender) Notification(ctx context.Context, sendID, recvID string, contentType int32, m proto.Message, opts ...NotificationOptions) error { return s.NotificationWithSesstionType(ctx, sendID, recvID, contentType, s.sessionTypeConf[contentType], m, opts...) } diff --git a/pkg/rpcclient/notification/extend_msg.go b/pkg/rpcclient/notification/extend_msg.go deleted file mode 100644 index 85e5b1903..000000000 --- a/pkg/rpcclient/notification/extend_msg.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package notification - -import ( - "context" - "encoding/json" - - "google.golang.org/protobuf/proto" - - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" - "github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext" - "github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry" - "github.com/OpenIMSDK/Open-IM-Server/pkg/errs" - "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg" - "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" - "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" - "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" -) - -type ExtendMsgNotificationSender struct { - *rpcclient.MessageRpcClient -} - -func NewExtendMsgNotificationSender(client discoveryregistry.SvcDiscoveryRegistry) *ExtendMsgNotificationSender { - return &ExtendMsgNotificationSender{} -} - -func (e *ExtendMsgNotificationSender) ExtendMessageUpdatedNotification( - ctx context.Context, - sendID string, - conversationID string, - sessionType int32, - req *msg.SetMessageReactionExtensionsReq, - resp *msg.SetMessageReactionExtensionsResp, - isHistory bool, - isReactionFromCache bool, -) { - var content sdkws.ReactionMessageModifierNotification - content.ConversationID = req.ConversationID - content.OpUserID = mcontext.GetOpUserID(ctx) - content.SessionType = req.SessionType - keyMap := make(map[string]*sdkws.KeyValue) - for _, valueResp := range resp.Result { - if valueResp.ErrCode == 0 { - keyMap[valueResp.KeyValue.TypeKey] = valueResp.KeyValue - } - } - if len(keyMap) == 0 { - return - } - content.SuccessReactionExtensions = keyMap - content.ClientMsgID = req.ClientMsgID - content.IsReact = resp.IsReact - content.IsExternalExtensions = req.IsExternalExtensions - content.MsgFirstModifyTime = resp.MsgFirstModifyTime - e.messageReactionSender( - ctx, - sendID, - conversationID, - sessionType, - constant.ReactionMessageModifier, - &content, - isHistory, - isReactionFromCache, - ) -} - -func (e *ExtendMsgNotificationSender) ExtendMessageDeleteNotification( - ctx context.Context, - sendID string, - conversationID string, - sessionType int32, - req *msg.DeleteMessagesReactionExtensionsReq, - resp *msg.DeleteMessagesReactionExtensionsResp, - isHistory bool, - isReactionFromCache bool, -) { - var content sdkws.ReactionMessageDeleteNotification - content.ConversationID = req.ConversationID - content.OpUserID = req.OpUserID - content.SessionType = req.SessionType - keyMap := make(map[string]*sdkws.KeyValue) - for _, valueResp := range resp.Result { - if valueResp.ErrCode == 0 { - keyMap[valueResp.KeyValue.TypeKey] = valueResp.KeyValue - } - } - if len(keyMap) == 0 { - return - } - content.SuccessReactionExtensions = keyMap - content.ClientMsgID = req.ClientMsgID - content.MsgFirstModifyTime = req.MsgFirstModifyTime - e.messageReactionSender( - ctx, - sendID, - conversationID, - sessionType, - constant.ReactionMessageDeleter, - &content, - isHistory, - isReactionFromCache, - ) -} - -func (e *ExtendMsgNotificationSender) messageReactionSender( - ctx context.Context, - sendID string, - conversationID string, - sessionType, contentType int32, - m proto.Message, - isHistory bool, - isReactionFromCache bool, -) error { - options := make(map[string]bool, 5) - utils.SetSwitchFromOptions(options, constant.IsOfflinePush, false) - utils.SetSwitchFromOptions(options, constant.IsConversationUpdate, false) - utils.SetSwitchFromOptions(options, constant.IsSenderConversationUpdate, false) - utils.SetSwitchFromOptions(options, constant.IsUnreadCount, false) - utils.SetSwitchFromOptions(options, constant.IsReactionFromCache, isReactionFromCache) - if !isHistory { - utils.SetSwitchFromOptions(options, constant.IsHistory, false) - utils.SetSwitchFromOptions(options, constant.IsPersistent, false) - } - bytes, err := json.Marshal(m) - if err != nil { - return errs.ErrData.Wrap(err.Error()) - } - pbData := msg.SendMsgReq{ - MsgData: &sdkws.MsgData{ - SendID: sendID, - ClientMsgID: utils.GetMsgID(sendID), - SessionType: sessionType, - MsgFrom: constant.SysMsgType, - ContentType: contentType, - Content: bytes, - CreateTime: utils.GetCurrentTimestampByMill(), - Options: options, - }, - } - switch sessionType { - case constant.SingleChatType, constant.NotificationChatType: - pbData.MsgData.RecvID = conversationID - case constant.GroupChatType, constant.SuperGroupChatType: - pbData.MsgData.GroupID = conversationID - } - _, err = e.SendMsg(ctx, &pbData) - return err -} diff --git a/pkg/rpcclient/notification/group.go b/pkg/rpcclient/notification/group.go index fcc32b092..3efe76802 100644 --- a/pkg/rpcclient/notification/group.go +++ b/pkg/rpcclient/notification/group.go @@ -1,17 +1,3 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package notification import ( @@ -30,13 +16,9 @@ import ( "github.com/OpenIMSDK/Open-IM-Server/pkg/utils" ) -func NewGroupNotificationSender( - db controller.GroupDatabase, - msgRpcClient *rpcclient.MessageRpcClient, - fn func(ctx context.Context, userIDs []string) ([]CommonUser, error), -) *GroupNotificationSender { +func NewGroupNotificationSender(db controller.GroupDatabase, msgRpcClient *rpcclient.MessageRpcClient, userRpcClient *rpcclient.UserRpcClient, fn func(ctx context.Context, userIDs []string) ([]CommonUser, error)) *GroupNotificationSender { return &GroupNotificationSender{ - NotificationSender: rpcclient.NewNotificationSender(rpcclient.WithRpcClient(msgRpcClient)), + NotificationSender: rpcclient.NewNotificationSender(rpcclient.WithRpcClient(msgRpcClient), rpcclient.WithUserRpcClient(userRpcClient)), getUsersInfo: fn, db: db, } @@ -98,11 +80,7 @@ func (g *GroupNotificationSender) getGroupInfo(ctx context.Context, groupID stri }, nil } -func (g *GroupNotificationSender) getGroupMembers( - ctx context.Context, - groupID string, - userIDs []string, -) ([]*sdkws.GroupMemberFullInfo, error) { +func (g *GroupNotificationSender) getGroupMembers(ctx context.Context, groupID string, userIDs []string) ([]*sdkws.GroupMemberFullInfo, error) { members, err := g.db.FindGroupMember(ctx, []string{groupID}, userIDs, nil) if err != nil { return nil, err @@ -117,9 +95,7 @@ func (g *GroupNotificationSender) getGroupMembers( for _, member := range members { user, ok := users[member.UserID] if !ok { - return nil, errs.ErrUserIDNotFound.Wrap( - fmt.Sprintf("group %s member %s not in user", member.GroupID, member.UserID), - ) + return nil, errs.ErrUserIDNotFound.Wrap(fmt.Sprintf("group %s member %s not in user", member.GroupID, member.UserID)) } if member.Nickname == "" { member.Nickname = user.Nickname @@ -141,11 +117,7 @@ func (g *GroupNotificationSender) getGroupMembers( return res, nil } -func (g *GroupNotificationSender) getGroupMemberMap( - ctx context.Context, - groupID string, - userIDs []string, -) (map[string]*sdkws.GroupMemberFullInfo, error) { +func (g *GroupNotificationSender) getGroupMemberMap(ctx context.Context, groupID string, userIDs []string) (map[string]*sdkws.GroupMemberFullInfo, error) { members, err := g.getGroupMembers(ctx, groupID, userIDs) if err != nil { return nil, err @@ -157,11 +129,7 @@ func (g *GroupNotificationSender) getGroupMemberMap( return m, nil } -func (g *GroupNotificationSender) getGroupMember( - ctx context.Context, - groupID string, - userID string, -) (*sdkws.GroupMemberFullInfo, error) { +func (g *GroupNotificationSender) getGroupMember(ctx context.Context, groupID string, userID string) (*sdkws.GroupMemberFullInfo, error) { members, err := g.getGroupMembers(ctx, groupID, []string{userID}) if err != nil { return nil, err @@ -181,11 +149,7 @@ func (g *GroupNotificationSender) getGroupOwnerAndAdminUserID(ctx context.Contex return utils.Slice(members, fn), nil } -func (g *GroupNotificationSender) groupDB2PB( - group *relation.GroupModel, - ownerUserID string, - memberCount uint32, -) *sdkws.GroupInfo { +func (g *GroupNotificationSender) groupDB2PB(group *relation.GroupModel, ownerUserID string, memberCount uint32) *sdkws.GroupInfo { return &sdkws.GroupInfo{ GroupID: group.GroupID, GroupName: group.GroupName, @@ -207,10 +171,7 @@ func (g *GroupNotificationSender) groupDB2PB( } } -func (g *GroupNotificationSender) groupMemberDB2PB( - member *relation.GroupMemberModel, - appMangerLevel int32, -) *sdkws.GroupMemberFullInfo { +func (g *GroupNotificationSender) groupMemberDB2PB(member *relation.GroupMemberModel, appMangerLevel int32) *sdkws.GroupMemberFullInfo { return &sdkws.GroupMemberFullInfo{ GroupID: member.GroupID, UserID: member.UserID, @@ -227,10 +188,7 @@ func (g *GroupNotificationSender) groupMemberDB2PB( } } -func (g *GroupNotificationSender) getUsersInfoMap( - ctx context.Context, - userIDs []string, -) (map[string]*sdkws.UserInfo, error) { +func (g *GroupNotificationSender) getUsersInfoMap(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error) { users, err := g.getUsersInfo(ctx, userIDs) if err != nil { return nil, err @@ -242,11 +200,7 @@ func (g *GroupNotificationSender) getUsersInfoMap( return result, nil } -func (g *GroupNotificationSender) fillOpUser( - ctx context.Context, - opUser **sdkws.GroupMemberFullInfo, - groupID string, -) error { +func (g *GroupNotificationSender) fillOpUser(ctx context.Context, opUser **sdkws.GroupMemberFullInfo, groupID string) error { if opUser == nil { return errs.ErrInternalServer.Wrap("**sdkws.GroupMemberFullInfo is nil") } @@ -285,62 +239,35 @@ func (g *GroupNotificationSender) fillOpUser( return nil } -func (g *GroupNotificationSender) GroupCreatedNotification( - ctx context.Context, - tips *sdkws.GroupCreatedTips, -) (err error) { +func (g *GroupNotificationSender) GroupCreatedNotification(ctx context.Context, tips *sdkws.GroupCreatedTips) (err error) { if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } return g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupCreatedNotification, tips) } -func (g *GroupNotificationSender) GroupInfoSetNotification( - ctx context.Context, - tips *sdkws.GroupInfoSetTips, -) (err error) { +func (g *GroupNotificationSender) GroupInfoSetNotification(ctx context.Context, tips *sdkws.GroupInfoSetTips) (err error) { if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } - return g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetNotification, tips) + return g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetNotification, tips, rpcclient.WithRpcGetUserName()) } -func (g *GroupNotificationSender) GroupInfoSetNameNotification( - ctx context.Context, - tips *sdkws.GroupInfoSetNameTips, -) (err error) { +func (g *GroupNotificationSender) GroupInfoSetNameNotification(ctx context.Context, tips *sdkws.GroupInfoSetNameTips) (err error) { if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } - return g.Notification( - ctx, - mcontext.GetOpUserID(ctx), - tips.Group.GroupID, - constant.GroupInfoSetNameNotification, - tips, - ) + return g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetNameNotification, tips) } -func (g *GroupNotificationSender) GroupInfoSetAnnouncementNotification( - ctx context.Context, - tips *sdkws.GroupInfoSetAnnouncementTips, -) (err error) { +func (g *GroupNotificationSender) GroupInfoSetAnnouncementNotification(ctx context.Context, tips *sdkws.GroupInfoSetAnnouncementTips) (err error) { if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } - return g.Notification( - ctx, - mcontext.GetOpUserID(ctx), - tips.Group.GroupID, - constant.GroupInfoSetAnnouncementNotification, - tips, - ) + return g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetAnnouncementNotification, tips, rpcclient.WithRpcGetUserName()) } -func (g *GroupNotificationSender) JoinGroupApplicationNotification( - ctx context.Context, - req *pbGroup.JoinGroupReq, -) (err error) { +func (g *GroupNotificationSender) JoinGroupApplicationNotification(ctx context.Context, req *pbGroup.JoinGroupReq) (err error) { group, err := g.getGroupInfo(ctx, req.GroupID) if err != nil { return err @@ -364,10 +291,7 @@ func (g *GroupNotificationSender) JoinGroupApplicationNotification( return nil } -func (g *GroupNotificationSender) MemberQuitNotification( - ctx context.Context, - member *sdkws.GroupMemberFullInfo, -) (err error) { +func (g *GroupNotificationSender) MemberQuitNotification(ctx context.Context, member *sdkws.GroupMemberFullInfo) (err error) { defer log.ZDebug(ctx, "return") defer func() { if err != nil { @@ -382,10 +306,7 @@ func (g *GroupNotificationSender) MemberQuitNotification( return g.Notification(ctx, mcontext.GetOpUserID(ctx), member.GroupID, constant.MemberQuitNotification, tips) } -func (g *GroupNotificationSender) GroupApplicationAcceptedNotification( - ctx context.Context, - req *pbGroup.GroupApplicationResponseReq, -) (err error) { +func (g *GroupNotificationSender) GroupApplicationAcceptedNotification(ctx context.Context, req *pbGroup.GroupApplicationResponseReq) (err error) { defer log.ZDebug(ctx, "return") defer func() { if err != nil { @@ -405,13 +326,7 @@ func (g *GroupNotificationSender) GroupApplicationAcceptedNotification( return err } for _, userID := range append(userIDs, mcontext.GetOpUserID(ctx)) { - err = g.Notification( - ctx, - mcontext.GetOpUserID(ctx), - userID, - constant.GroupApplicationAcceptedNotification, - tips, - ) + err = g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.GroupApplicationAcceptedNotification, tips) if err != nil { log.ZError(ctx, "failed", err) } @@ -419,10 +334,7 @@ func (g *GroupNotificationSender) GroupApplicationAcceptedNotification( return nil } -func (g *GroupNotificationSender) GroupApplicationRejectedNotification( - ctx context.Context, - req *pbGroup.GroupApplicationResponseReq, -) (err error) { +func (g *GroupNotificationSender) GroupApplicationRejectedNotification(ctx context.Context, req *pbGroup.GroupApplicationResponseReq) (err error) { group, err := g.getGroupInfo(ctx, req.GroupID) if err != nil { return err @@ -436,13 +348,7 @@ func (g *GroupNotificationSender) GroupApplicationRejectedNotification( return err } for _, userID := range append(userIDs, mcontext.GetOpUserID(ctx)) { - err = g.Notification( - ctx, - mcontext.GetOpUserID(ctx), - userID, - constant.GroupApplicationRejectedNotification, - tips, - ) + err = g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.GroupApplicationRejectedNotification, tips) if err != nil { log.ZError(ctx, "failed", err) } @@ -450,10 +356,7 @@ func (g *GroupNotificationSender) GroupApplicationRejectedNotification( return nil } -func (g *GroupNotificationSender) GroupOwnerTransferredNotification( - ctx context.Context, - req *pbGroup.TransferGroupOwnerReq, -) (err error) { +func (g *GroupNotificationSender) GroupOwnerTransferredNotification(ctx context.Context, req *pbGroup.TransferGroupOwnerReq) (err error) { group, err := g.getGroupInfo(ctx, req.GroupID) if err != nil { return err @@ -463,38 +366,21 @@ func (g *GroupNotificationSender) GroupOwnerTransferredNotification( if err != nil { return err } - tips := &sdkws.GroupOwnerTransferredTips{ - Group: group, - OpUser: member[opUserID], - NewGroupOwner: member[req.NewOwnerUserID], - } + tips := &sdkws.GroupOwnerTransferredTips{Group: group, OpUser: member[opUserID], NewGroupOwner: member[req.NewOwnerUserID]} if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } - return g.Notification( - ctx, - mcontext.GetOpUserID(ctx), - group.GroupID, - constant.GroupOwnerTransferredNotification, - tips, - ) + return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupOwnerTransferredNotification, tips) } -func (g *GroupNotificationSender) MemberKickedNotification( - ctx context.Context, - tips *sdkws.MemberKickedTips, -) (err error) { +func (g *GroupNotificationSender) MemberKickedNotification(ctx context.Context, tips *sdkws.MemberKickedTips) (err error) { if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } return g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.MemberKickedNotification, tips) } -func (g *GroupNotificationSender) MemberInvitedNotification( - ctx context.Context, - groupID, reason string, - invitedUserIDList []string, -) (err error) { +func (g *GroupNotificationSender) MemberInvitedNotification(ctx context.Context, groupID, reason string, invitedUserIDList []string) (err error) { group, err := g.getGroupInfo(ctx, groupID) if err != nil { return err @@ -513,10 +399,7 @@ func (g *GroupNotificationSender) MemberInvitedNotification( return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.MemberInvitedNotification, tips) } -func (g *GroupNotificationSender) MemberEnterNotification( - ctx context.Context, - req *pbGroup.GroupApplicationResponseReq, -) (err error) { +func (g *GroupNotificationSender) MemberEnterNotification(ctx context.Context, req *pbGroup.GroupApplicationResponseReq) (err error) { group, err := g.getGroupInfo(ctx, req.GroupID) if err != nil { return err @@ -529,21 +412,14 @@ func (g *GroupNotificationSender) MemberEnterNotification( return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.MemberEnterNotification, tips) } -func (g *GroupNotificationSender) GroupDismissedNotification( - ctx context.Context, - tips *sdkws.GroupDismissedTips, -) (err error) { +func (g *GroupNotificationSender) GroupDismissedNotification(ctx context.Context, tips *sdkws.GroupDismissedTips) (err error) { if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } return g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupDismissedNotification, tips) } -func (g *GroupNotificationSender) GroupMemberMutedNotification( - ctx context.Context, - groupID, groupMemberUserID string, - mutedSeconds uint32, -) (err error) { +func (g *GroupNotificationSender) GroupMemberMutedNotification(ctx context.Context, groupID, groupMemberUserID string, mutedSeconds uint32) (err error) { group, err := g.getGroupInfo(ctx, groupID) if err != nil { return err @@ -560,10 +436,7 @@ func (g *GroupNotificationSender) GroupMemberMutedNotification( return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberMutedNotification, tips) } -func (g *GroupNotificationSender) GroupMemberCancelMutedNotification( - ctx context.Context, - groupID, groupMemberUserID string, -) (err error) { +func (g *GroupNotificationSender) GroupMemberCancelMutedNotification(ctx context.Context, groupID, groupMemberUserID string) (err error) { group, err := g.getGroupInfo(ctx, groupID) if err != nil { return err @@ -572,21 +445,11 @@ func (g *GroupNotificationSender) GroupMemberCancelMutedNotification( if err != nil { return err } - tips := &sdkws.GroupMemberCancelMutedTips{ - Group: group, - OpUser: user[mcontext.GetOpUserID(ctx)], - MutedUser: user[groupMemberUserID], - } + tips := &sdkws.GroupMemberCancelMutedTips{Group: group, OpUser: user[mcontext.GetOpUserID(ctx)], MutedUser: user[groupMemberUserID]} if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } - return g.Notification( - ctx, - mcontext.GetOpUserID(ctx), - group.GroupID, - constant.GroupMemberCancelMutedNotification, - tips, - ) + return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberCancelMutedNotification, tips) } func (g *GroupNotificationSender) GroupMutedNotification(ctx context.Context, groupID string) (err error) { @@ -627,10 +490,7 @@ func (g *GroupNotificationSender) GroupCancelMutedNotification(ctx context.Conte return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupCancelMutedNotification, tips) } -func (g *GroupNotificationSender) GroupMemberInfoSetNotification( - ctx context.Context, - groupID, groupMemberUserID string, -) (err error) { +func (g *GroupNotificationSender) GroupMemberInfoSetNotification(ctx context.Context, groupID, groupMemberUserID string) (err error) { group, err := g.getGroupInfo(ctx, groupID) if err != nil { return err @@ -639,21 +499,14 @@ func (g *GroupNotificationSender) GroupMemberInfoSetNotification( if err != nil { return err } - tips := &sdkws.GroupMemberInfoSetTips{ - Group: group, - OpUser: user[mcontext.GetOpUserID(ctx)], - ChangedUser: user[groupMemberUserID], - } + tips := &sdkws.GroupMemberInfoSetTips{Group: group, OpUser: user[mcontext.GetOpUserID(ctx)], ChangedUser: user[groupMemberUserID]} if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberInfoSetNotification, tips) } -func (g *GroupNotificationSender) GroupMemberSetToAdminNotification( - ctx context.Context, - groupID, groupMemberUserID string, -) (err error) { +func (g *GroupNotificationSender) GroupMemberSetToAdminNotification(ctx context.Context, groupID, groupMemberUserID string) (err error) { group, err := g.getGroupInfo(ctx, groupID) if err != nil { return err @@ -662,27 +515,14 @@ func (g *GroupNotificationSender) GroupMemberSetToAdminNotification( if err != nil { return err } - tips := &sdkws.GroupMemberInfoSetTips{ - Group: group, - OpUser: user[mcontext.GetOpUserID(ctx)], - ChangedUser: user[groupMemberUserID], - } + tips := &sdkws.GroupMemberInfoSetTips{Group: group, OpUser: user[mcontext.GetOpUserID(ctx)], ChangedUser: user[groupMemberUserID]} if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } - return g.Notification( - ctx, - mcontext.GetOpUserID(ctx), - group.GroupID, - constant.GroupMemberSetToAdminNotification, - tips, - ) + return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberSetToAdminNotification, tips) } -func (g *GroupNotificationSender) GroupMemberSetToOrdinaryUserNotification( - ctx context.Context, - groupID, groupMemberUserID string, -) (err error) { +func (g *GroupNotificationSender) GroupMemberSetToOrdinaryUserNotification(ctx context.Context, groupID, groupMemberUserID string) (err error) { group, err := g.getGroupInfo(ctx, groupID) if err != nil { return err @@ -691,28 +531,14 @@ func (g *GroupNotificationSender) GroupMemberSetToOrdinaryUserNotification( if err != nil { return err } - tips := &sdkws.GroupMemberInfoSetTips{ - Group: group, - OpUser: user[mcontext.GetOpUserID(ctx)], - ChangedUser: user[groupMemberUserID], - } + tips := &sdkws.GroupMemberInfoSetTips{Group: group, OpUser: user[mcontext.GetOpUserID(ctx)], ChangedUser: user[groupMemberUserID]} if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } - return g.Notification( - ctx, - mcontext.GetOpUserID(ctx), - group.GroupID, - constant.GroupMemberSetToOrdinaryUserNotification, - tips, - ) + return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberSetToOrdinaryUserNotification, tips) } -func (g *GroupNotificationSender) MemberEnterDirectlyNotification( - ctx context.Context, - groupID string, - entrantUserID string, -) (err error) { +func (g *GroupNotificationSender) MemberEnterDirectlyNotification(ctx context.Context, groupID string, entrantUserID string) (err error) { defer log.ZDebug(ctx, "return") defer func() { if err != nil { diff --git a/pkg/rpcclient/notification/msg.go b/pkg/rpcclient/notification/msg.go new file mode 100644 index 000000000..97724f727 --- /dev/null +++ b/pkg/rpcclient/notification/msg.go @@ -0,0 +1,36 @@ +package notification + +import ( + "context" + + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant" + "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws" + "github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient" +) + +type MsgNotificationSender struct { + *rpcclient.NotificationSender +} + +func NewMsgNotificationSender(opts ...rpcclient.NotificationSenderOptions) *MsgNotificationSender { + return &MsgNotificationSender{rpcclient.NewNotificationSender(opts...)} +} + +func (m *MsgNotificationSender) UserDeleteMsgsNotification(ctx context.Context, userID, conversationID string, seqs []int64) error { + tips := sdkws.DeleteMsgsTips{ + UserID: userID, + ConversationID: conversationID, + Seqs: seqs, + } + return m.Notification(ctx, userID, userID, constant.MsgDeleteNotification, &tips) +} + +func (m *MsgNotificationSender) MarkAsReadNotification(ctx context.Context, conversationID string, sesstionType int32, sendID, recvID string, seqs []int64, hasReadSeq int64) error { + tips := &sdkws.MarkAsReadTips{ + MarkAsReadUserID: sendID, + ConversationID: conversationID, + Seqs: seqs, + HasReadSeq: hasReadSeq, + } + return m.NotificationWithSesstionType(ctx, sendID, recvID, constant.HasReadReceipt, sesstionType, tips) +} diff --git a/pkg/rpcclient/user.go b/pkg/rpcclient/user.go index 17dec167c..a88c061e2 100644 --- a/pkg/rpcclient/user.go +++ b/pkg/rpcclient/user.go @@ -17,7 +17,6 @@ package rpcclient import ( "context" "strings" - "time" "google.golang.org/grpc" @@ -33,17 +32,16 @@ import ( type User struct { conn grpc.ClientConnInterface Client user.UserClient - discov discoveryregistry.SvcDiscoveryRegistry + Discov discoveryregistry.SvcDiscoveryRegistry } func NewUser(discov discoveryregistry.SvcDiscoveryRegistry) *User { - ctx, _ := context.WithTimeout(context.Background(), time.Second*3) - conn, err := discov.GetConn(ctx, config.Config.RpcRegisterName.OpenImUserName) + conn, err := discov.GetConn(context.Background(), config.Config.RpcRegisterName.OpenImUserName) if err != nil { panic(err) } client := user.NewUserClient(conn) - return &User{discov: discov, Client: client, conn: conn} + return &User{Discov: discov, Client: client, conn: conn} } type UserRpcClient User diff --git a/pkg/utils/utils_v2.go b/pkg/utils/utils_v2.go index f4cc44871..dc14fe4e8 100644 --- a/pkg/utils/utils_v2.go +++ b/pkg/utils/utils_v2.go @@ -543,3 +543,15 @@ func Batch[T any, V any](fn func(T) V, ts []T) []V { } return res } + +func InitSlice[T any](val *[]T) { + if val != nil && *val == nil { + *val = []T{} + } +} + +func InitMap[K comparable, V any](val *map[K]V) { + if val != nil && *val == nil { + *val = map[K]V{} + } +} From b865fb12640fe29313f43030a69b7392042ff76a Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <86140903+cubxxw@users.noreply.github.com> Date: Wed, 12 Jul 2023 15:44:06 +0800 Subject: [PATCH 67/73] fix: fix start scripts (#501) * fix: fix start scripts Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> * feat: add openim ci Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> * feat: add bash scripts Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --------- Signed-off-by: Xinwei Xiong(cubxxw-openim) <3293172751nss@gmail.com> --- .github/workflows/scripts-test.yml | 14 ++++++++++---- assets/logo-gif/openim-logo.gif | Bin 430462 -> 1729731 bytes assets/openim-logo-gradient.pdf | Bin 0 -> 10462 bytes assets/openim-logo-gradient.svg | 1 + assets/openim-logo-green.pdf | Bin 0 -> 10749 bytes assets/openim-logo-green.svg | 1 + 6 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 assets/openim-logo-gradient.pdf create mode 100644 assets/openim-logo-gradient.svg create mode 100644 assets/openim-logo-green.pdf create mode 100644 assets/openim-logo-green.svg diff --git a/.github/workflows/scripts-test.yml b/.github/workflows/scripts-test.yml index 1358bcb37..f2fdc8a19 100644 --- a/.github/workflows/scripts-test.yml +++ b/.github/workflows/scripts-test.yml @@ -1,4 +1,4 @@ -name: Execute Scripts +name: OpenIM Start Execute Scripts on: push: @@ -27,20 +27,23 @@ jobs: uses: actions/checkout@v2 - name: Start Docker Compose - run: | + run: | docker-compose stop docker-compose up -d + sleep 60 - name: Stop all services run: | chmod +x ./scripts/stop_all.sh ./scripts/stop_all.sh + shell: bash - name: Build all services run: | chmod +x ./scripts/build_all_service.sh ./scripts/build_all_service.sh cat logs/openIM.log + shell: bash - name: Start all services run: | @@ -48,16 +51,19 @@ jobs: ./scripts/start_all.sh cat logs/openIM.log continue-on-error: true + shell: bash - name: Check all services run: | chmod +x ./scripts/check_all.sh ./scripts/check_all.sh cat logs/openIM.log + shell: bash continue-on-error: true - name: Print openIM.log run: | - cat -n logs/openIM.log + cat logs/openIM.log cat logs/openIM.log >> "$GITHUB_OUTPUT" - + shell: bash + continue-on-error: true diff --git a/assets/logo-gif/openim-logo.gif b/assets/logo-gif/openim-logo.gif index 7797896413b8fa290d3c51ad7ac145f34a44f2ed..42d7d65363d62829395b3ed25fce7fdd83a46c34 100644 GIT binary patch literal 1729731 zcma&NS5#9^+`k)@W&sNdiWHU5o1y|zRa!ub0s-mLTL7hZEEMUH-g^yAAan@5_a0iP zp%Z#S3E9W@ch1H8|6iS(S$oaed*-|Mnz`7|d>(lPIgvL;-zi1N2rMr^AP^V?f`C9U z5C{$eAweKC2t@p^7YqhLz#teH1P6nVU=SJ%BK~(P7y^PoKrjdh4gnz{AT$I-{O@@% z3Xc&n2-=JVP2m%Me;2=00goK08a1imonZQU81POv6L2x7pi3FjM zAmV@1gV7)e8U#aw;AjvM4ML+q#Q$mn6G0Fn2u1|Ki6A5qgeHQB|5GRw41z*HP#6db z2SJe_C>jJMf}kKU6byz!z)%<%3I{`xU?>_4C4!+K2owx~LLg8W1PX^hkq{^v0wqGA zAQ%)3gF;|X7z_%BL6I;h8U`i8pddID42MGCP#7EvheMHYC>jnW!l57}6pVyIkWd&B z3P(bbNGKW!B_g39G!%@6LeNkc8VW~4k!UCy4JD$XAR-h@ghGf=7!e95LXku$ng}Hl zp&$?f3_?Ia2p9+f2O*Fk1R8`Of)F4u0t`k#zz7%^0S6$Xb?aI0U$5{1_KZ<00RSXFn|ODXfQwo z10V3r z{e7EUUh4cz zu0&R~?Fy#2p3pznhC`^v3&Q?NUz%zY>hj|BNM6_n4!i{b5NG<=j=SizAHdUx={~oFHWq)~ zk>gzQyRT`vqndZstksT=` zSZA39m*lpJ{G%pXkQHklyqO!*DwmX3?9Hv4pZ(K%D?cw7VO@}4RcDPTLIGRWMaWj` z?V{>lh8gpEW6gMr#;X@RmTeRtip){Zc#6uqgsO@v1`c(0ko^oce3ionx_ni-JzKWL zH31j4C5z!acC{Okg}B=7#BIB}oz4rpbiebUjr73GH|r=OetWM8M9I6~OhymeZ=vES z+IKp|=-Ow}{Hon=zpn*5=y+&RbkND@x^vL=Bv5Z5CnZ+tuqQDwS+F;`yvU)CCr`;9 zKC30JsK1xzyyY*E^fUTmJWd(k3cwzUk$ty)<=#}(=t^Kt4m*goluqDIh=ga zwsX?=#qqKdZREcC@|Fh4Uu-r8Fd+!rqsX&UdN}Q_hy!RDFmWzodXmjVS??{e=CVF92%C0A!G= ziAQ(rC^eX_(&sl*RE7Vz?eyg;E1G*$`;OzoTl3_v<6B0Y_rE+!2M(+8e7P_3+$fPTc@-;Kp>?38Nkq6-xMOn-z)m=~C7leGjl|@ha2=anPE%5iM(X($gb$9Ca|L}xnuRnRCpb#7$ z8HdbfeI3-Q=ph)DHfPEycD+|Ecr-d&JcnDor#qcQ3M%{P}uSXU&D7xGtDF5fSPS=3MW1FiU~!VZL;p_tUzIbCHAxn1bP z50f1X)n)CA?PLwvEx9IS7!)!2@!VA4k>#6#=lTe30^>YV;goZWc zQUw=X`I$OG<9=AF*ZRfN*---ObfDCS(6?ZHgHYWHdkZ7?+xS6PcK7P#nJq*yp^ zCJ%u9d|LG7$~Cvx-qLQRxb z?hK(jUsIHYD|@Y|J3MT}`ltK(uPr`%eAW$7IfQW)t+I=q^?VF3k1owx0(7QZuO|Qf zQ7yhowc^sEPE!$|p|B+Ac-Cit+l_hIu*U7X3v_*@DGyu`w|K)6-SwvPWjLY1TZP$#nx7=czjq}pW$7Oe2g%{f z0&QVNk3FV`TwKZ%ieXmLo--rtRh8AY4-BO|ewM~o)^sW39Fpn{QzWbFrUq~>^`1uc za?TCvVT&H>=a?#kO7(?-JsnZVR

mWsOI*{Ct0{0D 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\OEj0%2){B)=^3y^&=#1A2M!}Y{^VT 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

=VoVpA%7aXkfWme^2PYwmcy<;x23ux9 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

)GE2eGB8k;HL4$7N@FK7i98Y*YdGky znnczP#s%}$zQHi2b~nb>-SAE|{@Po*Z!pZ(Ji6h`vRt;)tKD>5aJBy%3`5u=XZ{Am z>=Fe_x(;r7U%xEJX^+1nf^Dq;wd;PAm`W#VSZVwErrdNeks)la*bDs=@BMgnqTlum zw7Jgq_v3ZjYGbqvBiWU-7?lhUQ;A>CO4vEOmk(uEI;6KV654P2lzFQ^9_Iu8ij55@ zNGqNS8fX+WE$YmyDPf*`xOprax7tKe8bvsGW-5{UJ+HfLt4A+ow)Z{D-Ao OS? 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