From 34ed032af13e9743c8bf2b97695ba1d0d0692a54 Mon Sep 17 00:00:00 2001 From: xuexihuang <1339326187@qq.com> Date: Mon, 25 Dec 2023 15:32:07 +0800 Subject: [PATCH 01/20] K8s environment supports multiple msggateway by consistent hash (#1600) * feature:support multi msggateway * feature:support multi msggateway * feature:support multi msggateway by hash method * fix:fix log * change to consistent hash * change go.mod * fix:fix go routine values error * fix:fix push filter logic * fix:fix push filter logic --------- Co-authored-by: lin.huang --- go.mod | 8 +- go.sum | 12 +- internal/msggateway/n_ws_server.go | 13 +- internal/push/push_to_client.go | 139 +++++++++++++++--- .../kubernetes/kubernetes.go | 90 ++++++------ 5 files changed, 178 insertions(+), 84 deletions(-) diff --git a/go.mod b/go.mod index c8dd96689..9a9cde30e 100644 --- a/go.mod +++ b/go.mod @@ -34,10 +34,11 @@ require github.com/google/uuid v1.3.1 require ( github.com/IBM/sarama v1.41.3 github.com/OpenIMSDK/protocol v0.0.36 - github.com/OpenIMSDK/tools v0.0.20 + github.com/OpenIMSDK/tools v0.0.21 github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible github.com/go-redis/redis v6.15.9+incompatible github.com/redis/go-redis/v9 v9.2.1 + github.com/stathat/consistent v1.0.0 github.com/tencentyun/cos-go-sdk-v5 v0.7.45 go.uber.org/automaxprocs v1.5.3 golang.org/x/sync v0.4.0 @@ -141,6 +142,7 @@ require ( gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gorm.io/gorm v1.23.8 // indirect + stathat.com/c/consistent v1.0.0 // indirect ) require ( @@ -154,7 +156,3 @@ require ( golang.org/x/crypto v0.14.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) -//replace ( -// github.com/OpenIMSDK/protocol v0.0.34 => github.com/AndrewZuo01/protocol v0.0.0-20231218034338-b8d838e0b182 -//) - diff --git a/go.sum b/go.sum index 30e4b3cb4..61323c57b 100644 --- a/go.sum +++ b/go.sum @@ -18,10 +18,10 @@ firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIw github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c= github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ= -github.com/OpenIMSDK/protocol v0.0.31 h1:ax43x9aqA6EKNXNukS5MT5BSTqkUmwO4uTvbJLtzCgE= -github.com/OpenIMSDK/protocol v0.0.31/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= -github.com/OpenIMSDK/tools v0.0.20 h1:zBTjQZRJ5lR1FIzP9mtWyAvh5dKsmJXQugi4p8X/97k= -github.com/OpenIMSDK/tools v0.0.20/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= +github.com/OpenIMSDK/protocol v0.0.36 h1:UJnFsr1A4RrNeHMNDVS/1nvXWFoGM43dcXpZeJiIZ+0= +github.com/OpenIMSDK/protocol v0.0.36/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= +github.com/OpenIMSDK/tools v0.0.21 h1:iTapc2mIEVH/xl5Nd6jfwPub11Pgp44tVcE1rjB3a48= +github.com/OpenIMSDK/tools v0.0.21/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= @@ -308,6 +308,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= +github.com/stathat/consistent v1.0.0 h1:ZFJ1QTRn8npNBKW065raSZ8xfOqhpb8vLOkfp4CcL/U= +github.com/stathat/consistent v1.0.0/go.mod h1:uajTPbgSygZBJ+V+0mY7meZ8i0XAcZs7AQ6V121XSxw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -536,3 +538,5 @@ gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +stathat.com/c/consistent v1.0.0 h1:ezyc51EGcRPJUxfHGSgJjWzJdj3NiMU9pNfLNGiXV0c= +stathat.com/c/consistent v1.0.0/go.mod h1:QkzMWzcbB+yQBL2AttO6sgsQS/JSTapcDISJalmCDS0= diff --git a/internal/msggateway/n_ws_server.go b/internal/msggateway/n_ws_server.go index 737d5db14..70c2f8fe0 100644 --- a/internal/msggateway/n_ws_server.go +++ b/internal/msggateway/n_ws_server.go @@ -288,12 +288,13 @@ func (ws *WsServer) registerClient(client *Client) { } wg := sync.WaitGroup{} - wg.Add(1) - go func() { - defer wg.Done() - _ = ws.sendUserOnlineInfoToOtherNode(client.ctx, client) - }() - + if config.Config.Envs.Discovery == "zookeeper" { + wg.Add(1) + go func() { + defer wg.Done() + _ = ws.sendUserOnlineInfoToOtherNode(client.ctx, client) + }() + } wg.Add(1) go func() { defer wg.Done() diff --git a/internal/push/push_to_client.go b/internal/push/push_to_client.go index 2ee8c457f..12b78ea2d 100644 --- a/internal/push/push_to_client.go +++ b/internal/push/push_to_client.go @@ -18,6 +18,7 @@ import ( "context" "encoding/json" "errors" + "google.golang.org/grpc" "sync" "golang.org/x/sync/errgroup" @@ -142,6 +143,47 @@ func (p *Pusher) UnmarshalNotificationElem(bytes []byte, t any) error { return json.Unmarshal([]byte(notification.Detail), t) } +/* +k8s deployment,offline push group messages function +*/ +func (p *Pusher) k8sOfflinePush2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData, wsResults []*msggateway.SingleMsgToUserResults) error { + + var needOfflinePushUserIDs []string + for _, v := range wsResults { + if !v.OnlinePush { + needOfflinePushUserIDs = append(needOfflinePushUserIDs, v.UserID) + } + } + if len(needOfflinePushUserIDs) > 0 { + var offlinePushUserIDs []string + err := callbackOfflinePush(ctx, needOfflinePushUserIDs, msg, &offlinePushUserIDs) + if err != nil { + return err + } + + if len(offlinePushUserIDs) > 0 { + needOfflinePushUserIDs = offlinePushUserIDs + } + if msg.ContentType != constant.SignalingNotification { + resp, err := p.conversationRpcClient.Client.GetConversationOfflinePushUserIDs( + ctx, + &conversation.GetConversationOfflinePushUserIDsReq{ConversationID: utils.GenGroupConversationID(groupID), UserIDs: needOfflinePushUserIDs}, + ) + if err != nil { + return err + } + if len(resp.UserIDs) > 0 { + err = p.offlinePushMsg(ctx, groupID, msg, resp.UserIDs) + if err != nil { + log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg) + return err + } + } + } + + } + return nil +} func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData) (err error) { log.ZDebug(ctx, "Get super group msg from msg_transfer and push msg", "msg", msg.String(), "groupID", groupID) var pushToUserIDs []string @@ -205,7 +247,10 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws log.ZDebug(ctx, "get conn and online push success", "result", wsResults, "msg", msg) isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush) - if isOfflinePush { + if isOfflinePush && config.Config.Envs.Discovery == "k8s" { + return p.k8sOfflinePush2SuperGroup(ctx, groupID, msg, wsResults) + } + if isOfflinePush && config.Config.Envs.Discovery == "zookeeper" { var ( onlineSuccessUserIDs = []string{msg.SendID} webAndPcBackgroundUserIDs []string @@ -239,14 +284,7 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws } needOfflinePushUserIDs := utils.DifferenceString(onlineSuccessUserIDs, pushToUserIDs) - if msg.ContentType != constant.SignalingNotification { - notNotificationUserIDs, err := p.conversationLocalCache.GetRecvMsgNotNotifyUserIDs(ctx, groupID) - if err != nil { - return err - } - needOfflinePushUserIDs = utils.SliceSub(needOfflinePushUserIDs, notNotificationUserIDs) - } // Use offline push messaging if len(needOfflinePushUserIDs) > 0 { var offlinePushUserIDs []string @@ -258,30 +296,89 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws if len(offlinePushUserIDs) > 0 { needOfflinePushUserIDs = offlinePushUserIDs } - resp, err := p.conversationRpcClient.Client.GetConversationOfflinePushUserIDs( - ctx, - &conversation.GetConversationOfflinePushUserIDsReq{ConversationID: utils.GenGroupConversationID(groupID), UserIDs: needOfflinePushUserIDs}, - ) - if err != nil { - return err - } - if len(resp.UserIDs) > 0 { - err = p.offlinePushMsg(ctx, groupID, msg, resp.UserIDs) + if msg.ContentType != constant.SignalingNotification { + resp, err := p.conversationRpcClient.Client.GetConversationOfflinePushUserIDs( + ctx, + &conversation.GetConversationOfflinePushUserIDsReq{ConversationID: utils.GenGroupConversationID(groupID), UserIDs: needOfflinePushUserIDs}, + ) if err != nil { - log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg) return err } - if _, err := p.GetConnsAndOnlinePush(ctx, msg, utils.IntersectString(resp.UserIDs, webAndPcBackgroundUserIDs)); err != nil { - log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg, "userIDs", utils.IntersectString(needOfflinePushUserIDs, webAndPcBackgroundUserIDs)) - return err + if len(resp.UserIDs) > 0 { + err = p.offlinePushMsg(ctx, groupID, msg, resp.UserIDs) + if err != nil { + log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg) + return err + } + if _, err := p.GetConnsAndOnlinePush(ctx, msg, utils.IntersectString(resp.UserIDs, webAndPcBackgroundUserIDs)); err != nil { + log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg, "userIDs", utils.IntersectString(needOfflinePushUserIDs, webAndPcBackgroundUserIDs)) + return err + } } } + } } return nil } +func (p *Pusher) k8sOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) { + var usersHost = make(map[string][]string) + for _, v := range pushToUserIDs { + tHost, err := p.discov.GetUserIdHashGatewayHost(ctx, v) + if err != nil { + log.ZError(ctx, "get msggateway hash error", err) + return nil, err + } + tUsers, tbl := usersHost[tHost] + if tbl { + tUsers = append(tUsers, v) + usersHost[tHost] = tUsers + } else { + usersHost[tHost] = []string{v} + } + } + log.ZDebug(ctx, "genUsers send hosts struct:", "usersHost", usersHost) + var usersConns = make(map[*grpc.ClientConn][]string) + for host, userIds := range usersHost { + tconn, _ := p.discov.GetConn(ctx, host) + usersConns[tconn] = userIds + } + var ( + mu sync.Mutex + wg = errgroup.Group{} + maxWorkers = config.Config.Push.MaxConcurrentWorkers + ) + if maxWorkers < 3 { + maxWorkers = 3 + } + wg.SetLimit(maxWorkers) + for conn, userIds := range usersConns { + tcon := conn + tuserIds := userIds + wg.Go(func() error { + input := &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: tuserIds} + msgClient := msggateway.NewMsgGatewayClient(tcon) + reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input) + if err != nil { + return nil + } + log.ZDebug(ctx, "push result", "reply", reply) + if reply != nil && reply.SinglePushResult != nil { + mu.Lock() + wsResults = append(wsResults, reply.SinglePushResult...) + mu.Unlock() + } + return nil + }) + } + _ = wg.Wait() + return wsResults, nil +} func (p *Pusher) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) { + if config.Config.Envs.Discovery == "k8s" { + return p.k8sOnlinePush(ctx, msg, pushToUserIDs) + } conns, err := p.discov.GetConns(ctx, config.Config.RpcRegisterName.OpenImMessageGatewayName) log.ZDebug(ctx, "get gateway conn", "conn length", len(conns)) if err != nil { diff --git a/pkg/common/discoveryregister/kubernetes/kubernetes.go b/pkg/common/discoveryregister/kubernetes/kubernetes.go index cd5fb0a36..2ff1539e6 100644 --- a/pkg/common/discoveryregister/kubernetes/kubernetes.go +++ b/pkg/common/discoveryregister/kubernetes/kubernetes.go @@ -18,6 +18,7 @@ import ( "context" "errors" "fmt" + "github.com/stathat/consistent" "os" "strconv" "strings" @@ -31,51 +32,54 @@ import ( // K8sDR represents the Kubernetes service discovery and registration client. type K8sDR struct { - options []grpc.DialOption - rpcRegisterAddr string + options []grpc.DialOption + rpcRegisterAddr string + gatewayHostConsistent *consistent.Consistent } -// NewK8sDiscoveryRegister creates a new instance of K8sDR for Kubernetes service discovery and registration. func NewK8sDiscoveryRegister() (discoveryregistry.SvcDiscoveryRegistry, error) { - - return &K8sDR{}, nil + gatewayConsistent := consistent.New() + gatewayHosts := getMsgGatewayHost(context.Background()) + for _, v := range gatewayHosts { + gatewayConsistent.Add(v) + } + return &K8sDR{gatewayHostConsistent: gatewayConsistent}, nil } -// Register registers a service with Kubernetes. func (cli *K8sDR) Register(serviceName, host string, port int, opts ...grpc.DialOption) error { if serviceName != config.Config.RpcRegisterName.OpenImMessageGatewayName { cli.rpcRegisterAddr = serviceName } else { - cli.rpcRegisterAddr = cli.getSelfHost(context.Background()) + cli.rpcRegisterAddr = getSelfHost(context.Background()) } return nil } - -// UnRegister removes a service registration from Kubernetes. func (cli *K8sDR) UnRegister() error { return nil } - -// CreateRpcRootNodes creates root nodes for RPC in Kubernetes. func (cli *K8sDR) CreateRpcRootNodes(serviceNames []string) error { return nil } - -// RegisterConf2Registry registers a configuration to the registry. func (cli *K8sDR) RegisterConf2Registry(key string, conf []byte) error { return nil } -// GetConfFromRegistry retrieves a configuration from the registry. func (cli *K8sDR) GetConfFromRegistry(key string) ([]byte, error) { + return nil, nil } - -func (cli *K8sDR) getSelfHost(ctx context.Context) string { +func (cli *K8sDR) GetUserIdHashGatewayHost(ctx context.Context, userId string) (string, error) { + host, err := cli.gatewayHostConsistent.Get(userId) + if err != nil { + log.ZError(ctx, "GetUserIdHashGatewayHost error", err) + } + return host, err +} +func getSelfHost(ctx context.Context) string { port := 88 instance := "openimserver" selfPodName := os.Getenv("MY_POD_NAME") @@ -95,26 +99,8 @@ func (cli *K8sDR) getSelfHost(ctx context.Context) string { return host } -// GetConns returns a list of gRPC client connections for a given service. -func (cli *K8sDR) GetConns(ctx context.Context, serviceName string, opts ...grpc.DialOption) ([]*grpc.ClientConn, error) { - if serviceName != config.Config.RpcRegisterName.OpenImMessageGatewayName { - conn, err := grpc.DialContext(ctx, serviceName, append(cli.options, opts...)...) - return []*grpc.ClientConn{conn}, err - } - var ret []*grpc.ClientConn - gatewayHosts := cli.getMsgGatewayHost(ctx) - for _, host := range gatewayHosts { - conn, err := grpc.DialContext(ctx, host, append(cli.options, opts...)...) - if err != nil { - return nil, err - } - ret = append(ret, conn) - } - return ret, nil -} - // like openimserver-openim-msggateway-0.openimserver-openim-msggateway-headless.openim-lin.svc.cluster.local:88 -func (cli *K8sDR) getMsgGatewayHost(ctx context.Context) []string { +func getMsgGatewayHost(ctx context.Context) []string { port := 88 instance := "openimserver" selfPodName := os.Getenv("MY_POD_NAME") @@ -135,40 +121,48 @@ func (cli *K8sDR) getMsgGatewayHost(ctx context.Context) []string { ret = append(ret, host) } log.ZInfo(ctx, "getMsgGatewayHost", "instance", instance, "selfPodName", selfPodName, "replicas", replicas, "ns", ns, "ret", ret) - return ret } +func (cli *K8sDR) GetConns(ctx context.Context, serviceName string, opts ...grpc.DialOption) ([]*grpc.ClientConn, error) { -// GetConn returns a single gRPC client connection for a given service. + if serviceName != config.Config.RpcRegisterName.OpenImMessageGatewayName { + conn, err := grpc.DialContext(ctx, serviceName, append(cli.options, opts...)...) + return []*grpc.ClientConn{conn}, err + } else { + var ret []*grpc.ClientConn + gatewayHosts := getMsgGatewayHost(ctx) + for _, host := range gatewayHosts { + conn, err := grpc.DialContext(ctx, host, append(cli.options, opts...)...) + if err != nil { + return nil, err + } else { + ret = append(ret, conn) + } + } + return ret, nil + } +} func (cli *K8sDR) GetConn(ctx context.Context, serviceName string, opts ...grpc.DialOption) (*grpc.ClientConn, error) { + return grpc.DialContext(ctx, serviceName, append(cli.options, opts...)...) } -// GetSelfConnTarget returns the connection target of the client itself. func (cli *K8sDR) GetSelfConnTarget() string { + return cli.rpcRegisterAddr } - -// AddOption adds gRPC dial options to the client. func (cli *K8sDR) AddOption(opts ...grpc.DialOption) { cli.options = append(cli.options, opts...) } - -// CloseConn closes a given gRPC client connection. func (cli *K8sDR) CloseConn(conn *grpc.ClientConn) { conn.Close() } -// do not use this method for call rpc. +// do not use this method for call rpc func (cli *K8sDR) GetClientLocalConns() map[string][]*grpc.ClientConn { fmt.Println("should not call this function!!!!!!!!!!!!!!!!!!!!!!!!!") - return nil } - -// Close closes the K8sDR client. func (cli *K8sDR) Close() { - - // Close any open resources here (if applicable) return } From f10528010b9bad2ac32f833a8abe93f2b33a8f07 Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751@qq.com> Date: Mon, 25 Dec 2023 16:26:59 +0800 Subject: [PATCH 02/20] Update check-all.sh (#1591) --- scripts/check-all.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/check-all.sh b/scripts/check-all.sh index a438ac26b..efaa2ccb7 100755 --- a/scripts/check-all.sh +++ b/scripts/check-all.sh @@ -33,15 +33,15 @@ openim::log::info "\n# Begin to check all openim service" # OpenIM status # Elegant printing function print_services_and_ports() { - local -n service_names=$1 - local -n service_ports=$2 + service_names=("$1[@]") + service_ports=("$2[@]") echo "+-------------------------+----------+" echo "| Service Name | Port |" echo "+-------------------------+----------+" - for index in "${!service_names[@]}"; do - printf "| %-23s | %-8s |\n" "${service_names[$index]}" "${service_ports[$index]}" + for index in "${!service_names}"; do + printf "| %-23s | %-8s |\n" "${!service_names[$index]}" "${!service_ports[$index]}" done echo "+-------------------------+----------+" From 11a147792d7a8bd0e9d14933c64d60bcdb4da1da Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751@qq.com> Date: Mon, 25 Dec 2023 16:28:42 +0800 Subject: [PATCH 03/20] Update prometheus.yml (#1586) --- deployments/templates/prometheus.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/deployments/templates/prometheus.yml b/deployments/templates/prometheus.yml index 5c3e0af66..f1a551a62 100644 --- a/deployments/templates/prometheus.yml +++ b/deployments/templates/prometheus.yml @@ -44,12 +44,12 @@ scrape_configs: # prometheus fetches application services - job_name: 'openimserver-openim-api' static_configs: - - targets: [ '${OPENIM_SERVER_ADDRESS}:${API_PROM_PORT}' ] + - targets: [ '${DOCKER_BRIDGE_GATEWAY}:${API_PROM_PORT}' ] labels: namespace: 'default' - job_name: 'openimserver-openim-msggateway' static_configs: - - targets: [ '${OPENIM_SERVER_ADDRESS}:${MSG_GATEWAY_PROM_PORT}' ] + - targets: [ '${DOCKER_BRIDGE_GATEWAY}:${MSG_GATEWAY_PROM_PORT}' ] labels: namespace: 'default' - job_name: 'openimserver-openim-msgtransfer' @@ -59,41 +59,41 @@ scrape_configs: namespace: 'default' - job_name: 'openimserver-openim-push' static_configs: - - targets: [ '${OPENIM_SERVER_ADDRESS}:${PUSH_PROM_PORT}' ] + - targets: [ '${DOCKER_BRIDGE_GATEWAY}:${PUSH_PROM_PORT}' ] labels: namespace: 'default' - job_name: 'openimserver-openim-rpc-auth' static_configs: - - targets: [ '${OPENIM_SERVER_ADDRESS}:${AUTH_PROM_PORT}' ] + - targets: [ '${DOCKER_BRIDGE_GATEWAY}:${AUTH_PROM_PORT}' ] labels: namespace: 'default' - job_name: 'openimserver-openim-rpc-conversation' static_configs: - - targets: [ '${OPENIM_SERVER_ADDRESS}:${CONVERSATION_PROM_PORT}' ] + - targets: [ '${DOCKER_BRIDGE_GATEWAY}:${CONVERSATION_PROM_PORT}' ] labels: namespace: 'default' - job_name: 'openimserver-openim-rpc-friend' static_configs: - - targets: [ '${OPENIM_SERVER_ADDRESS}:${FRIEND_PROM_PORT}' ] + - targets: [ '${DOCKER_BRIDGE_GATEWAY}:${FRIEND_PROM_PORT}' ] labels: namespace: 'default' - job_name: 'openimserver-openim-rpc-group' static_configs: - - targets: [ '${OPENIM_SERVER_ADDRESS}:${GROUP_PROM_PORT}' ] + - targets: [ '${DOCKER_BRIDGE_GATEWAY}:${GROUP_PROM_PORT}' ] labels: namespace: 'default' - job_name: 'openimserver-openim-rpc-msg' static_configs: - - targets: [ '${OPENIM_SERVER_ADDRESS}:${MESSAGE_PROM_PORT}' ] + - targets: [ '${DOCKER_BRIDGE_GATEWAY}:${MESSAGE_PROM_PORT}' ] labels: namespace: 'default' - job_name: 'openimserver-openim-rpc-third' static_configs: - - targets: [ '${OPENIM_SERVER_ADDRESS}:${THIRD_PROM_PORT}' ] + - targets: [ '${DOCKER_BRIDGE_GATEWAY}:${THIRD_PROM_PORT}' ] labels: namespace: 'default' - job_name: 'openimserver-openim-rpc-user' static_configs: - - targets: [ '${OPENIM_SERVER_ADDRESS}:${USER_PROM_PORT}' ] + - targets: [ '${DOCKER_BRIDGE_GATEWAY}:${USER_PROM_PORT}' ] labels: namespace: 'default' From de451d4cea1d961464318b7b2cd0ee657bd8b37d Mon Sep 17 00:00:00 2001 From: Gordon <46924906+FGadvancer@users.noreply.github.com> Date: Mon, 25 Dec 2023 16:30:18 +0800 Subject: [PATCH 04/20] fix: online notifications do not push to herself. (#1534) * fix: online notifications do not push to herself. * fix: online notifications do not push to herself. * fix: online notifications do not push to herself. --- internal/push/push_handler.go | 11 ++++++----- internal/push/push_rpc_server.go | 3 +-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/push/push_handler.go b/internal/push/push_handler.go index a1a9ff08e..c91206ecc 100644 --- a/internal/push/push_handler.go +++ b/internal/push/push_handler.go @@ -67,13 +67,14 @@ func (c *ConsumerHandler) handleMs2PsChat(ctx context.Context, msg []byte) { case constant.SuperGroupChatType: err = c.pusher.Push2SuperGroup(ctx, pbData.MsgData.GroupID, pbData.MsgData) default: - var pushUserIDs []string - if pbData.MsgData.SendID != pbData.MsgData.RecvID { - pushUserIDs = []string{pbData.MsgData.SendID, pbData.MsgData.RecvID} + var pushUserIDList []string + isSenderSync := utils.GetSwitchFromOptions(pbData.MsgData.Options, constant.IsSenderSync) + if !isSenderSync || pbData.MsgData.SendID == pbData.MsgData.RecvID { + pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID) } else { - pushUserIDs = []string{pbData.MsgData.SendID} + pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID, pbData.MsgData.SendID) } - err = c.pusher.Push2User(ctx, pushUserIDs, pbData.MsgData) + err = c.pusher.Push2User(ctx, pushUserIDList, pbData.MsgData) } if err != nil { if err == errNoOfflinePusher { diff --git a/internal/push/push_rpc_server.go b/internal/push/push_rpc_server.go index 188ddc0e1..9e66f8f73 100644 --- a/internal/push/push_rpc_server.go +++ b/internal/push/push_rpc_server.go @@ -16,9 +16,8 @@ package push import ( "context" - "sync" - "github.com/OpenIMSDK/tools/utils" + "sync" "google.golang.org/grpc" From 7389639f174441ff66414e2e541e52aa138ba67f Mon Sep 17 00:00:00 2001 From: Brabem <69128477+luhaoling@users.noreply.github.com> Date: Tue, 26 Dec 2023 10:15:15 +0800 Subject: [PATCH 05/20] feat: add the notificationAccount (#1602) * feat: add notification API * fix: fix the script * fix: fix the error --- deployments/templates/openim.yaml | 8 ++ docs/contrib/environment.md | 74 ++++++++--------- go.mod | 2 + go.sum | 4 +- internal/api/friend.go | 3 - internal/api/msg.go | 5 +- internal/api/route.go | 6 +- internal/api/user.go | 15 +++- internal/rpc/friend/friend.go | 11 ++- internal/rpc/user/user.go | 128 ++++++++++++++++++++++++++++++ pkg/apistruct/msg.go | 2 +- pkg/authverify/token.go | 9 +++ pkg/common/config/config.go | 5 ++ pkg/common/db/controller/user.go | 6 ++ pkg/common/http/http_client.go | 2 +- pkg/rpcclient/user.go | 7 ++ scripts/check-all.sh | 2 +- scripts/install/environment.sh | 2 + 18 files changed, 238 insertions(+), 53 deletions(-) diff --git a/deployments/templates/openim.yaml b/deployments/templates/openim.yaml index ed5dc4fe1..cbb4a8c6e 100644 --- a/deployments/templates/openim.yaml +++ b/deployments/templates/openim.yaml @@ -247,6 +247,14 @@ manager: userID: [ "${MANAGER_USERID_1}", "${MANAGER_USERID_2}", "${MANAGER_USERID_3}" ] nickname: [ "${NICKNAME_1}", "${NICKNAME_2}", "${NICKNAME_3}" ] +# chatAdmin, use for send notification +# +# Built-in app system notification account ID +# Built-in app system notification account nickname +im-admin: + userID: [ "${IM_ADMIN_USERID}" ] + nickname: [ "${IM_ADMIN_NAME}" ] + # Multi-platform login policy # For each platform(Android, iOS, Windows, Mac, web), only one can be online at a time multiLoginPolicy: ${MULTILOGIN_POLICY} diff --git a/docs/contrib/environment.md b/docs/contrib/environment.md index 310c2df6a..e52d57235 100644 --- a/docs/contrib/environment.md +++ b/docs/contrib/environment.md @@ -453,43 +453,45 @@ This section involves configuring the log settings, including storage location, This section involves setting up additional configuration variables for Websocket, Push Notifications, and Chat. -| Parameter | Example Value | Description | -|-------------------------|-------------------|------------------------------------| -| WEBSOCKET_MAX_CONN_NUM | "100000" | Maximum Websocket connections | -| WEBSOCKET_MAX_MSG_LEN | "4096" | Maximum Websocket message length | -| WEBSOCKET_TIMEOUT | "10" | Websocket timeout | -| PUSH_ENABLE | "getui" | Push notification enable status | -| GETUI_PUSH_URL | [Generated URL] | GeTui Push Notification URL | -| GETUI_MASTER_SECRET | [User Defined] | GeTui Master Secret | -| GETUI_APP_KEY | [User Defined] | GeTui Application Key | -| GETUI_INTENT | [User Defined] | GeTui Push Intent | -| GETUI_CHANNEL_ID | [User Defined] | GeTui Channel ID | -| GETUI_CHANNEL_NAME | [User Defined] | GeTui Channel Name | -| FCM_SERVICE_ACCOUNT | "x.json" | FCM Service Account | -| JPNS_APP_KEY | [User Defined] | JPNS Application Key | -| JPNS_MASTER_SECRET | [User Defined] | JPNS Master Secret | -| JPNS_PUSH_URL | [User Defined] | JPNS Push Notification URL | -| JPNS_PUSH_INTENT | [User Defined] | JPNS Push Intent | -| MANAGER_USERID_1 | "openIM123456" | Administrator ID 1 | -| MANAGER_USERID_2 | "openIM654321" | Administrator ID 2 | -| MANAGER_USERID_3 | "openIMAdmin" | Administrator ID 3 | -| NICKNAME_1 | "system1" | Nickname 1 | -| NICKNAME_2 | "system2" | Nickname 2 | -| NICKNAME_3 | "system3" | Nickname 3 | -| MULTILOGIN_POLICY | "1" | Multi-login Policy | -| CHAT_PERSISTENCE_MYSQL | "true" | Chat Persistence in MySQL | -| MSG_CACHE_TIMEOUT | "86400" | Message Cache Timeout | -| GROUP_MSG_READ_RECEIPT | "true" | Group Message Read Receipt Enable | +| Parameter | Example Value | Description | +|-------------------------|-------------------|----------------------------------| +| WEBSOCKET_MAX_CONN_NUM | "100000" | Maximum Websocket connections | +| WEBSOCKET_MAX_MSG_LEN | "4096" | Maximum Websocket message length | +| WEBSOCKET_TIMEOUT | "10" | Websocket timeout | +| PUSH_ENABLE | "getui" | Push notification enable status | +| GETUI_PUSH_URL | [Generated URL] | GeTui Push Notification URL | +| GETUI_MASTER_SECRET | [User Defined] | GeTui Master Secret | +| GETUI_APP_KEY | [User Defined] | GeTui Application Key | +| GETUI_INTENT | [User Defined] | GeTui Push Intent | +| GETUI_CHANNEL_ID | [User Defined] | GeTui Channel ID | +| GETUI_CHANNEL_NAME | [User Defined] | GeTui Channel Name | +| FCM_SERVICE_ACCOUNT | "x.json" | FCM Service Account | +| JPNS_APP_KEY | [User Defined] | JPNS Application Key | +| JPNS_MASTER_SECRET | [User Defined] | JPNS Master Secret | +| JPNS_PUSH_URL | [User Defined] | JPNS Push Notification URL | +| JPNS_PUSH_INTENT | [User Defined] | JPNS Push Intent | +| MANAGER_USERID_1 | "openIM123456" | Administrator ID 1 | +| MANAGER_USERID_2 | "openIM654321" | Administrator ID 2 | +| MANAGER_USERID_3 | "openIMAdmin" | Administrator ID 3 | +| NICKNAME_1 | "system1" | Nickname 1 | +| NICKNAME_2 | "system2" | Nickname 2 | +| NICKNAME_3 | "system3" | Nickname 3 | +| IM_ADMIN_USERID | "imAdmin" | IM Administrator ID | +| IM_ADMIN_NAME | "imAdmin" | IM Administrator Nickname | +| MULTILOGIN_POLICY | "1" | Multi-login Policy | +| CHAT_PERSISTENCE_MYSQL | "true" | Chat Persistence in MySQL | +| MSG_CACHE_TIMEOUT | "86400" | Message Cache Timeout | +| GROUP_MSG_READ_RECEIPT | "true" | Group Message Read Receipt Enable | | SINGLE_MSG_READ_RECEIPT | "true" | Single Message Read Receipt Enable | -| RETAIN_CHAT_RECORDS | "365" | Retain Chat Records (in days) | -| CHAT_RECORDS_CLEAR_TIME | [Cron Expression] | Chat Records Clear Time | -| MSG_DESTRUCT_TIME | [Cron Expression] | Message Destruct Time | -| SECRET | "${PASSWORD}" | Secret Key | -| TOKEN_EXPIRE | "90" | Token Expiry Time | -| FRIEND_VERIFY | "false" | Friend Verification Enable | -| IOS_PUSH_SOUND | "xxx" | iOS | -| CALLBACK_ENABLE | "false" | Enable callback | -| CALLBACK_TIMEOUT | "5" | Maximum timeout for callback call | +| RETAIN_CHAT_RECORDS | "365" | Retain Chat Records (in days) | +| CHAT_RECORDS_CLEAR_TIME | [Cron Expression] | Chat Records Clear Time | +| MSG_DESTRUCT_TIME | [Cron Expression] | Message Destruct Time | +| SECRET | "${PASSWORD}" | Secret Key | +| TOKEN_EXPIRE | "90" | Token Expiry Time | +| FRIEND_VERIFY | "false" | Friend Verification Enable | +| IOS_PUSH_SOUND | "xxx" | iOS | +| CALLBACK_ENABLE | "false" | Enable callback | +| CALLBACK_TIMEOUT | "5" | Maximum timeout for callback call | | CALLBACK_FAILED_CONTINUE| "true" | fails to continue to the next step | ### 2.20. Prometheus Configuration diff --git a/go.mod b/go.mod index 9a9cde30e..fb5be5b90 100644 --- a/go.mod +++ b/go.mod @@ -156,3 +156,5 @@ require ( golang.org/x/crypto v0.14.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) + +replace github.com/OpenIMSDK/protocol v0.0.36 => github.com/luhaoling/protocol v0.0.0-20231222100538-d625562d53d5 diff --git a/go.sum b/go.sum index 61323c57b..cde33c7cd 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,6 @@ firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIw github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c= github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ= -github.com/OpenIMSDK/protocol v0.0.36 h1:UJnFsr1A4RrNeHMNDVS/1nvXWFoGM43dcXpZeJiIZ+0= -github.com/OpenIMSDK/protocol v0.0.36/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/OpenIMSDK/tools v0.0.21 h1:iTapc2mIEVH/xl5Nd6jfwPub11Pgp44tVcE1rjB3a48= github.com/OpenIMSDK/tools v0.0.21/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= @@ -227,6 +225,8 @@ github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205Ah github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw= github.com/lithammer/shortuuid v3.0.0+incompatible h1:NcD0xWW/MZYXEHa6ITy6kaXN5nwm/V115vj2YXfhS0w= github.com/lithammer/shortuuid v3.0.0+incompatible/go.mod h1:FR74pbAuElzOUuenUHTK2Tciko1/vKuIKS9dSkDrA4w= +github.com/luhaoling/protocol v0.0.0-20231222100538-d625562d53d5 h1:nmrJmAgQsCAxKgw109kaTcBV4rMWDRvqOson0ehw708= +github.com/luhaoling/protocol v0.0.0-20231222100538-d625562d53d5/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= diff --git a/internal/api/friend.go b/internal/api/friend.go index 58e7398dd..23f337a9f 100644 --- a/internal/api/friend.go +++ b/internal/api/friend.go @@ -92,6 +92,3 @@ func (o *FriendApi) GetFriendIDs(c *gin.Context) { func (o *FriendApi) GetSpecifiedFriendsInfo(c *gin.Context) { a2r.Call(friend.FriendClient.GetSpecifiedFriendsInfo, o.Client, c) } -func (o *FriendApi) SetPinFriends(c *gin.Context) { - a2r.Call(friend.FriendClient.PinFriends, o.Client, c) -} diff --git a/internal/api/msg.go b/internal/api/msg.go index 664ee505a..b6bfcebb6 100644 --- a/internal/api/msg.go +++ b/internal/api/msg.go @@ -169,9 +169,8 @@ func (m *MessageApi) getSendMsgReq(c *gin.Context, req apistruct.SendMsg) (sendM case constant.OANotification: data = apistruct.OANotificationElem{} req.SessionType = constant.NotificationChatType - if !authverify.IsManagerUserID(req.SendID) { - return nil, errs.ErrNoPermission. - Wrap("only app manager can as sender send OANotificationElem") + if err = m.userRpcClient.GetNotificationByID(c, req.SendID); err != nil { + return nil, err } default: return nil, errs.ErrArgs.WithDetail("not support err contentType") diff --git a/internal/api/route.go b/internal/api/route.go index 8bfea5cca..05aa6ac92 100644 --- a/internal/api/route.go +++ b/internal/api/route.go @@ -82,6 +82,10 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive userRouterGroup.POST("/process_user_command_delete", ParseToken, u.ProcessUserCommandDelete) userRouterGroup.POST("/process_user_command_update", ParseToken, u.ProcessUserCommandUpdate) userRouterGroup.POST("/process_user_command_get", ParseToken, u.ProcessUserCommandGet) + + userRouterGroup.POST("/add_notification_account", ParseToken, u.AddNotificationAccount) + userRouterGroup.POST("/update_notification_account", ParseToken, u.UpdateNotificationAccountInfo) + userRouterGroup.POST("/search_notification_account", ParseToken, u.SearchNotificationAccount) } // friend routing group friendRouterGroup := r.Group("/friend", ParseToken) @@ -103,7 +107,7 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive friendRouterGroup.POST("/is_friend", f.IsFriend) friendRouterGroup.POST("/get_friend_id", f.GetFriendIDs) friendRouterGroup.POST("/get_specified_friends_info", f.GetSpecifiedFriendsInfo) - friendRouterGroup.POST("/set_pin_friend", f.SetPinFriends) + //friendRouterGroup.POST("/set_pin_friend", f.SetPinFriends) } g := NewGroupApi(*groupRpc) groupRouterGroup := r.Group("/group", ParseToken) diff --git a/internal/api/user.go b/internal/api/user.go index 8350d1711..cf68fb422 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -15,8 +15,6 @@ package api import ( - "github.com/gin-gonic/gin" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/msggateway" "github.com/OpenIMSDK/protocol/user" @@ -24,6 +22,7 @@ import ( "github.com/OpenIMSDK/tools/apiresp" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" + "github.com/gin-gonic/gin" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" @@ -219,3 +218,15 @@ func (u *UserApi) ProcessUserCommandUpdate(c *gin.Context) { func (u *UserApi) ProcessUserCommandGet(c *gin.Context) { a2r.Call(user.UserClient.ProcessUserCommandGet, u.Client, c) } + +func (u *UserApi) AddNotificationAccount(c *gin.Context) { + a2r.Call(user.UserClient.AddNotificationAccount, u.Client, c) +} + +func (u *UserApi) UpdateNotificationAccountInfo(c *gin.Context) { + a2r.Call(user.UserClient.UpdateNotificationAccountInfo, u.Client, c) +} + +func (u *UserApi) SearchNotificationAccount(c *gin.Context) { + a2r.Call(user.UserClient.SearchNotificationAccount, u.Client, c) +} diff --git a/internal/rpc/friend/friend.go b/internal/rpc/friend/friend.go index 12203aa7e..97e53d197 100644 --- a/internal/rpc/friend/friend.go +++ b/internal/rpc/friend/friend.go @@ -53,6 +53,11 @@ type friendServer struct { RegisterCenter registry.SvcDiscoveryRegistry } +func (s *friendServer) UpdateFriends(ctx context.Context, req *pbfriend.UpdateFriendsReq) (*pbfriend.UpdateFriendsResp, error) { + //TODO implement me + panic("implement me") +} + func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { // Initialize MongoDB mongo, err := unrelation.NewMongo() @@ -438,8 +443,8 @@ func (s *friendServer) GetSpecifiedFriendsInfo(ctx context.Context, req *pbfrien } func (s *friendServer) PinFriends( ctx context.Context, - req *pbfriend.PinFriendsReq, -) (*pbfriend.PinFriendsResp, error) { + req *pbfriend.UpdateFriendsReq, +) (*pbfriend.UpdateFriendsResp, error) { if len(req.FriendUserIDs) == 0 { return nil, errs.ErrArgs.Wrap("friendIDList is empty") } @@ -465,6 +470,6 @@ func (s *friendServer) PinFriends( } } - resp := &pbfriend.PinFriendsResp{} + resp := &pbfriend.UpdateFriendsResp{} return resp, nil } diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index 70e3aebe6..9bc56298f 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -17,6 +17,7 @@ package user import ( "context" "errors" + "math/rand" "strings" "time" @@ -72,6 +73,12 @@ func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { for k, v := range config.Config.Manager.UserID { users = append(users, &tablerelation.UserModel{UserID: v, Nickname: config.Config.Manager.Nickname[k], AppMangerLevel: constant.AppAdmin}) } + if len(config.Config.IMAdmin.UserID) != len(config.Config.IMAdmin.Nickname) { + return errors.New("len(config.Config.AppNotificationAdmin.AppManagerUid) != len(config.Config.AppNotificationAdmin.Nickname)") + } + for k, v := range config.Config.IMAdmin.UserID { + users = append(users, &tablerelation.UserModel{UserID: v, Nickname: config.Config.IMAdmin.Nickname[k], AppMangerLevel: constant.AppNotificationAdmin}) + } userDB, err := mgo.NewUserMongo(mongo.GetDatabase()) if err != nil { return err @@ -390,3 +397,124 @@ func (s *userServer) ProcessUserCommandGet(ctx context.Context, req *pbuser.Proc // Return the response with the slice return &pbuser.ProcessUserCommandGetResp{KVArray: commandInfoSlice}, nil } + +func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.AddNotificationAccountReq) (*pbuser.AddNotificationAccountResp, error) { + if err := authverify.CheckIMAdmin(ctx); err != nil { + return nil, err + } + + var userID string + for i := 0; i < 20; i++ { + userId := s.genUserID() + _, err := s.UserDatabase.FindWithError(ctx, []string{userId}) + if err == nil { + continue + } + userID = userId + break + } + if userID == "" { + return nil, errs.ErrInternalServer.Wrap("gen user id failed") + } + + user := &tablerelation.UserModel{ + UserID: userID, + Nickname: req.NickName, + FaceURL: req.FaceURL, + CreateTime: time.Now(), + AppMangerLevel: constant.AppNotificationAdmin, + } + if err := s.UserDatabase.Create(ctx, []*tablerelation.UserModel{user}); err != nil { + return nil, err + } + + return &pbuser.AddNotificationAccountResp{}, nil +} + +func (s *userServer) UpdateNotificationAccountInfo(ctx context.Context, req *pbuser.UpdateNotificationAccountInfoReq) (*pbuser.UpdateNotificationAccountInfoResp, error) { + if err := authverify.CheckIMAdmin(ctx); err != nil { + return nil, err + } + + if _, err := s.UserDatabase.FindWithError(ctx, []string{req.UserID}); err != nil { + return nil, errs.ErrArgs.Wrap() + } + + user := map[string]interface{}{} + + if req.NickName != "" { + user["nickname"] = req.NickName + } + + if req.FaceURL != "" { + user["face_url"] = req.FaceURL + } + + if err := s.UserDatabase.UpdateByMap(ctx, req.UserID, user); err != nil { + return nil, err + } + + return &pbuser.UpdateNotificationAccountInfoResp{}, nil +} + +func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser.SearchNotificationAccountReq) (*pbuser.SearchNotificationAccountResp, error) { + if err := authverify.CheckIMAdmin(ctx); err != nil { + return nil, err + } + + _, users, err := s.UserDatabase.Page(ctx, req.Pagination) + if err != nil { + return nil, err + } + + var total int64 + accounts := make([]*pbuser.NotificationAccountInfo, 0, len(users)) + for _, v := range users { + if v.AppMangerLevel != constant.AppNotificationAdmin { + continue + } + temp := &pbuser.NotificationAccountInfo{ + UserID: v.UserID, + FaceURL: v.FaceURL, + NickName: v.Nickname, + } + accounts = append(accounts, temp) + total += 1 + } + return &pbuser.SearchNotificationAccountResp{Total: total, NotificationAccounts: accounts}, nil +} + +func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserInfoExReq) (*pbuser.UpdateUserInfoExResp, error) { + //TODO implement me + panic("implement me") +} + +func (s *userServer) GetNotificationAccount(ctx context.Context, req *pbuser.GetNotificationAccountReq) (*pbuser.GetNotificationAccountResp, error) { + if req.UserID == "" { + return nil, errs.ErrArgs.Wrap("userID is empty") + } + user, err := s.UserDatabase.GetUserByID(ctx, req.UserID) + if err != nil { + return nil, errs.ErrUserIDNotFound.Wrap() + } + if user.AppMangerLevel == constant.AppAdmin || user.AppMangerLevel == constant.AppNotificationAdmin { + return &pbuser.GetNotificationAccountResp{}, nil + } + + return nil, errs.ErrNoPermission.Wrap("notification messages cannot be sent for this ID") +} + +func (s *userServer) genUserID() string { + const l = 10 + data := make([]byte, l) + rand.Read(data) + chars := []byte("0123456789") + for i := 0; i < len(data); i++ { + if i == 0 { + data[i] = chars[1:][data[i]%9] + } else { + data[i] = chars[data[i]%10] + } + } + return string(data) +} diff --git a/pkg/apistruct/msg.go b/pkg/apistruct/msg.go index 4b7bd1e6f..d23db9bf5 100644 --- a/pkg/apistruct/msg.go +++ b/pkg/apistruct/msg.go @@ -87,7 +87,7 @@ type OANotificationElem struct { 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" validate:"required"` + MixType int32 `mapstructure:"mixType" json:"mixType"` PictureElem *PictureElem `mapstructure:"pictureElem" json:"pictureElem"` SoundElem *SoundElem `mapstructure:"soundElem" json:"soundElem"` VideoElem *VideoElem `mapstructure:"videoElem" json:"videoElem"` diff --git a/pkg/authverify/token.go b/pkg/authverify/token.go index fd01e8c5a..d9aa0dbb1 100644 --- a/pkg/authverify/token.go +++ b/pkg/authverify/token.go @@ -54,6 +54,15 @@ func CheckAdmin(ctx context.Context) error { } return errs.ErrNoPermission.Wrap(fmt.Sprintf("user %s is not admin userID", mcontext.GetOpUserID(ctx))) } +func CheckIMAdmin(ctx context.Context) error { + if utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID) { + return nil + } + if utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID) { + return nil + } + return errs.ErrNoPermission.Wrap(fmt.Sprintf("user %s is not CheckIMAdmin userID", mcontext.GetOpUserID(ctx))) +} func ParseRedisInterfaceToken(redisToken any) (*tokenverify.Claims, error) { return tokenverify.GetClaimFromToken(string(redisToken.([]uint8)), Secret()) diff --git a/pkg/common/config/config.go b/pkg/common/config/config.go index 6da89fc8f..707601234 100644 --- a/pkg/common/config/config.go +++ b/pkg/common/config/config.go @@ -236,6 +236,11 @@ type configStruct struct { Nickname []string `yaml:"nickname"` } `yaml:"manager"` + IMAdmin struct { + UserID []string `yaml:"userID"` + Nickname []string `yaml:"nickname"` + } `yaml:"im-admin"` + MultiLoginPolicy int `yaml:"multiLoginPolicy"` ChatPersistenceMysql bool `yaml:"chatPersistenceMysql"` MsgCacheTimeout int `yaml:"msgCacheTimeout"` diff --git a/pkg/common/db/controller/user.go b/pkg/common/db/controller/user.go index 433fb7ad3..2fafcf266 100644 --- a/pkg/common/db/controller/user.go +++ b/pkg/common/db/controller/user.go @@ -50,6 +50,8 @@ type UserDatabase interface { IsExist(ctx context.Context, userIDs []string) (exist bool, err error) // GetAllUserID Get all user IDs GetAllUserID(ctx context.Context, pagination pagination.Pagination) (int64, []string, error) + // Get user by userID + GetUserByID(ctx context.Context, userID string) (user *relation.UserModel, err error) // InitOnce Inside the function, first query whether it exists in the db, if it exists, do nothing; if it does not exist, insert it InitOnce(ctx context.Context, users []*relation.UserModel) (err error) // CountTotal Get the total number of users @@ -183,6 +185,10 @@ func (u *userDatabase) GetAllUserID(ctx context.Context, pagination pagination.P return u.userDB.GetAllUserID(ctx, pagination) } +func (u *userDatabase) GetUserByID(ctx context.Context, userID string) (user *relation.UserModel, err error) { + return u.userDB.Take(ctx, userID) +} + // CountTotal Get the total number of users. func (u *userDatabase) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) { return u.userDB.CountTotal(ctx, before) diff --git a/pkg/common/http/http_client.go b/pkg/common/http/http_client.go index baad60459..a80d1c9a4 100644 --- a/pkg/common/http/http_client.go +++ b/pkg/common/http/http_client.go @@ -112,7 +112,6 @@ func callBackPostReturn(ctx context.Context, url, command string, input interfac //v.Set(constant.CallbackCommand, command) //url = url + "/" + v.Encode() url = url + "/" + command - b, err := Post(ctx, url, nil, input, callbackConfig.CallbackTimeOut) if err != nil { if callbackConfig.CallbackFailedContinue != nil && *callbackConfig.CallbackFailedContinue { @@ -121,6 +120,7 @@ func callBackPostReturn(ctx context.Context, url, command string, input interfac } return errs.ErrNetwork.Wrap(err.Error()) } + defer log.ZDebug(ctx, "callback", "data", string(b)) if err = json.Unmarshal(b, output); err != nil { if callbackConfig.CallbackFailedContinue != nil && *callbackConfig.CallbackFailedContinue { diff --git a/pkg/rpcclient/user.go b/pkg/rpcclient/user.go index c40d95727..de633ee30 100644 --- a/pkg/rpcclient/user.go +++ b/pkg/rpcclient/user.go @@ -179,3 +179,10 @@ func (u *UserRpcClient) SetUserStatus(ctx context.Context, userID string, status }) return err } + +func (u *UserRpcClient) GetNotificationByID(ctx context.Context, userID string) error { + _, err := u.Client.GetNotificationAccount(ctx, &user.GetNotificationAccountReq{ + UserID: userID, + }) + return err +} diff --git a/scripts/check-all.sh b/scripts/check-all.sh index efaa2ccb7..e1e07bd65 100755 --- a/scripts/check-all.sh +++ b/scripts/check-all.sh @@ -89,4 +89,4 @@ else echo "++++ Check all openim service ports successfully !" fi -set -e +set -e \ No newline at end of file diff --git a/scripts/install/environment.sh b/scripts/install/environment.sh index 3dd062af6..feb36c3c1 100755 --- a/scripts/install/environment.sh +++ b/scripts/install/environment.sh @@ -353,6 +353,8 @@ def "MANAGER_USERID_3" "openIMAdmin" # 管理员ID 3 def "NICKNAME_1" "system1" # 昵称1 def "NICKNAME_2" "system2" # 昵称2 def "NICKNAME_3" "system3" # 昵称3 +def "IM_ADMIN_USERID" "imAdmin" # IM管理员ID +def "IM_ADMIN_NAME" "imAdmin" # IM管理员昵称 def "MULTILOGIN_POLICY" "1" # 多登录策略 def "CHAT_PERSISTENCE_MYSQL" "true" # 聊天持久化MySQL def "MSG_CACHE_TIMEOUT" "86400" # 消息缓存超时 From 47dd6b17f61736686dabf3798888aaa7060d75a8 Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751@qq.com> Date: Tue, 26 Dec 2023 12:16:55 +0800 Subject: [PATCH 06/20] Update openimci.yml (#1610) * Update openimci.yml * Update Makefile --- .github/workflows/openimci.yml | 8 +++++++- Makefile | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/openimci.yml b/.github/workflows/openimci.yml index d65c0dc89..d8e988f0b 100644 --- a/.github/workflows/openimci.yml +++ b/.github/workflows/openimci.yml @@ -207,6 +207,12 @@ jobs: sudo make check || \ (echo "An error occurred, printing logs:" && sudo cat ./_output/logs/* 2>/dev/null) + - name: Restart Services and Print Logs for Ubuntu + if: runner.os == 'Linux' + run: | + sudo make restart + sudo make check + # - name: Build, Start, Check Services and Print Logs for macOS # if: runner.os == 'macOS' # run: | @@ -239,4 +245,4 @@ jobs: - name: Test Docker Build run: | sudo make init - sudo make image \ No newline at end of file + sudo make image diff --git a/Makefile b/Makefile index 1941bde6c..4faf1c21d 100644 --- a/Makefile +++ b/Makefile @@ -95,7 +95,7 @@ stop: ## restart: Restart openim (make init configuration file is initialized) ✨ .PHONY: restart -restart: clean stop build init start check +restart: clean stop build start check ## multiarch: Build binaries for multiple platforms. See option PLATFORMS. ✨ .PHONY: multiarch From cd1235fb32072af665808d3bffd6f782a6643dd3 Mon Sep 17 00:00:00 2001 From: chao <48119764+withchao@users.noreply.github.com> Date: Tue, 26 Dec 2023 17:29:42 +0800 Subject: [PATCH 07/20] feat: s3 FormData upload (#1614) * upgrade package and rtc convert * upgrade package and rtc convert * upgrade package and rtc convert * upgrade package and rtc convert * friend user * s3 form data * s3 form data * s3 form data * s3 form data * s3 form data * s3 form data * s3 form data * s3 form data * s3 form data --- go.mod | 8 +- go.sum | 8 +- internal/api/route.go | 2 + internal/api/third.go | 8 ++ internal/rpc/friend/friend.go | 3 +- internal/rpc/third/s3.go | 113 ++++++++++++++++++++++ internal/rpc/third/third.go | 10 -- internal/rpc/third/tool.go | 3 + internal/rpc/user/user.go | 9 +- pkg/common/cmd/root.go | 2 +- pkg/common/db/controller/s3.go | 10 ++ pkg/common/db/s3/cont/consts.go | 1 + pkg/common/db/s3/cont/controller.go | 4 + pkg/common/db/s3/cos/cos.go | 69 +++++++++++++ pkg/common/db/s3/minio/minio.go | 50 ++++++++++ pkg/common/db/s3/oss/oss.go | 49 ++++++++++ pkg/common/db/s3/s3.go | 11 +++ pkg/common/ginprometheus/ginprometheus.go | 2 +- 18 files changed, 334 insertions(+), 28 deletions(-) diff --git a/go.mod b/go.mod index fb5be5b90..a753cae51 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,8 @@ go 1.19 require ( firebase.google.com/go v3.13.0+incompatible + github.com/OpenIMSDK/protocol v0.0.39 + github.com/OpenIMSDK/tools v0.0.21 github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/dtm-labs/rockscache v0.1.1 github.com/gin-gonic/gin v1.9.1 @@ -33,8 +35,6 @@ require github.com/google/uuid v1.3.1 require ( github.com/IBM/sarama v1.41.3 - github.com/OpenIMSDK/protocol v0.0.36 - github.com/OpenIMSDK/tools v0.0.21 github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible github.com/go-redis/redis v6.15.9+incompatible github.com/redis/go-redis/v9 v9.2.1 @@ -133,7 +133,7 @@ require ( golang.org/x/oauth2 v0.13.0 // indirect golang.org/x/sys v0.14.0 // indirect golang.org/x/text v0.13.0 // indirect - golang.org/x/time v0.3.0 // indirect + golang.org/x/time v0.5.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect @@ -156,5 +156,3 @@ require ( golang.org/x/crypto v0.14.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) - -replace github.com/OpenIMSDK/protocol v0.0.36 => github.com/luhaoling/protocol v0.0.0-20231222100538-d625562d53d5 diff --git a/go.sum b/go.sum index cde33c7cd..a4609f6f2 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIw github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c= github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ= +github.com/OpenIMSDK/protocol v0.0.39 h1:DfvFcNGBcfj2vtT7W3uw4U/ipnI7NecTzQdlSYGuQz8= +github.com/OpenIMSDK/protocol v0.0.39/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/OpenIMSDK/tools v0.0.21 h1:iTapc2mIEVH/xl5Nd6jfwPub11Pgp44tVcE1rjB3a48= github.com/OpenIMSDK/tools v0.0.21/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= @@ -225,8 +227,6 @@ github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205Ah github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw= github.com/lithammer/shortuuid v3.0.0+incompatible h1:NcD0xWW/MZYXEHa6ITy6kaXN5nwm/V115vj2YXfhS0w= github.com/lithammer/shortuuid v3.0.0+incompatible/go.mod h1:FR74pbAuElzOUuenUHTK2Tciko1/vKuIKS9dSkDrA4w= -github.com/luhaoling/protocol v0.0.0-20231222100538-d625562d53d5 h1:nmrJmAgQsCAxKgw109kaTcBV4rMWDRvqOson0ehw708= -github.com/luhaoling/protocol v0.0.0-20231222100538-d625562d53d5/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= @@ -453,8 +453,8 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= diff --git a/internal/api/route.go b/internal/api/route.go index 05aa6ac92..8fb372587 100644 --- a/internal/api/route.go +++ b/internal/api/route.go @@ -171,6 +171,8 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive objectGroup.POST("/auth_sign", t.AuthSign) objectGroup.POST("/complete_multipart_upload", t.CompleteMultipartUpload) objectGroup.POST("/access_url", t.AccessURL) + objectGroup.POST("/initiate_form_data", t.InitiateFormData) + objectGroup.POST("/complete_form_data", t.CompleteFormData) objectGroup.GET("/*name", t.ObjectRedirect) } // Message diff --git a/internal/api/third.go b/internal/api/third.go index 5191903da..37ec55098 100644 --- a/internal/api/third.go +++ b/internal/api/third.go @@ -71,6 +71,14 @@ func (o *ThirdApi) AccessURL(c *gin.Context) { a2r.Call(third.ThirdClient.AccessURL, o.Client, c) } +func (o *ThirdApi) InitiateFormData(c *gin.Context) { + a2r.Call(third.ThirdClient.InitiateFormData, o.Client, c) +} + +func (o *ThirdApi) CompleteFormData(c *gin.Context) { + a2r.Call(third.ThirdClient.CompleteFormData, o.Client, c) +} + func (o *ThirdApi) ObjectRedirect(c *gin.Context) { name := c.Param("name") if name == "" { diff --git a/internal/rpc/friend/friend.go b/internal/rpc/friend/friend.go index 97e53d197..c40b566f3 100644 --- a/internal/rpc/friend/friend.go +++ b/internal/rpc/friend/friend.go @@ -54,8 +54,7 @@ type friendServer struct { } func (s *friendServer) UpdateFriends(ctx context.Context, req *pbfriend.UpdateFriendsReq) (*pbfriend.UpdateFriendsResp, error) { - //TODO implement me - panic("implement me") + return nil, errs.ErrInternalServer.Wrap("not implemented") } func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { diff --git a/internal/rpc/third/s3.go b/internal/rpc/third/s3.go index ca826e805..2c230f258 100644 --- a/internal/rpc/third/s3.go +++ b/internal/rpc/third/s3.go @@ -16,6 +16,12 @@ package third import ( "context" + "encoding/base64" + "encoding/hex" + "encoding/json" + "github.com/google/uuid" + "github.com/openimsdk/open-im-server/v3/pkg/authverify" + "path" "strconv" "time" @@ -179,6 +185,113 @@ func (t *thirdServer) AccessURL(ctx context.Context, req *third.AccessURLReq) (* }, nil } +func (t *thirdServer) InitiateFormData(ctx context.Context, req *third.InitiateFormDataReq) (*third.InitiateFormDataResp, error) { + if req.Name == "" { + return nil, errs.ErrArgs.Wrap("name is empty") + } + if req.Size <= 0 { + return nil, errs.ErrArgs.Wrap("size must be greater than 0") + } + if err := checkUploadName(ctx, req.Name); err != nil { + return nil, err + } + var duration time.Duration + opUserID := mcontext.GetOpUserID(ctx) + var key string + if authverify.IsManagerUserID(opUserID) { + if req.Millisecond <= 0 { + duration = time.Minute * 10 + } else { + duration = time.Millisecond * time.Duration(req.Millisecond) + } + if req.Absolute { + key = req.Name + } + } else { + duration = time.Minute * 10 + } + uid, err := uuid.NewRandom() + if err != nil { + return nil, err + } + if key == "" { + date := time.Now().Format("20060102") + key = path.Join(cont.DirectPath, date, opUserID, hex.EncodeToString(uid[:])+path.Ext(req.Name)) + } + mate := FormDataMate{ + Name: req.Name, + Size: req.Size, + ContentType: req.ContentType, + Group: req.Group, + Key: key, + } + mateData, err := json.Marshal(&mate) + if err != nil { + return nil, err + } + resp, err := t.s3dataBase.FormData(ctx, key, req.Size, req.ContentType, duration) + if err != nil { + return nil, err + } + return &third.InitiateFormDataResp{ + Id: base64.RawStdEncoding.EncodeToString(mateData), + Url: resp.URL, + File: resp.File, + Header: toPbMapArray(resp.Header), + FormData: resp.FormData, + Expires: resp.Expires.UnixMilli(), + SuccessCodes: utils.Slice(resp.SuccessCodes, func(code int) int32 { + return int32(code) + }), + }, nil +} + +func (t *thirdServer) CompleteFormData(ctx context.Context, req *third.CompleteFormDataReq) (*third.CompleteFormDataResp, error) { + if req.Id == "" { + return nil, errs.ErrArgs.Wrap("id is empty") + } + data, err := base64.RawStdEncoding.DecodeString(req.Id) + if err != nil { + return nil, errs.ErrArgs.Wrap("invalid id " + err.Error()) + } + var mate FormDataMate + if err := json.Unmarshal(data, &mate); err != nil { + return nil, errs.ErrArgs.Wrap("invalid id " + err.Error()) + } + if err := checkUploadName(ctx, mate.Name); err != nil { + return nil, err + } + info, err := t.s3dataBase.StatObject(ctx, mate.Key) + if err != nil { + return nil, err + } + if info.Size > 0 && info.Size != mate.Size { + return nil, errs.ErrData.Wrap("file size mismatch") + } + obj := &relation.ObjectModel{ + Name: mate.Name, + UserID: mcontext.GetOpUserID(ctx), + Hash: "etag_" + info.ETag, + Key: info.Key, + Size: info.Size, + ContentType: mate.ContentType, + Group: mate.Group, + CreateTime: time.Now(), + } + if err := t.s3dataBase.SetObject(ctx, obj); err != nil { + return nil, err + } + return &third.CompleteFormDataResp{Url: t.apiAddress(mate.Name)}, nil +} + func (t *thirdServer) apiAddress(name string) string { return t.apiURL + name } + +type FormDataMate struct { + Name string `json:"name"` + Size int64 `json:"size"` + ContentType string `json:"contentType"` + Group string `json:"group"` + Key string `json:"key"` +} diff --git a/internal/rpc/third/third.go b/internal/rpc/third/third.go index 35df3f925..7a63d3526 100644 --- a/internal/rpc/third/third.go +++ b/internal/rpc/third/third.go @@ -101,16 +101,6 @@ type thirdServer struct { defaultExpire time.Duration } -func (t *thirdServer) InitiateFormData(ctx context.Context, req *third.InitiateFormDataReq) (*third.InitiateFormDataResp, error) { - //TODO implement me - panic("implement me") -} - -func (t *thirdServer) CompleteFormData(ctx context.Context, req *third.CompleteFormDataReq) (*third.CompleteFormDataResp, error) { - //TODO implement me - panic("implement me") -} - 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 { diff --git a/internal/rpc/third/tool.go b/internal/rpc/third/tool.go index a65d882dd..a6c16ff9d 100644 --- a/internal/rpc/third/tool.go +++ b/internal/rpc/third/tool.go @@ -29,6 +29,9 @@ import ( ) func toPbMapArray(m map[string][]string) []*third.KeyValues { + if len(m) == 0 { + return nil + } res := make([]*third.KeyValues, 0, len(m)) for key := range m { res = append(res, &third.KeyValues{ diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index 9bc56298f..b5ad186a5 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -57,6 +57,10 @@ type userServer struct { RegisterCenter registry.SvcDiscoveryRegistry } +func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserInfoExReq) (*pbuser.UpdateUserInfoExResp, error) { + return nil, errs.ErrInternalServer.Wrap("not implemented") +} + func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { rdb, err := cache.NewRedis() if err != nil { @@ -484,11 +488,6 @@ func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser. return &pbuser.SearchNotificationAccountResp{Total: total, NotificationAccounts: accounts}, nil } -func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserInfoExReq) (*pbuser.UpdateUserInfoExResp, error) { - //TODO implement me - panic("implement me") -} - func (s *userServer) GetNotificationAccount(ctx context.Context, req *pbuser.GetNotificationAccountReq) (*pbuser.GetNotificationAccountResp, error) { if req.UserID == "" { return nil, errs.ErrArgs.Wrap("userID is empty") diff --git a/pkg/common/cmd/root.go b/pkg/common/cmd/root.go index 0bc308e07..66bec61a7 100644 --- a/pkg/common/cmd/root.go +++ b/pkg/common/cmd/root.go @@ -45,7 +45,7 @@ type CmdOpts struct { func WithCronTaskLogName() func(*CmdOpts) { return func(opts *CmdOpts) { - opts.loggerPrefixName = "OpenIM.CronTask.log.all" + opts.loggerPrefixName = "openim.crontask.log.all" } } diff --git a/pkg/common/db/controller/s3.go b/pkg/common/db/controller/s3.go index 6916a7d30..95505de41 100644 --- a/pkg/common/db/controller/s3.go +++ b/pkg/common/db/controller/s3.go @@ -35,6 +35,8 @@ type S3Database interface { CompleteMultipartUpload(ctx context.Context, uploadID string, parts []string) (*cont.UploadResult, error) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (time.Time, string, error) SetObject(ctx context.Context, info *relation.ObjectModel) error + StatObject(ctx context.Context, name string) (*s3.ObjectInfo, error) + FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*s3.FormData, error) } func NewS3Database(rdb redis.UniversalClient, s3 s3.Interface, obj relation.ObjectInfoModelInterface) S3Database { @@ -100,3 +102,11 @@ func (s *s3Database) AccessURL(ctx context.Context, name string, expire time.Dur } return expireTime, rawURL, nil } + +func (s *s3Database) StatObject(ctx context.Context, name string) (*s3.ObjectInfo, error) { + return s.s3.StatObject(ctx, name) +} + +func (s *s3Database) FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*s3.FormData, error) { + return s.s3.FormData(ctx, name, size, contentType, duration) +} diff --git a/pkg/common/db/s3/cont/consts.go b/pkg/common/db/s3/cont/consts.go index 1a0467ce5..a01a8312c 100644 --- a/pkg/common/db/s3/cont/consts.go +++ b/pkg/common/db/s3/cont/consts.go @@ -17,6 +17,7 @@ package cont const ( hashPath = "openim/data/hash/" tempPath = "openim/temp/" + DirectPath = "openim/direct" UploadTypeMultipart = 1 // 分片上传 UploadTypePresigned = 2 // 预签名上传 partSeparator = "," diff --git a/pkg/common/db/s3/cont/controller.go b/pkg/common/db/s3/cont/controller.go index 1bf1a4b12..82c27c1f2 100644 --- a/pkg/common/db/s3/cont/controller.go +++ b/pkg/common/db/s3/cont/controller.go @@ -279,3 +279,7 @@ func (c *Controller) AccessURL(ctx context.Context, name string, expire time.Dur } return c.impl.AccessURL(ctx, name, expire, opt) } + +func (c *Controller) FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*s3.FormData, error) { + return c.impl.FormData(ctx, name, size, contentType, duration) +} diff --git a/pkg/common/db/s3/cos/cos.go b/pkg/common/db/s3/cos/cos.go index 7add88487..7d2c0befe 100644 --- a/pkg/common/db/s3/cos/cos.go +++ b/pkg/common/db/s3/cos/cos.go @@ -16,6 +16,11 @@ package cos import ( "context" + "crypto/hmac" + "crypto/sha1" + "encoding/base64" + "encoding/hex" + "encoding/json" "errors" "fmt" "net/http" @@ -44,6 +49,8 @@ const ( imageWebp = "webp" ) +const successCode = http.StatusOK + const ( videoSnapshotImagePng = "png" videoSnapshotImageJpg = "jpg" @@ -326,3 +333,65 @@ func (c *Cos) getPresignedURL(ctx context.Context, name string, expire time.Dura } return c.client.Object.GetObjectURL(name), nil } + +func (c *Cos) FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*s3.FormData, error) { + // https://cloud.tencent.com/document/product/436/14690 + now := time.Now() + expiration := now.Add(duration) + keyTime := fmt.Sprintf("%d;%d", now.Unix(), expiration.Unix()) + conditions := []any{ + map[string]string{"q-sign-algorithm": "sha1"}, + map[string]string{"q-ak": c.credential.SecretID}, + map[string]string{"q-sign-time": keyTime}, + map[string]string{"key": name}, + } + if contentType != "" { + conditions = append(conditions, map[string]string{"Content-Type": contentType}) + } + policy := map[string]any{ + "expiration": expiration.Format("2006-01-02T15:04:05.000Z"), + "conditions": conditions, + } + policyJson, err := json.Marshal(policy) + if err != nil { + return nil, err + } + signKey := hmacSha1val(c.credential.SecretKey, keyTime) + strToSign := sha1val(string(policyJson)) + signature := hmacSha1val(signKey, strToSign) + + fd := &s3.FormData{ + URL: c.client.BaseURL.BucketURL.String(), + File: "file", + Expires: expiration, + FormData: map[string]string{ + "policy": base64.StdEncoding.EncodeToString(policyJson), + "q-sign-algorithm": "sha1", + "q-ak": c.credential.SecretID, + "q-key-time": keyTime, + "q-signature": signature, + "key": name, + "success_action_status": strconv.Itoa(successCode), + }, + SuccessCodes: []int{successCode}, + } + if contentType != "" { + fd.FormData["Content-Type"] = contentType + } + if c.credential.SessionToken != "" { + fd.FormData["x-cos-security-token"] = c.credential.SessionToken + } + return fd, nil +} + +func hmacSha1val(key, msg string) string { + v := hmac.New(sha1.New, []byte(key)) + v.Write([]byte(msg)) + return hex.EncodeToString(v.Sum(nil)) +} + +func sha1val(msg string) string { + sha1Hash := sha1.New() + sha1Hash.Write([]byte(msg)) + return hex.EncodeToString(sha1Hash.Sum(nil)) +} diff --git a/pkg/common/db/s3/minio/minio.go b/pkg/common/db/s3/minio/minio.go index be49e2faa..7dfe35b51 100644 --- a/pkg/common/db/s3/minio/minio.go +++ b/pkg/common/db/s3/minio/minio.go @@ -57,6 +57,8 @@ const ( imageThumbnailPath = "openim/thumbnail" ) +const successCode = http.StatusOK + func NewMinio(cache cache.MinioCache) (s3.Interface, error) { u, err := url.Parse(config.Config.Object.Minio.Endpoint) if err != nil { @@ -441,3 +443,51 @@ func (m *Minio) getObjectData(ctx context.Context, name string, limit int64) ([] } return io.ReadAll(io.LimitReader(object, limit)) } + +func (m *Minio) FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*s3.FormData, error) { + if err := m.initMinio(ctx); err != nil { + return nil, err + } + policy := minio.NewPostPolicy() + if err := policy.SetKey(name); err != nil { + return nil, err + } + expires := time.Now().Add(duration) + if err := policy.SetExpires(expires); err != nil { + return nil, err + } + if size > 0 { + if err := policy.SetContentLengthRange(0, size); err != nil { + return nil, err + } + } + if err := policy.SetSuccessStatusAction(strconv.Itoa(successCode)); err != nil { + return nil, err + } + if contentType != "" { + if err := policy.SetContentType(contentType); err != nil { + return nil, err + } + } + if err := policy.SetBucket(m.bucket); err != nil { + return nil, err + } + u, fd, err := m.core.PresignedPostPolicy(ctx, policy) + if err != nil { + return nil, err + } + sign, err := url.Parse(m.signEndpoint) + if err != nil { + return nil, err + } + u.Scheme = sign.Scheme + u.Host = sign.Host + return &s3.FormData{ + URL: u.String(), + File: "file", + Header: nil, + FormData: fd, + Expires: expires, + SuccessCodes: []int{successCode}, + }, nil +} diff --git a/pkg/common/db/s3/oss/oss.go b/pkg/common/db/s3/oss/oss.go index 6a728127b..8fa2a538e 100644 --- a/pkg/common/db/s3/oss/oss.go +++ b/pkg/common/db/s3/oss/oss.go @@ -16,8 +16,13 @@ package oss import ( "context" + "crypto/hmac" + "crypto/sha1" + "encoding/base64" + "encoding/json" "errors" "fmt" + "io" "net/http" "net/url" "reflect" @@ -45,6 +50,8 @@ const ( imageWebp = "webp" ) +const successCode = http.StatusOK + const ( videoSnapshotImagePng = "png" videoSnapshotImageJpg = "jpg" @@ -327,3 +334,45 @@ func (o *OSS) AccessURL(ctx context.Context, name string, expire time.Duration, params := getURLParams(*o.bucket.Client.Conn, rawParams) return getURL(o.um, o.bucket.BucketName, name, params).String(), nil } + +func (o *OSS) FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*s3.FormData, error) { + // https://help.aliyun.com/zh/oss/developer-reference/postobject?spm=a2c4g.11186623.0.0.1cb83cebkP55nn + expires := time.Now().Add(duration) + conditions := []any{ + map[string]string{"bucket": o.bucket.BucketName}, + map[string]string{"key": name}, + } + if size > 0 { + conditions = append(conditions, []any{"content-length-range", 0, size}) + } + policy := map[string]any{ + "expiration": expires.Format("2006-01-02T15:04:05.000Z"), + "conditions": conditions, + } + policyJson, err := json.Marshal(policy) + if err != nil { + return nil, err + } + policyStr := base64.StdEncoding.EncodeToString(policyJson) + h := hmac.New(sha1.New, []byte(o.credentials.GetAccessKeySecret())) + if _, err := io.WriteString(h, policyStr); err != nil { + return nil, err + } + fd := &s3.FormData{ + URL: o.bucketURL, + File: "file", + Expires: expires, + FormData: map[string]string{ + "key": name, + "policy": policyStr, + "OSSAccessKeyId": o.credentials.GetAccessKeyID(), + "success_action_status": strconv.Itoa(successCode), + "signature": base64.StdEncoding.EncodeToString(h.Sum(nil)), + }, + SuccessCodes: []int{successCode}, + } + if contentType != "" { + fd.FormData["x-oss-content-type"] = contentType + } + return fd, nil +} diff --git a/pkg/common/db/s3/s3.go b/pkg/common/db/s3/s3.go index afbe91955..0352004b5 100644 --- a/pkg/common/db/s3/s3.go +++ b/pkg/common/db/s3/s3.go @@ -74,6 +74,15 @@ type CopyObjectInfo struct { ETag string `json:"etag"` } +type FormData struct { + URL string `json:"url"` + File string `json:"file"` + Header http.Header `json:"header"` + FormData map[string]string `json:"form"` + Expires time.Time `json:"expires"` + SuccessCodes []int `json:"successActionStatus"` +} + type SignPart struct { PartNumber int `json:"partNumber"` URL string `json:"url"` @@ -152,4 +161,6 @@ type Interface interface { 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) + + FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*FormData, error) } diff --git a/pkg/common/ginprometheus/ginprometheus.go b/pkg/common/ginprometheus/ginprometheus.go index f116fc23a..1ee8f8e34 100644 --- a/pkg/common/ginprometheus/ginprometheus.go +++ b/pkg/common/ginprometheus/ginprometheus.go @@ -432,7 +432,7 @@ func computeApproximateRequestSize(r *http.Request) int { } s += len(r.Host) - // r.Form and r.MultipartForm are assumed to be included in r.URL. + // r.FormData and r.MultipartForm are assumed to be included in r.URL. if r.ContentLength != -1 { s += int(r.ContentLength) From 222064542927b643ed00d6edd7cd0ec9114a84e9 Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751@qq.com> Date: Tue, 26 Dec 2023 18:03:07 +0800 Subject: [PATCH 08/20] Update README.md (#1615) --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index a2c5fc732..781db1217 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,10 @@ It's crafted in Golang and supports cross-platform deployment, ensuring a cohere ## :rocket: Quick Start +We support many platforms. Here are the addresses for quick experience on the web side: + +👉 **[OpenIM online web demo](https://web-enterprise.rentsoft.cn/)** + You can quickly learn OpenIM engineering solutions, all it takes is one simple command: ```bash From cff90a3099f8784c49f616fa1884e71505c305d5 Mon Sep 17 00:00:00 2001 From: Brabem <69128477+luhaoling@users.noreply.github.com> Date: Thu, 28 Dec 2023 00:29:44 +0800 Subject: [PATCH 09/20] fix: fix the searchNotificationAccout by userID or nickname (#1617) * feat: add notification API * fix: fix the script * fix: fix the error * fix: fix the searchNotificationAccount * fix: fix the protocol version * fix: fix the proto version --- go.mod | 4 +- go.sum | 4 +- internal/rpc/user/user.go | 62 +++++++++++++++++++--------- pkg/common/db/controller/user.go | 7 ++++ pkg/common/db/mgo/user.go | 4 ++ pkg/common/db/table/relation/user.go | 1 + 6 files changed, 60 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index a753cae51..1b80650c5 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( firebase.google.com/go v3.13.0+incompatible - github.com/OpenIMSDK/protocol v0.0.39 + github.com/OpenIMSDK/protocol v0.0.40 github.com/OpenIMSDK/tools v0.0.21 github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/dtm-labs/rockscache v0.1.1 @@ -156,3 +156,5 @@ require ( golang.org/x/crypto v0.14.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) + +replace github.com/OpenIMSDK/protocol v0.0.40 => github.com/luhaoling/protocol v0.0.0-20231227040641-2f934a0d64a3 diff --git a/go.sum b/go.sum index a4609f6f2..09e4ce06b 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,6 @@ firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIw github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c= github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ= -github.com/OpenIMSDK/protocol v0.0.39 h1:DfvFcNGBcfj2vtT7W3uw4U/ipnI7NecTzQdlSYGuQz8= -github.com/OpenIMSDK/protocol v0.0.39/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/OpenIMSDK/tools v0.0.21 h1:iTapc2mIEVH/xl5Nd6jfwPub11Pgp44tVcE1rjB3a48= github.com/OpenIMSDK/tools v0.0.21/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= @@ -227,6 +225,8 @@ github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205Ah github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw= github.com/lithammer/shortuuid v3.0.0+incompatible h1:NcD0xWW/MZYXEHa6ITy6kaXN5nwm/V115vj2YXfhS0w= github.com/lithammer/shortuuid v3.0.0+incompatible/go.mod h1:FR74pbAuElzOUuenUHTK2Tciko1/vKuIKS9dSkDrA4w= +github.com/luhaoling/protocol v0.0.0-20231227040641-2f934a0d64a3 h1:HZz2U/M3T4x9SqPxWdrD9MZy7jxx7nS+nx/aRN9m3RQ= +github.com/luhaoling/protocol v0.0.0-20231227040641-2f934a0d64a3/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index b5ad186a5..af9720590 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -17,6 +17,7 @@ package user import ( "context" "errors" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "math/rand" "strings" "time" @@ -57,10 +58,6 @@ type userServer struct { RegisterCenter registry.SvcDiscoveryRegistry } -func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserInfoExReq) (*pbuser.UpdateUserInfoExResp, error) { - return nil, errs.ErrInternalServer.Wrap("not implemented") -} - func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { rdb, err := cache.NewRedis() if err != nil { @@ -466,26 +463,36 @@ func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser. return nil, err } + if req.NickName != "" { + users, err := s.UserDatabase.FindByNickname(ctx, req.NickName) + if err != nil { + return nil, err + } + resp := s.userModelToResp(users) + return resp, nil + } + + if req.UserID != "" { + users, err := s.UserDatabase.Find(ctx, []string{req.UserID}) + if err != nil { + return nil, err + } + resp := s.userModelToResp(users) + return resp, nil + } + _, users, err := s.UserDatabase.Page(ctx, req.Pagination) if err != nil { return nil, err } - var total int64 - accounts := make([]*pbuser.NotificationAccountInfo, 0, len(users)) - for _, v := range users { - if v.AppMangerLevel != constant.AppNotificationAdmin { - continue - } - temp := &pbuser.NotificationAccountInfo{ - UserID: v.UserID, - FaceURL: v.FaceURL, - NickName: v.Nickname, - } - accounts = append(accounts, temp) - total += 1 - } - return &pbuser.SearchNotificationAccountResp{Total: total, NotificationAccounts: accounts}, nil + resp := s.userModelToResp(users) + return resp, nil +} + +func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserInfoExReq) (*pbuser.UpdateUserInfoExResp, error) { + //TODO implement me + panic("implement me") } func (s *userServer) GetNotificationAccount(ctx context.Context, req *pbuser.GetNotificationAccountReq) (*pbuser.GetNotificationAccountResp, error) { @@ -517,3 +524,20 @@ func (s *userServer) genUserID() string { } return string(data) } + +func (s *userServer) userModelToResp(users []*relation.UserModel) *pbuser.SearchNotificationAccountResp { + accounts := make([]*pbuser.NotificationAccountInfo, 0) + var total int64 + for _, v := range users { + if v.AppMangerLevel == constant.AppNotificationAdmin || v.AppMangerLevel == constant.AppAdmin { + temp := &pbuser.NotificationAccountInfo{ + UserID: v.UserID, + FaceURL: v.FaceURL, + NickName: v.Nickname, + } + accounts = append(accounts, temp) + total += 1 + } + } + return &pbuser.SearchNotificationAccountResp{Total: total, NotificationAccounts: accounts} +} diff --git a/pkg/common/db/controller/user.go b/pkg/common/db/controller/user.go index 2fafcf266..72bdf6b06 100644 --- a/pkg/common/db/controller/user.go +++ b/pkg/common/db/controller/user.go @@ -38,6 +38,8 @@ type UserDatabase interface { FindWithError(ctx context.Context, userIDs []string) (users []*relation.UserModel, err error) // Find Get the information of the specified user If the userID is not found, no error will be returned Find(ctx context.Context, userIDs []string) (users []*relation.UserModel, err error) + // Find userInfo By Nickname + FindByNickname(ctx context.Context, nickname string) (users []*relation.UserModel, err error) // Create Insert multiple external guarantees that the userID is not repeated and does not exist in the db Create(ctx context.Context, users []*relation.UserModel) (err error) // Update update (non-zero value) external guarantee userID exists @@ -133,6 +135,11 @@ func (u *userDatabase) Find(ctx context.Context, userIDs []string) (users []*rel return u.cache.GetUsersInfo(ctx, userIDs) } +// Find userInfo By Nickname +func (u *userDatabase) FindByNickname(ctx context.Context, nickname string) (users []*relation.UserModel, err error) { + return u.userDB.TakeByNickname(ctx, nickname) +} + // Create Insert multiple external guarantees that the userID is not repeated and does not exist in the db. func (u *userDatabase) Create(ctx context.Context, users []*relation.UserModel) (err error) { return u.tx.Transaction(ctx, func(ctx context.Context) error { diff --git a/pkg/common/db/mgo/user.go b/pkg/common/db/mgo/user.go index 268d69ce3..0ca711ad8 100644 --- a/pkg/common/db/mgo/user.go +++ b/pkg/common/db/mgo/user.go @@ -65,6 +65,10 @@ func (u *UserMgo) Take(ctx context.Context, userID string) (user *relation.UserM return mgoutil.FindOne[*relation.UserModel](ctx, u.coll, bson.M{"user_id": userID}) } +func (u *UserMgo) TakeByNickname(ctx context.Context, nickname string) (user []*relation.UserModel, err error) { + return mgoutil.Find[*relation.UserModel](ctx, u.coll, bson.M{"nickname": nickname}) +} + func (u *UserMgo) Page(ctx context.Context, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error) { return mgoutil.FindPage[*relation.UserModel](ctx, u.coll, bson.M{}, pagination) } diff --git a/pkg/common/db/table/relation/user.go b/pkg/common/db/table/relation/user.go index 213a6b774..8917ba55f 100644 --- a/pkg/common/db/table/relation/user.go +++ b/pkg/common/db/table/relation/user.go @@ -53,6 +53,7 @@ type UserModelInterface interface { UpdateByMap(ctx context.Context, userID string, args map[string]any) (err error) Find(ctx context.Context, userIDs []string) (users []*UserModel, err error) Take(ctx context.Context, userID string) (user *UserModel, err error) + TakeByNickname(ctx context.Context, nickname string) (user []*UserModel, err error) Page(ctx context.Context, pagination pagination.Pagination) (count int64, users []*UserModel, err error) Exist(ctx context.Context, userID string) (exist bool, err error) GetAllUserID(ctx context.Context, pagination pagination.Pagination) (count int64, userIDs []string, err error) From 53a3f475f382743e93c62e0473d3ef0affbbf215 Mon Sep 17 00:00:00 2001 From: Gordon <46924906+FGadvancer@users.noreply.github.com> Date: Thu, 28 Dec 2023 00:41:51 +0800 Subject: [PATCH 10/20] fix: add notifications for some notifications. (#1609) --- pkg/msgprocessor/options.go | 13 +------------ pkg/rpcclient/msg.go | 9 +++++++++ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/pkg/msgprocessor/options.go b/pkg/msgprocessor/options.go index c17c7cb05..ea383a1af 100644 --- a/pkg/msgprocessor/options.go +++ b/pkg/msgprocessor/options.go @@ -30,10 +30,9 @@ func NewOptions(opts ...OptionsOpt) Options { options[constant.IsOfflinePush] = false options[constant.IsUnreadCount] = false options[constant.IsConversationUpdate] = false - options[constant.IsSenderSync] = false + options[constant.IsSenderSync] = true options[constant.IsNotPrivate] = false options[constant.IsSenderConversationUpdate] = false - options[constant.IsSenderNotificationPush] = false options[constant.IsReactionFromCache] = false for _, opt := range opts { opt(options) @@ -114,12 +113,6 @@ func WithSenderConversationUpdate() OptionsOpt { } } -func WithSenderNotificationPush() OptionsOpt { - return func(options Options) { - options[constant.IsSenderNotificationPush] = true - } -} - func WithReactionFromCache() OptionsOpt { return func(options Options) { options[constant.IsReactionFromCache] = true @@ -174,10 +167,6 @@ func (o Options) IsSenderConversationUpdate() bool { return o.Is(constant.IsSenderConversationUpdate) } -func (o Options) IsSenderNotificationPush() bool { - return o.Is(constant.IsSenderNotificationPush) -} - func (o Options) IsReactionFromCache() bool { return o.Is(constant.IsReactionFromCache) } diff --git a/pkg/rpcclient/msg.go b/pkg/rpcclient/msg.go index 3b09b5062..50e1cd4a1 100644 --- a/pkg/rpcclient/msg.go +++ b/pkg/rpcclient/msg.go @@ -256,6 +256,7 @@ func (s *NotificationSender) NotificationWithSesstionType(ctx context.Context, s optionsConfig.ReliabilityLevel = constant.UnreliableNotification } options := config.GetOptionsByNotification(optionsConfig) + s.SetOptionsByContentType(ctx, options, contentType) msg.Options = options offlineInfo.Title = title offlineInfo.Desc = desc @@ -274,3 +275,11 @@ func (s *NotificationSender) NotificationWithSesstionType(ctx context.Context, s 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...) } + +func (s *NotificationSender) SetOptionsByContentType(_ context.Context, options map[string]bool, contentType int32) { + switch contentType { + case constant.UserStatusChangeNotification: + options[constant.IsSenderSync] = false + default: + } +} From b90b8a1a558603107b65b51b1742c2ce944def0f Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751@qq.com> Date: Thu, 28 Dec 2023 14:17:27 +0800 Subject: [PATCH 11/20] fix: fix openim zk env set (#1623) * fix: fix openim zk env set Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> * fix: fix openim zk env set Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> --------- Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> --- pkg/common/db/cache/init_redis.go | 10 +++++++++- .../discoveryregister_test.go | 3 ++- .../discoveryregister/zookeeper/zookeeper.go | 14 +++++++++++--- pkg/common/kafka/producer.go | 13 ++++++------- pkg/common/kafka/util.go | 19 +++++++++++++++++++ tools/component/component_test.go | 12 ------------ 6 files changed, 47 insertions(+), 24 deletions(-) diff --git a/pkg/common/db/cache/init_redis.go b/pkg/common/db/cache/init_redis.go index 63d2f5707..1308e9649 100644 --- a/pkg/common/db/cache/init_redis.go +++ b/pkg/common/db/cache/init_redis.go @@ -87,7 +87,15 @@ func NewRedis() (redis.UniversalClient, error) { // overrideConfigFromEnv overrides configuration fields with environment variables if present. func overrideConfigFromEnv() { if envAddr := os.Getenv("REDIS_ADDRESS"); envAddr != "" { - config.Config.Redis.Address = strings.Split(envAddr, ",") // Assuming addresses are comma-separated + if envPort := os.Getenv("REDIS_PORT"); envPort != "" { + addresses := strings.Split(envAddr, ",") + for i, addr := range addresses { + addresses[i] = addr + ":" + envPort + } + config.Config.Redis.Address = addresses + } else { + config.Config.Redis.Address = strings.Split(envAddr, ",") + } } if envUser := os.Getenv("REDIS_USERNAME"); envUser != "" { config.Config.Redis.Username = envUser diff --git a/pkg/common/discoveryregister/discoveryregister_test.go b/pkg/common/discoveryregister/discoveryregister_test.go index d4a634b91..d83da1285 100644 --- a/pkg/common/discoveryregister/discoveryregister_test.go +++ b/pkg/common/discoveryregister/discoveryregister_test.go @@ -24,7 +24,8 @@ import ( func setupTestEnvironment() { os.Setenv("ZOOKEEPER_SCHEMA", "openim") - os.Setenv("ZOOKEEPER_ADDRESS", "172.28.0.1:12181") + os.Setenv("ZOOKEEPER_ADDRESS", "172.28.0.1") + os.Setenv("ZOOKEEPER_PORT", "12181") os.Setenv("ZOOKEEPER_USERNAME", "") os.Setenv("ZOOKEEPER_PASSWORD", "") } diff --git a/pkg/common/discoveryregister/zookeeper/zookeeper.go b/pkg/common/discoveryregister/zookeeper/zookeeper.go index a4edffc43..db1a5c6c4 100644 --- a/pkg/common/discoveryregister/zookeeper/zookeeper.go +++ b/pkg/common/discoveryregister/zookeeper/zookeeper.go @@ -52,10 +52,18 @@ func getEnv(key, fallback string) string { return fallback } -// getZkAddrFromEnv returns the value of an environment variable if it exists, otherwise it returns the fallback value. +// getZkAddrFromEnv returns the Zookeeper addresses combined from the ZOOKEEPER_ADDRESS and ZOOKEEPER_PORT environment variables. +// If the environment variables are not set, it returns the fallback value. func getZkAddrFromEnv(fallback []string) []string { - if value, exists := os.LookupEnv("ZOOKEEPER_ADDRESS"); exists { - return strings.Split(value, ",") + address, addrExists := os.LookupEnv("ZOOKEEPER_ADDRESS") + port, portExists := os.LookupEnv("ZOOKEEPER_PORT") + + if addrExists && portExists { + addresses := strings.Split(address, ",") + for i, addr := range addresses { + addresses[i] = addr + ":" + port + } + return addresses } return fallback } diff --git a/pkg/common/kafka/producer.go b/pkg/common/kafka/producer.go index 9a06809b7..06b1e2b4c 100644 --- a/pkg/common/kafka/producer.go +++ b/pkg/common/kafka/producer.go @@ -31,15 +31,14 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) -const ( - maxRetry = 10 // Maximum number of retries for producer creation -) +const maxRetry = 10 // number of retries -var errEmptyMsg = errors.New("binary msg is empty") +var errEmptyMsg = errors.New("kafka binary msg is empty") +// Producer represents a Kafka producer. type Producer struct { - topic string addr []string + topic string config *sarama.Config producer sarama.SyncProducer } @@ -68,7 +67,7 @@ func NewKafkaProducer(addr []string, topic string) *Producer { // Get Kafka configuration from environment variables or fallback to config file kafkaUsername := getEnvOrConfig("KAFKA_USERNAME", config.Config.Kafka.Username) kafkaPassword := getEnvOrConfig("KAFKA_PASSWORD", config.Config.Kafka.Password) - kafkaAddr := getEnvOrConfig("KAFKA_ADDRESS", addr[0]) // Assuming addr[0] contains address from config + kafkaAddr := getKafkaAddrFromEnv(addr) // Updated to use the new function // Configure SASL authentication if credentials are provided if kafkaUsername != "" && kafkaPassword != "" { @@ -78,7 +77,7 @@ func NewKafkaProducer(addr []string, topic string) *Producer { } // Set the Kafka address - p.addr = []string{kafkaAddr} + p.addr = kafkaAddr // Set up TLS configuration (if required) SetupTLSConfig(p.config) diff --git a/pkg/common/kafka/util.go b/pkg/common/kafka/util.go index da0c5349b..f318ecf73 100644 --- a/pkg/common/kafka/util.go +++ b/pkg/common/kafka/util.go @@ -15,7 +15,9 @@ package kafka import ( + "fmt" "os" + "strings" "github.com/IBM/sarama" @@ -44,3 +46,20 @@ func getEnvOrConfig(envName string, configValue string) string { } return configValue } + +// getKafkaAddrFromEnv returns the Kafka addresses combined from the KAFKA_ADDRESS and KAFKA_PORT environment variables. +// If the environment variables are not set, it returns the fallback value. +func getKafkaAddrFromEnv(fallback []string) []string { + envAddr := os.Getenv("KAFKA_ADDRESS") + envPort := os.Getenv("KAFKA_PORT") + + if envAddr != "" && envPort != "" { + addresses := strings.Split(envAddr, ",") + for i, addr := range addresses { + addresses[i] = fmt.Sprintf("%s:%s", addr, envPort) + } + return addresses + } + + return fallback +} diff --git a/tools/component/component_test.go b/tools/component/component_test.go index 08ea0fda8..4488c029e 100644 --- a/tools/component/component_test.go +++ b/tools/component/component_test.go @@ -21,22 +21,10 @@ import ( "time" "github.com/redis/go-redis/v9" - "github.com/stretchr/testify/assert" "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) -func TestCheckMysql(t *testing.T) { - err := mockInitCfg() - assert.NoError(t, err, "Initialization should not produce errors") - - err = checkMysql() - if err != nil { - // You might expect an error if MySQL isn't running locally with the mock credentials. - t.Logf("Expected error due to mock configuration: %v", err) - } -} - // Mock for initCfg for testing purpose func mockInitCfg() error { config.Config.Mysql.Username = "root" From 998d4a3504fd30623e75ec6e9590050749104483 Mon Sep 17 00:00:00 2001 From: AndrewZuo01 <59896149+AndrewZuo01@users.noreply.github.com> Date: Thu, 28 Dec 2023 14:45:27 +0800 Subject: [PATCH 12/20] Add updates friend, set user ex (#1592) * update set pin friends * update set pin friends * update set pin friends * update set pin friends * update set pin friends * update set pin friends * fix bugs * fix bugs * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * Update go.mod * Update friend.go * debug * debug * debug * add pin friend test * add pin friend test * add pin friend test * add pin friend test * add pin friend test * add pin friend test * add pin friend test * add pin friend test * add pin friend test * I cannot solve todo in test.sh * update user command * update user command * update user command * update user command * update user command * update user command * update user command * update user command * update user command * update user command * update user command * update user command * update user command * update user command * Update go.mod * fix group notification * fix group notification * update openimsdk tools * update openim server remove duplicate code * update openim server remove duplicate code * update user command get * update user command get * update response of callback response error * update black ex * update join group ex * update user pb2map * update go sum * update go sum * update updateUserInfoEx * update updateUserInfoEx * update updateUserInfoEx add callback functions * fix dismiss group function * fix dismiss group function * fix dismiss group function * fix dismiss group function * update pin friend to update friend * fix go mod * fix err golangci-lint * fix UserPb2DBMap * update comments, update go.mod check for register username * update comments, update go.mod check for register username * update comments, update go.mod check for register username * update comments, update go.mod check for register username * fix callback * fix go.mod * fix debug * fix bugs * update notification * update notification * update notification * update notification * update notification * update notification * update notification * update notification --------- Co-authored-by: Xinwei Xiong <3293172751@qq.com> --- config/templates/config.yaml.template | 8 ++++ go.sum | 2 + internal/api/friend.go | 3 ++ internal/api/msg.go | 26 +++++++++++ internal/api/route.go | 3 +- internal/api/user.go | 4 +- internal/msggateway/client.go | 4 ++ internal/rpc/friend/friend.go | 36 +++++++++------- internal/rpc/msg/callback.go | 2 +- internal/rpc/user/callback.go | 36 +++++++++++++++- internal/rpc/user/user.go | 39 ++++++++++++++--- pkg/callbackstruct/constant.go | 2 + pkg/callbackstruct/user.go | 30 ++++++++++++- pkg/common/config/config.go | 2 + pkg/common/convert/user.go | 21 ++++++++- pkg/common/db/controller/friend.go | 60 ++++++++++++++++++++------ pkg/common/db/mgo/friend.go | 30 +++++++++++++ pkg/common/db/table/relation/friend.go | 4 ++ pkg/rpcclient/msg.go | 2 + pkg/rpcclient/notification/friend.go | 7 ++- tools/component/component.go | 22 +++++++--- 21 files changed, 297 insertions(+), 46 deletions(-) diff --git a/config/templates/config.yaml.template b/config/templates/config.yaml.template index fd51a2e31..89f11f359 100644 --- a/config/templates/config.yaml.template +++ b/config/templates/config.yaml.template @@ -312,6 +312,14 @@ callback: enable: false timeout: 5 failedContinue: true + beforeUpdateUserInfoEx: + enable: false + timeout: 5 + failedContinue: true + afterUpdateUserInfoEx: + enable: false + timeout: 5 + failedContinue: true afterSendSingleMsg: enable: false timeout: 5 diff --git a/go.sum b/go.sum index 09e4ce06b..881000381 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIw github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c= github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ= +github.com/OpenIMSDK/protocol v0.0.40 h1:1/Oij6RSAaePCPrWGwp9Cyz976/8Uxr94hM5M5FXzlg= +github.com/OpenIMSDK/protocol v0.0.40/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/OpenIMSDK/tools v0.0.21 h1:iTapc2mIEVH/xl5Nd6jfwPub11Pgp44tVcE1rjB3a48= github.com/OpenIMSDK/tools v0.0.21/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= diff --git a/internal/api/friend.go b/internal/api/friend.go index 23f337a9f..7dc898a02 100644 --- a/internal/api/friend.go +++ b/internal/api/friend.go @@ -92,3 +92,6 @@ func (o *FriendApi) GetFriendIDs(c *gin.Context) { func (o *FriendApi) GetSpecifiedFriendsInfo(c *gin.Context) { a2r.Call(friend.FriendClient.GetSpecifiedFriendsInfo, o.Client, c) } +func (o *FriendApi) UpdateFriends(c *gin.Context) { + a2r.Call(friend.FriendClient.UpdateFriends, o.Client, c) +} diff --git a/internal/api/msg.go b/internal/api/msg.go index b6bfcebb6..8346ad860 100644 --- a/internal/api/msg.go +++ b/internal/api/msg.go @@ -172,6 +172,7 @@ func (m *MessageApi) getSendMsgReq(c *gin.Context, req apistruct.SendMsg) (sendM if err = m.userRpcClient.GetNotificationByID(c, req.SendID); err != nil { return nil, err } + default: return nil, errs.ErrArgs.WithDetail("not support err contentType") } @@ -185,38 +186,63 @@ func (m *MessageApi) getSendMsgReq(c *gin.Context, req apistruct.SendMsg) (sendM return m.newUserSendMsgReq(c, &req), nil } +// SendMessage handles the sending of a message. It's an HTTP handler function to be used with Gin framework. func (m *MessageApi) SendMessage(c *gin.Context) { + // Initialize a request struct for sending a message. req := apistruct.SendMsgReq{} + + // Bind the JSON request body to the request struct. if err := c.BindJSON(&req); err != nil { + // Respond with an error if request body binding fails. apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap()) return } + + // Check if the user has the app manager role. if !authverify.IsAppManagerUid(c) { + // Respond with a permission error if the user is not an app manager. apiresp.GinError(c, errs.ErrNoPermission.Wrap("only app manager can send message")) return } + + // Prepare the message request with additional required data. sendMsgReq, err := m.getSendMsgReq(c, req.SendMsg) if err != nil { + // Log and respond with an error if preparation fails. log.ZError(c, "decodeData failed", err) apiresp.GinError(c, err) return } + + // Set the receiver ID in the message data. sendMsgReq.MsgData.RecvID = req.RecvID + + // Declare a variable to store the message sending status. var status int + + // Attempt to send the message using the client. respPb, err := m.Client.SendMsg(c, sendMsgReq) if err != nil { + // Set the status to failed and respond with an error if sending fails. status = constant.MsgSendFailed log.ZError(c, "send message err", err) apiresp.GinError(c, err) return } + + // Set the status to successful if the message is sent. status = constant.MsgSendSuccessed + + // Attempt to update the message sending status in the system. _, err = m.Client.SetSendMsgStatus(c, &msg.SetSendMsgStatusReq{ Status: int32(status), }) if err != nil { + // Log the error if updating the status fails. log.ZError(c, "SetSendMsgStatus failed", err) } + + // Respond with a success message and the response payload. apiresp.GinSuccess(c, respPb) } diff --git a/internal/api/route.go b/internal/api/route.go index 8fb372587..766912d45 100644 --- a/internal/api/route.go +++ b/internal/api/route.go @@ -67,6 +67,7 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive { userRouterGroup.POST("/user_register", u.UserRegister) userRouterGroup.POST("/update_user_info", ParseToken, u.UpdateUserInfo) + userRouterGroup.POST("/update_user_info_ex", ParseToken, u.UpdateUserInfoEx) userRouterGroup.POST("/set_global_msg_recv_opt", ParseToken, u.SetGlobalRecvMessageOpt) userRouterGroup.POST("/get_users_info", ParseToken, u.GetUsersPublicInfo) userRouterGroup.POST("/get_all_users_uid", ParseToken, u.GetAllUsersID) @@ -107,7 +108,7 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive friendRouterGroup.POST("/is_friend", f.IsFriend) friendRouterGroup.POST("/get_friend_id", f.GetFriendIDs) friendRouterGroup.POST("/get_specified_friends_info", f.GetSpecifiedFriendsInfo) - //friendRouterGroup.POST("/set_pin_friend", f.SetPinFriends) + friendRouterGroup.POST("/update_friends", f.UpdateFriends) } g := NewGroupApi(*groupRpc) groupRouterGroup := r.Group("/group", ParseToken) diff --git a/internal/api/user.go b/internal/api/user.go index cf68fb422..5f0e23631 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -41,7 +41,9 @@ func (u *UserApi) UserRegister(c *gin.Context) { func (u *UserApi) UpdateUserInfo(c *gin.Context) { a2r.Call(user.UserClient.UpdateUserInfo, u.Client, c) } - +func (u *UserApi) UpdateUserInfoEx(c *gin.Context) { + a2r.Call(user.UserClient.UpdateUserInfoEx, u.Client, c) +} func (u *UserApi) SetGlobalRecvMessageOpt(c *gin.Context) { a2r.Call(user.UserClient.SetGlobalRecvMessageOpt, u.Client, c) } diff --git a/internal/msggateway/client.go b/internal/msggateway/client.go index 69b49d81a..43047fd73 100644 --- a/internal/msggateway/client.go +++ b/internal/msggateway/client.go @@ -87,6 +87,7 @@ func newClient(ctx *UserConnContext, conn LongConn, isCompress bool) *Client { } } +// ResetClient updates the client's state with new connection and context information. func (c *Client) ResetClient( ctx *UserConnContext, conn LongConn, @@ -108,11 +109,13 @@ func (c *Client) ResetClient( c.token = token } +// pingHandler handles ping messages and sends pong responses. func (c *Client) pingHandler(_ string) error { _ = c.conn.SetReadDeadline(pongWait) return c.writePongMsg() } +// readMessage continuously reads messages from the connection. func (c *Client) readMessage() { defer func() { if r := recover(); r != nil { @@ -164,6 +167,7 @@ func (c *Client) readMessage() { } } +// handleMessage processes a single message received by the client. func (c *Client) handleMessage(message []byte) error { if c.IsCompress { var err error diff --git a/internal/rpc/friend/friend.go b/internal/rpc/friend/friend.go index c40b566f3..c53cb88f5 100644 --- a/internal/rpc/friend/friend.go +++ b/internal/rpc/friend/friend.go @@ -53,10 +53,6 @@ type friendServer struct { RegisterCenter registry.SvcDiscoveryRegistry } -func (s *friendServer) UpdateFriends(ctx context.Context, req *pbfriend.UpdateFriendsReq) (*pbfriend.UpdateFriendsResp, error) { - return nil, errs.ErrInternalServer.Wrap("not implemented") -} - func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { // Initialize MongoDB mongo, err := unrelation.NewMongo() @@ -440,7 +436,7 @@ func (s *friendServer) GetSpecifiedFriendsInfo(ctx context.Context, req *pbfrien } return resp, nil } -func (s *friendServer) PinFriends( +func (s *friendServer) UpdateFriends( ctx context.Context, req *pbfriend.UpdateFriendsReq, ) (*pbfriend.UpdateFriendsResp, error) { @@ -450,25 +446,35 @@ func (s *friendServer) PinFriends( if utils.Duplicate(req.FriendUserIDs) { return nil, errs.ErrArgs.Wrap("friendIDList repeated") } - var isPinned bool - if req.IsPinned != nil { - isPinned = req.IsPinned.Value - } else { - return nil, errs.ErrArgs.Wrap("isPinned is nil") - } - //check whther in friend list + _, err := s.friendDatabase.FindFriendsWithError(ctx, req.OwnerUserID, req.FriendUserIDs) if err != nil { return nil, err } - //set friendslist friend pin status to isPinned for _, friendID := range req.FriendUserIDs { - if err := s.friendDatabase.UpdateFriendPinStatus(ctx, req.OwnerUserID, friendID, isPinned); err != nil { - return nil, err + if req.IsPinned != nil { + if err = s.friendDatabase.UpdateFriendPinStatus(ctx, req.OwnerUserID, friendID, req.IsPinned.Value); err != nil { + return nil, err + } + } + if req.Remark != nil { + if err = s.friendDatabase.UpdateFriendRemark(ctx, req.OwnerUserID, friendID, req.Remark.Value); err != nil { + return nil, err + } + } + if req.Ex != nil { + if err = s.friendDatabase.UpdateFriendEx(ctx, req.OwnerUserID, friendID, req.Ex.Value); err != nil { + return nil, err + } } } resp := &pbfriend.UpdateFriendsResp{} + + err = s.notificationSender.FriendsInfoUpdateNotification(ctx, req.OwnerUserID, req.FriendUserIDs) + if err != nil { + return nil, errs.Wrap(err, "FriendsInfoUpdateNotification Error") + } return resp, nil } diff --git a/internal/rpc/msg/callback.go b/internal/rpc/msg/callback.go index 3a8587ea3..5d192fb87 100644 --- a/internal/rpc/msg/callback.go +++ b/internal/rpc/msg/callback.go @@ -100,7 +100,7 @@ func callbackAfterSendSingleMsg(ctx context.Context, msg *pbchat.SendMsgReq) err } func callbackBeforeSendGroupMsg(ctx context.Context, msg *pbchat.SendMsgReq) error { - if !config.Config.Callback.CallbackBeforeSendSingleMsg.Enable { + if !config.Config.Callback.CallbackBeforeSendGroupMsg.Enable { return nil } req := &cbapi.CallbackBeforeSendGroupMsgReq{ diff --git a/internal/rpc/user/callback.go b/internal/rpc/user/callback.go index 7d865af5f..9e02eb130 100644 --- a/internal/rpc/user/callback.go +++ b/internal/rpc/user/callback.go @@ -44,7 +44,6 @@ func CallbackBeforeUpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInf utils.NotNilReplace(&req.UserInfo.Nickname, resp.Nickname) return nil } - func CallbackAfterUpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInfoReq) error { if !config.Config.Callback.CallbackAfterUpdateUserInfo.Enable { return nil @@ -61,6 +60,41 @@ func CallbackAfterUpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInfo } return nil } +func CallbackBeforeUpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserInfoExReq) error { + if !config.Config.Callback.CallbackBeforeUpdateUserInfoEx.Enable { + return nil + } + cbReq := &cbapi.CallbackBeforeUpdateUserInfoExReq{ + CallbackCommand: cbapi.CallbackBeforeUpdateUserInfoExCommand, + UserID: req.UserInfo.UserID, + FaceURL: &req.UserInfo.FaceURL, + Nickname: &req.UserInfo.Nickname, + } + resp := &cbapi.CallbackBeforeUpdateUserInfoExResp{} + if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackBeforeUpdateUserInfoEx); err != nil { + return err + } + utils.NotNilReplace(&req.UserInfo.FaceURL, resp.FaceURL) + utils.NotNilReplace(req.UserInfo.Ex, resp.Ex) + utils.NotNilReplace(&req.UserInfo.Nickname, resp.Nickname) + return nil +} +func CallbackAfterUpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserInfoExReq) error { + if !config.Config.Callback.CallbackAfterUpdateUserInfoEx.Enable { + return nil + } + cbReq := &cbapi.CallbackAfterUpdateUserInfoExReq{ + CallbackCommand: cbapi.CallbackAfterUpdateUserInfoExCommand, + UserID: req.UserInfo.UserID, + FaceURL: req.UserInfo.FaceURL, + Nickname: req.UserInfo.Nickname, + } + resp := &cbapi.CallbackAfterUpdateUserInfoExResp{} + if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackBeforeUpdateUserInfoEx); err != nil { + return err + } + return nil +} func CallbackBeforeUserRegister(ctx context.Context, req *pbuser.UserRegisterReq) error { if !config.Config.Callback.CallbackBeforeUserRegister.Enable { diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index af9720590..fdfd81ed2 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -149,7 +149,41 @@ func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserI } return resp, nil } +func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserInfoExReq) (resp *pbuser.UpdateUserInfoExResp, err error) { + resp = &pbuser.UpdateUserInfoExResp{} + err = authverify.CheckAccessV3(ctx, req.UserInfo.UserID) + if err != nil { + return nil, err + } + if err = CallbackBeforeUpdateUserInfoEx(ctx, req); err != nil { + return nil, err + } + data := convert.UserPb2DBMapEx(req.UserInfo) + if err = s.UpdateByMap(ctx, req.UserInfo.UserID, data); err != nil { + return nil, err + } + _ = s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserInfo.UserID) + friends, err := s.friendRpcClient.GetFriendIDs(ctx, req.UserInfo.UserID) + if err != nil { + return nil, err + } + if req.UserInfo.Nickname != "" || req.UserInfo.FaceURL != "" { + if err := s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil { + log.ZError(ctx, "NotificationUserInfoUpdate", err) + } + } + for _, friendID := range friends { + s.friendNotificationSender.FriendInfoUpdatedNotification(ctx, req.UserInfo.UserID, friendID) + } + if err := CallbackAfterUpdateUserInfoEx(ctx, req); err != nil { + return nil, err + } + if err := s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil { + log.ZError(ctx, "NotificationUserInfoUpdate", err, "userID", req.UserInfo.UserID) + } + return resp, nil +} 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 { @@ -490,11 +524,6 @@ func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser. return resp, nil } -func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserInfoExReq) (*pbuser.UpdateUserInfoExResp, error) { - //TODO implement me - panic("implement me") -} - func (s *userServer) GetNotificationAccount(ctx context.Context, req *pbuser.GetNotificationAccountReq) (*pbuser.GetNotificationAccountResp, error) { if req.UserID == "" { return nil, errs.ErrArgs.Wrap("userID is empty") diff --git a/pkg/callbackstruct/constant.go b/pkg/callbackstruct/constant.go index 0a03a1ef1..cda98af16 100644 --- a/pkg/callbackstruct/constant.go +++ b/pkg/callbackstruct/constant.go @@ -37,6 +37,8 @@ const ( CallbackGroupMsgReadCommand = "callbackGroupMsgReadCommand" CallbackMsgModifyCommand = "callbackMsgModifyCommand" CallbackAfterUpdateUserInfoCommand = "callbackAfterUpdateUserInfoCommand" + CallbackAfterUpdateUserInfoExCommand = "callbackAfterUpdateUserInfoExCommand" + CallbackBeforeUpdateUserInfoExCommand = "callbackBeforeUpdateUserInfoExCommand" CallbackBeforeUserRegisterCommand = "callbackBeforeUserRegisterCommand" CallbackAfterUserRegisterCommand = "callbackAfterUserRegisterCommand" CallbackTransferGroupOwnerAfter = "callbackTransferGroupOwnerAfter" diff --git a/pkg/callbackstruct/user.go b/pkg/callbackstruct/user.go index f35cff554..bfb69bd38 100644 --- a/pkg/callbackstruct/user.go +++ b/pkg/callbackstruct/user.go @@ -14,7 +14,10 @@ package callbackstruct -import "github.com/OpenIMSDK/protocol/sdkws" +import ( + "github.com/OpenIMSDK/protocol/sdkws" + "github.com/OpenIMSDK/protocol/wrapperspb" +) type CallbackBeforeUpdateUserInfoReq struct { CallbackCommand `json:"callbackCommand"` @@ -41,6 +44,31 @@ type CallbackAfterUpdateUserInfoResp struct { CommonCallbackResp } +type CallbackBeforeUpdateUserInfoExReq struct { + CallbackCommand `json:"callbackCommand"` + UserID string `json:"userID"` + Nickname *string `json:"nickName"` + FaceURL *string `json:"faceURL"` + Ex *wrapperspb.StringValue `json:"ex"` +} +type CallbackBeforeUpdateUserInfoExResp struct { + CommonCallbackResp + Nickname *string `json:"nickName"` + FaceURL *string `json:"faceURL"` + Ex *wrapperspb.StringValue `json:"ex"` +} + +type CallbackAfterUpdateUserInfoExReq struct { + CallbackCommand `json:"callbackCommand"` + UserID string `json:"userID"` + Nickname string `json:"nickName"` + FaceURL string `json:"faceURL"` + Ex *wrapperspb.StringValue `json:"ex"` +} +type CallbackAfterUpdateUserInfoExResp struct { + CommonCallbackResp +} + type CallbackBeforeUserRegisterReq struct { CallbackCommand `json:"callbackCommand"` Secret string `json:"secret"` diff --git a/pkg/common/config/config.go b/pkg/common/config/config.go index 707601234..ea26ca677 100644 --- a/pkg/common/config/config.go +++ b/pkg/common/config/config.go @@ -282,6 +282,8 @@ type configStruct struct { CallbackBeforeSetFriendRemark CallBackConfig `yaml:"callbackBeforeSetFriendRemark"` CallbackAfterSetFriendRemark CallBackConfig `yaml:"callbackAfterSetFriendRemark"` CallbackBeforeUpdateUserInfo CallBackConfig `yaml:"beforeUpdateUserInfo"` + CallbackBeforeUpdateUserInfoEx CallBackConfig `yaml:"beforeUpdateUserInfoEx"` + CallbackAfterUpdateUserInfoEx CallBackConfig `yaml:"afterUpdateUserInfoEx"` CallbackBeforeUserRegister CallBackConfig `yaml:"beforeUserRegister"` CallbackAfterUpdateUserInfo CallBackConfig `yaml:"updateUserInfo"` CallbackAfterUserRegister CallBackConfig `yaml:"afterUserRegister"` diff --git a/pkg/common/convert/user.go b/pkg/common/convert/user.go index 72041a790..6d43595bc 100644 --- a/pkg/common/convert/user.go +++ b/pkg/common/convert/user.go @@ -64,7 +64,7 @@ func UserPb2DBMap(user *sdkws.UserInfo) map[string]any { "global_recv_msg_opt": user.GlobalRecvMsgOpt, } for key, value := range fields { - if v, ok := value.(string); ok { + if v, ok := value.(string); ok && v != "" { val[key] = v } else if v, ok := value.(int32); ok && v != 0 { val[key] = v @@ -72,3 +72,22 @@ func UserPb2DBMap(user *sdkws.UserInfo) map[string]any { } return val } +func UserPb2DBMapEx(user *sdkws.UserInfoWithEx) map[string]any { + if user == nil { + return nil + } + val := make(map[string]any) + + // Map fields from UserInfoWithEx to val + val["nickname"] = user.Nickname + val["face_url"] = user.FaceURL + + if user.Ex != nil { + val["ex"] = user.Ex.Value + } + if user.GlobalRecvMsgOpt != 0 { + val["global_recv_msg_opt"] = user.GlobalRecvMsgOpt + } + + return val +} diff --git a/pkg/common/db/controller/friend.go b/pkg/common/db/controller/friend.go index 34ce22295..924a179ba 100644 --- a/pkg/common/db/controller/friend.go +++ b/pkg/common/db/controller/friend.go @@ -32,33 +32,57 @@ import ( ) type FriendDatabase interface { - // 检查user2是否在user1的好友列表中(inUser1Friends==true) 检查user1是否在user2的好友列表中(inUser2Friends==true) + // CheckIn checks if user2 is in user1's friend list (inUser1Friends==true) and if user1 is in user2's friend list (inUser2Friends==true) CheckIn(ctx context.Context, user1, user2 string) (inUser1Friends bool, inUser2Friends bool, err error) - // 增加或者更新好友申请 + + // AddFriendRequest adds or updates a friend request AddFriendRequest(ctx context.Context, fromUserID, toUserID string, reqMsg string, ex string) (err error) - // 先判断是否在好友表,如果在则不插入 + + // BecomeFriends first checks if the users are already in the friends table; if not, it inserts them as friends BecomeFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, addSource int32) (err error) - // 拒绝好友申请 + + // RefuseFriendRequest refuses a friend request RefuseFriendRequest(ctx context.Context, friendRequest *relation.FriendRequestModel) (err error) - // 同意好友申请 + + // AgreeFriendRequest accepts a friend request AgreeFriendRequest(ctx context.Context, friendRequest *relation.FriendRequestModel) (err error) - // 删除好友 + + // Delete removes a friend or friends from the owner's friend list Delete(ctx context.Context, ownerUserID string, friendUserIDs []string) (err error) - // 更新好友备注 + + // UpdateRemark updates the remark for a friend UpdateRemark(ctx context.Context, ownerUserID, friendUserID, remark string) (err error) - // 获取ownerUserID的好友列表 + + // PageOwnerFriends retrieves the friend list of ownerUserID with pagination PageOwnerFriends(ctx context.Context, ownerUserID string, pagination pagination.Pagination) (total int64, friends []*relation.FriendModel, err error) - // friendUserID在哪些人的好友列表中 + + // PageInWhoseFriends finds the users who have friendUserID in their friend list with pagination PageInWhoseFriends(ctx context.Context, friendUserID string, pagination pagination.Pagination) (total int64, friends []*relation.FriendModel, err error) - // 获取我发出去的好友申请 + + // PageFriendRequestFromMe retrieves the friend requests sent by the user with pagination PageFriendRequestFromMe(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, friends []*relation.FriendRequestModel, err error) - // 获取我收到的的好友申请 + + // PageFriendRequestToMe retrieves the friend requests received by the user with pagination PageFriendRequestToMe(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, friends []*relation.FriendRequestModel, err error) - // 获取某人指定好友的信息 + + // FindFriendsWithError fetches specified friends of a user and returns an error if any do not exist FindFriendsWithError(ctx context.Context, ownerUserID string, friendUserIDs []string) (friends []*relation.FriendModel, err error) + + // FindFriendUserIDs retrieves the friend IDs of a user FindFriendUserIDs(ctx context.Context, ownerUserID string) (friendUserIDs []string, err error) + + // FindBothFriendRequests finds friend requests sent and received FindBothFriendRequests(ctx context.Context, fromUserID, toUserID string) (friends []*relation.FriendRequestModel, err error) + + // UpdateFriendPinStatus updates the pinned status of a friend UpdateFriendPinStatus(ctx context.Context, ownerUserID string, friendUserID string, isPinned bool) (err error) + + // UpdateFriendRemark updates the remark for a friend + UpdateFriendRemark(ctx context.Context, ownerUserID string, friendUserID string, remark string) (err error) + + // UpdateFriendEx updates the 'ex' field for a friend + UpdateFriendEx(ctx context.Context, ownerUserID string, friendUserID string, ex string) (err error) + } type friendDatabase struct { @@ -305,3 +329,15 @@ func (f *friendDatabase) UpdateFriendPinStatus(ctx context.Context, ownerUserID } return f.cache.DelFriend(ownerUserID, friendUserID).ExecDel(ctx) } +func (f *friendDatabase) UpdateFriendRemark(ctx context.Context, ownerUserID string, friendUserID string, remark string) (err error) { + if err := f.friend.UpdateFriendRemark(ctx, ownerUserID, friendUserID, remark); err != nil { + return err + } + return f.cache.DelFriend(ownerUserID, friendUserID).ExecDel(ctx) +} +func (f *friendDatabase) UpdateFriendEx(ctx context.Context, ownerUserID string, friendUserID string, ex string) (err error) { + if err := f.friend.UpdateFriendEx(ctx, ownerUserID, friendUserID, ex); err != nil { + return err + } + return f.cache.DelFriend(ownerUserID, friendUserID).ExecDel(ctx) +} diff --git a/pkg/common/db/mgo/friend.go b/pkg/common/db/mgo/friend.go index 667098819..72289181b 100644 --- a/pkg/common/db/mgo/friend.go +++ b/pkg/common/db/mgo/friend.go @@ -160,3 +160,33 @@ func (f *FriendMgo) UpdatePinStatus(ctx context.Context, ownerUserID string, fri return nil } +func (f *FriendMgo) UpdateFriendRemark(ctx context.Context, ownerUserID string, friendUserID string, remark string) (err error) { + + filter := bson.M{"owner_user_id": ownerUserID, "friend_user_id": friendUserID} + // Create an update operation to set the "is_pinned" field to isPinned for all documents. + update := bson.M{"$set": bson.M{"remark": remark}} + + // Perform the update operation for all documents in the collection. + _, err = f.coll.UpdateMany(ctx, filter, update) + + if err != nil { + return errs.Wrap(err, "update remark error") + } + + return nil +} +func (f *FriendMgo) UpdateFriendEx(ctx context.Context, ownerUserID string, friendUserID string, ex string) (err error) { + + filter := bson.M{"owner_user_id": ownerUserID, "friend_user_id": friendUserID} + // Create an update operation to set the "is_pinned" field to isPinned for all documents. + update := bson.M{"$set": bson.M{"ex": ex}} + + // Perform the update operation for all documents in the collection. + _, err = f.coll.UpdateMany(ctx, filter, update) + + if err != nil { + return errs.Wrap(err, "update ex error") + } + + return nil +} diff --git a/pkg/common/db/table/relation/friend.go b/pkg/common/db/table/relation/friend.go index 4f85998f4..cc337701d 100644 --- a/pkg/common/db/table/relation/friend.go +++ b/pkg/common/db/table/relation/friend.go @@ -59,4 +59,8 @@ type FriendModelInterface interface { FindFriendUserIDs(ctx context.Context, ownerUserID string) (friendUserIDs []string, err error) // UpdatePinStatus update friend's pin status UpdatePinStatus(ctx context.Context, ownerUserID string, friendUserID string, isPinned bool) (err error) + // UpdateFriendRemark update friend's remark + UpdateFriendRemark(ctx context.Context, ownerUserID string, friendUserID string, remark string) (err error) + // UpdateFriendEx update friend's ex + UpdateFriendEx(ctx context.Context, ownerUserID string, friendUserID string, ex string) (err error) } diff --git a/pkg/rpcclient/msg.go b/pkg/rpcclient/msg.go index 50e1cd4a1..03769dc74 100644 --- a/pkg/rpcclient/msg.go +++ b/pkg/rpcclient/msg.go @@ -68,6 +68,7 @@ func newContentTypeConf() map[int32]config.NotificationConf { constant.BlackAddedNotification: config.Config.Notification.BlackAdded, constant.BlackDeletedNotification: config.Config.Notification.BlackDeleted, constant.FriendInfoUpdatedNotification: config.Config.Notification.FriendInfoUpdated, + constant.FriendsInfoUpdateNotification: config.Config.Notification.FriendInfoUpdated, //use the same FriendInfoUpdated // conversation constant.ConversationChangeNotification: config.Config.Notification.ConversationChanged, constant.ConversationUnreadNotification: config.Config.Notification.ConversationChanged, @@ -115,6 +116,7 @@ func newSessionTypeConf() map[int32]int32 { constant.BlackAddedNotification: constant.SingleChatType, constant.BlackDeletedNotification: constant.SingleChatType, constant.FriendInfoUpdatedNotification: constant.SingleChatType, + constant.FriendsInfoUpdateNotification: constant.SingleChatType, // conversation constant.ConversationChangeNotification: constant.SingleChatType, constant.ConversationUnreadNotification: constant.SingleChatType, diff --git a/pkg/rpcclient/notification/friend.go b/pkg/rpcclient/notification/friend.go index b061a24ae..00759b1b2 100644 --- a/pkg/rpcclient/notification/friend.go +++ b/pkg/rpcclient/notification/friend.go @@ -196,7 +196,12 @@ func (f *FriendNotificationSender) FriendRemarkSetNotification(ctx context.Conte tips.FromToUserID.ToUserID = toUserID return f.Notification(ctx, fromUserID, toUserID, constant.FriendRemarkSetNotification, &tips) } - +func (f *FriendNotificationSender) FriendsInfoUpdateNotification(ctx context.Context, toUserID string, friendIDs []string) error { + tips := sdkws.FriendsInfoUpdateTips{} + tips.FromToUserID.ToUserID = toUserID + tips.FriendIDs = friendIDs + return f.Notification(ctx, toUserID, toUserID, constant.FriendsInfoUpdateNotification, &tips) +} func (f *FriendNotificationSender) BlackAddedNotification(ctx context.Context, req *pbfriend.AddBlackReq) error { tips := sdkws.BlackAddedTips{FromToUserID: &sdkws.FromToUserID{}} tips.FromToUserID.FromUserID = req.OwnerUserID diff --git a/tools/component/component.go b/tools/component/component.go index 28ea7a2fe..616ffec2d 100644 --- a/tools/component/component.go +++ b/tools/component/component.go @@ -16,6 +16,7 @@ package main import ( "context" + "errors" "flag" "fmt" "net" @@ -285,10 +286,23 @@ func checkZookeeper() (string, error) { // Connect to Zookeeper str := "the addr is:" + address - c, _, err := zk.Connect(zookeeperAddresses, time.Second) // Adjust the timeout as necessary + c, eventChan, err := zk.Connect(zookeeperAddresses, time.Second) // Adjust the timeout as necessary if err != nil { return "", errs.Wrap(errStr(err, str)) } + timeout := time.After(5 * time.Second) + for { + select { + case event := <-eventChan: + if event.State == zk.StateConnected { + fmt.Println("Connected to Zookeeper") + goto Connected + } + case <-timeout: + return "", errs.Wrap(errors.New("timeout waiting for Zookeeper connection"), "Zookeeper Addr: "+strings.Join(config.Config.Zookeeper.ZkAddr, " ")) + } + } +Connected: defer c.Close() // Set authentication if username and password are provided @@ -298,12 +312,6 @@ func checkZookeeper() (string, error) { } } - // Check if Zookeeper is reachable - _, _, err = c.Get("/") - if err != nil { - return "", errs.Wrap(err, str) - } - return str, nil } From cfde7bb0ff6d7441e3ae593016d6848b9e6af020 Mon Sep 17 00:00:00 2001 From: a3d21 <93191329+a3d21@users.noreply.github.com> Date: Fri, 29 Dec 2023 10:25:03 +0800 Subject: [PATCH 13/20] fix: mongo uri connect (#1611) --- pkg/common/db/unrelation/mongo.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/common/db/unrelation/mongo.go b/pkg/common/db/unrelation/mongo.go index 2c1dc6f38..8cfb97a98 100644 --- a/pkg/common/db/unrelation/mongo.go +++ b/pkg/common/db/unrelation/mongo.go @@ -74,6 +74,10 @@ func buildMongoURI() string { return uri } + if config.Config.Mongo.Uri != "" { + return config.Config.Mongo.Uri + } + username := os.Getenv("MONGO_USERNAME") password := os.Getenv("MONGO_PASSWORD") address := os.Getenv("MONGO_ADDRESS") From 7ddb84f7fcdab6fc2b67593691fbf741da0c56ff Mon Sep 17 00:00:00 2001 From: Brabem <69128477+luhaoling@users.noreply.github.com> Date: Fri, 29 Dec 2023 18:14:15 +0800 Subject: [PATCH 14/20] fix: add the GetconversationAPI 1542 (#1604) * feat: add GetConversationsUnreadSeqAndMaxSeq func * feat: add GetConversationsUnreadSeqAndMaxSeq func * feat: add GetConversationsUnreadSeqAndMaxSeq func * fix: fix the conflect * feat: add GetConversationList Api * fix: fix the error * fix: move the GetConversationList to conversation folder * fix: fix the go.mod * fix: add InitiateFormData and CompleteFormData * fix: fix the error * fix: find error * fix: test * feat: add notification API * fix: find error * fix: fix the error * fix: fix the PinFriend error * fix: fix the Ex error * fix: find the error * fix: fix the rpc error * fix: fix the error * fix: fix the log error * fix: fix the error1 * fix: fix the error * fix: fix the script * fix: fix the error * fix: fix the error * fix: fix the error of tag * fix: fix the protocol * fix: fix the error * fix: fix the error * fix: fix the err not wrap * fix: fix the error * fix: fix GetGroupMembers by nickname * fix: fix the error * fix: fix the FindOneByDocIDs * fix: fix the protocol version * fix: fit the protocol version --- go.mod | 4 +- go.sum | 6 +- internal/api/conversation.go | 4 + internal/api/msg.go | 5 +- internal/api/route.go | 1 + internal/rpc/conversation/conversaion.go | 173 +++++++++++++++++++++++ internal/rpc/msg/as_read.go | 3 +- internal/rpc/msg/seq.go | 25 +++- pkg/apistruct/manage.go | 24 ++++ pkg/common/db/controller/msg.go | 16 +++ pkg/rpcclient/msg.go | 25 +++- 11 files changed, 273 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index 1b80650c5..2db34a8b1 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( firebase.google.com/go v3.13.0+incompatible - github.com/OpenIMSDK/protocol v0.0.40 + github.com/OpenIMSDK/protocol v0.0.41 github.com/OpenIMSDK/tools v0.0.21 github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/dtm-labs/rockscache v0.1.1 @@ -156,5 +156,3 @@ require ( golang.org/x/crypto v0.14.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) - -replace github.com/OpenIMSDK/protocol v0.0.40 => github.com/luhaoling/protocol v0.0.0-20231227040641-2f934a0d64a3 diff --git a/go.sum b/go.sum index 881000381..fc1d15242 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,8 @@ firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIw github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c= github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ= -github.com/OpenIMSDK/protocol v0.0.40 h1:1/Oij6RSAaePCPrWGwp9Cyz976/8Uxr94hM5M5FXzlg= -github.com/OpenIMSDK/protocol v0.0.40/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= +github.com/OpenIMSDK/protocol v0.0.42 h1:vIWXqZJZZ1ddleJA25fxhjZ1GyEHATpYM3wVWh4/+PY= +github.com/OpenIMSDK/protocol v0.0.42/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/OpenIMSDK/tools v0.0.21 h1:iTapc2mIEVH/xl5Nd6jfwPub11Pgp44tVcE1rjB3a48= github.com/OpenIMSDK/tools v0.0.21/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= @@ -227,8 +227,6 @@ github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205Ah github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw= github.com/lithammer/shortuuid v3.0.0+incompatible h1:NcD0xWW/MZYXEHa6ITy6kaXN5nwm/V115vj2YXfhS0w= github.com/lithammer/shortuuid v3.0.0+incompatible/go.mod h1:FR74pbAuElzOUuenUHTK2Tciko1/vKuIKS9dSkDrA4w= -github.com/luhaoling/protocol v0.0.0-20231227040641-2f934a0d64a3 h1:HZz2U/M3T4x9SqPxWdrD9MZy7jxx7nS+nx/aRN9m3RQ= -github.com/luhaoling/protocol v0.0.0-20231227040641-2f934a0d64a3/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= diff --git a/internal/api/conversation.go b/internal/api/conversation.go index e422de677..6463cbde6 100644 --- a/internal/api/conversation.go +++ b/internal/api/conversation.go @@ -33,6 +33,10 @@ func (o *ConversationApi) GetAllConversations(c *gin.Context) { a2r.Call(conversation.ConversationClient.GetAllConversations, o.Client, c) } +func (o *ConversationApi) GetConversationsList(c *gin.Context) { + a2r.Call(conversation.ConversationClient.GetConversationList, o.Client, c) +} + func (o *ConversationApi) GetConversation(c *gin.Context) { a2r.Call(conversation.ConversationClient.GetConversation, o.Client, c) } diff --git a/internal/api/msg.go b/internal/api/msg.go index 8346ad860..548450534 100644 --- a/internal/api/msg.go +++ b/internal/api/msg.go @@ -250,13 +250,14 @@ func (m *MessageApi) SendBusinessNotification(c *gin.Context) { req := struct { Key string `json:"key"` Data string `json:"data"` - SendUserID string `json:"sendUserID"` - RecvUserID string `json:"recvUserID"` + SendUserID string `json:"sendUserID" binding:"required"` + RecvUserID string `json:"recvUserID" binding:"required"` }{} if err := c.BindJSON(&req); err != nil { apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap()) return } + if !authverify.IsAppManagerUid(c) { apiresp.GinError(c, errs.ErrNoPermission.Wrap("only app manager can send message")) return diff --git a/internal/api/route.go b/internal/api/route.go index 766912d45..1c91f4dde 100644 --- a/internal/api/route.go +++ b/internal/api/route.go @@ -204,6 +204,7 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive conversationGroup := r.Group("/conversation", ParseToken) { c := NewConversationApi(*conversationRpc) + conversationGroup.POST("/get_conversations_list", c.GetConversationsList) conversationGroup.POST("/get_all_conversations", c.GetAllConversations) conversationGroup.POST("/get_conversation", c.GetConversation) conversationGroup.POST("/get_conversations", c.GetConversations) diff --git a/internal/rpc/conversation/conversaion.go b/internal/rpc/conversation/conversaion.go index 88c9ff7ff..b80e32953 100644 --- a/internal/rpc/conversation/conversaion.go +++ b/internal/rpc/conversation/conversaion.go @@ -17,6 +17,8 @@ package conversation import ( "context" "errors" + "github.com/OpenIMSDK/protocol/sdkws" + "sort" "github.com/OpenIMSDK/tools/tx" @@ -41,6 +43,8 @@ import ( ) type conversationServer struct { + msgRpcClient *rpcclient.MessageRpcClient + user *rpcclient.UserRpcClient groupRpcClient *rpcclient.GroupRpcClient conversationDatabase controller.ConversationDatabase conversationNotificationSender *notification.ConversationNotificationSender @@ -61,7 +65,10 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e } groupRpcClient := rpcclient.NewGroupRpcClient(client) msgRpcClient := rpcclient.NewMessageRpcClient(client) + userRpcClient := rpcclient.NewUserRpcClient(client) pbconversation.RegisterConversationServer(server, &conversationServer{ + msgRpcClient: &msgRpcClient, + user: &userRpcClient, conversationNotificationSender: notification.NewConversationNotificationSender(&msgRpcClient), groupRpcClient: &groupRpcClient, conversationDatabase: controller.NewConversationDatabase(conversationDB, cache.NewConversationRedis(rdb, cache.GetDefaultOpt(), conversationDB), tx.NewMongo(mongo.GetClient())), @@ -82,6 +89,73 @@ func (c *conversationServer) GetConversation(ctx context.Context, req *pbconvers return resp, nil } +func (m *conversationServer) GetConversationList(ctx context.Context, req *pbconversation.GetConversationListReq) (resp *pbconversation.GetConversationListResp, err error) { + log.ZDebug(ctx, "GetConversationList", "seqs", req, "userID", req.UserID) + var conversationIDs []string + if len(req.ConversationIDs) == 0 { + conversationIDs, err = m.conversationDatabase.GetConversationIDs(ctx, req.UserID) + if err != nil { + return nil, err + } + } else { + conversationIDs = req.ConversationIDs + } + + conversations, err := m.conversationDatabase.FindConversations(ctx, req.UserID, conversationIDs) + if err != nil { + return nil, err + } + if len(conversations) == 0 { + return nil, errs.ErrRecordNotFound.Wrap() + } + + maxSeqs, err := m.msgRpcClient.GetMaxSeqs(ctx, conversationIDs) + if err != nil { + return nil, err + } + + chatLogs, err := m.msgRpcClient.GetMsgByConversationIDs(ctx, conversationIDs, maxSeqs) + if err != nil { + return nil, err + } + + conversationMsg, err := m.getConversationInfo(ctx, chatLogs, req.UserID) + if err != nil { + return nil, err + } + + hasReadSeqs, err := m.msgRpcClient.GetHasReadSeqs(ctx, req.UserID, conversationIDs) + if err != nil { + return nil, err + } + + conversation_unreadCount := make(map[string]int64) + for conversationID, maxSeq := range maxSeqs { + conversation_unreadCount[conversationID] = maxSeq - hasReadSeqs[conversationID] + } + + conversation_isPinkTime := make(map[int64]string) + conversation_notPinkTime := make(map[int64]string) + for _, v := range conversations { + conversationID := v.ConversationID + time := conversationMsg[conversationID].MsgInfo.LatestMsgRecvTime + conversationMsg[conversationID].RecvMsgOpt = v.RecvMsgOpt + if v.IsPinned { + conversationMsg[conversationID].IsPinned = v.IsPinned + conversation_isPinkTime[time] = conversationID + continue + } + conversation_notPinkTime[time] = conversationID + } + resp = &pbconversation.GetConversationListResp{ + ConversationElems: []*pbconversation.ConversationElem{}, + } + + m.conversationSort(conversation_isPinkTime, resp, conversation_unreadCount, conversationMsg) + m.conversationSort(conversation_notPinkTime, resp, conversation_unreadCount, conversationMsg) + return resp, nil +} + func (c *conversationServer) GetAllConversations(ctx context.Context, req *pbconversation.GetAllConversationsReq) (*pbconversation.GetAllConversationsResp, error) { conversations, err := c.conversationDatabase.GetUserAllConversation(ctx, req.OwnerUserID) if err != nil { @@ -348,3 +422,102 @@ func (c *conversationServer) GetConversationOfflinePushUserIDs( } return &pbconversation.GetConversationOfflinePushUserIDsResp{UserIDs: utils.Keys(userIDSet)}, nil } + +func (c *conversationServer) conversationSort( + conversations map[int64]string, + resp *pbconversation.GetConversationListResp, + conversation_unreadCount map[string]int64, + conversationMsg map[string]*pbconversation.ConversationElem, +) { + keys := []int64{} + for key := range conversations { + keys = append(keys, key) + } + + sort.Slice(keys[:], func(i, j int) bool { + return keys[i] > keys[j] + }) + index := 0 + + cons := make([]*pbconversation.ConversationElem, len(conversations)) + for _, v := range keys { + conversationID := conversations[v] + conversationElem := conversationMsg[conversationID] + conversationElem.UnreadCount = conversation_unreadCount[conversationID] + cons[index] = conversationElem + index++ + } + resp.ConversationElems = append(resp.ConversationElems, cons...) +} + +func (c *conversationServer) getConversationInfo( + ctx context.Context, + chatLogs map[string]*sdkws.MsgData, + userID string) (map[string]*pbconversation.ConversationElem, error) { + var ( + sendIDs []string + groupIDs []string + sendMap = make(map[string]*sdkws.UserInfo) + groupMap = make(map[string]*sdkws.GroupInfo) + conversationMsg = make(map[string]*pbconversation.ConversationElem) + ) + for _, chatLog := range chatLogs { + switch chatLog.SessionType { + case constant.SingleChatType: + if chatLog.SendID == userID { + sendIDs = append(sendIDs, chatLog.RecvID) + } + sendIDs = append(sendIDs, chatLog.SendID) + case constant.GroupChatType, constant.SuperGroupChatType: + groupIDs = append(groupIDs, chatLog.GroupID) + sendIDs = append(sendIDs, chatLog.SendID) + } + } + if len(sendIDs) != 0 { + sendInfos, err := c.user.GetUsersInfo(ctx, sendIDs) + if err != nil { + return nil, err + } + for _, sendInfo := range sendInfos { + sendMap[sendInfo.UserID] = sendInfo + } + } + if len(groupIDs) != 0 { + groupInfos, err := c.groupRpcClient.GetGroupInfos(ctx, groupIDs, false) + if err != nil { + return nil, err + } + for _, groupInfo := range groupInfos { + groupMap[groupInfo.GroupID] = groupInfo + } + } + for conversationID, chatLog := range chatLogs { + pbchatLog := &pbconversation.ConversationElem{} + msgInfo := &pbconversation.MsgInfo{} + if err := utils.CopyStructFields(msgInfo, chatLog); err != nil { + return nil, err + } + switch chatLog.SessionType { + case constant.SingleChatType: + if chatLog.SendID == userID { + msgInfo.FaceURL = sendMap[chatLog.RecvID].FaceURL + msgInfo.SenderName = sendMap[chatLog.RecvID].Nickname + break + } + msgInfo.FaceURL = sendMap[chatLog.SendID].FaceURL + msgInfo.SenderName = sendMap[chatLog.SendID].Nickname + case constant.GroupChatType, constant.SuperGroupChatType: + msgInfo.GroupName = groupMap[chatLog.GroupID].GroupName + msgInfo.GroupFaceURL = groupMap[chatLog.GroupID].FaceURL + msgInfo.GroupMemberCount = groupMap[chatLog.GroupID].MemberCount + msgInfo.GroupID = chatLog.GroupID + msgInfo.GroupType = groupMap[chatLog.GroupID].GroupType + msgInfo.SenderName = sendMap[chatLog.SendID].Nickname + } + pbchatLog.ConversationID = conversationID + msgInfo.LatestMsgRecvTime = chatLog.SendTime + pbchatLog.MsgInfo = msgInfo + conversationMsg[conversationID] = pbchatLog + } + return conversationMsg, nil +} diff --git a/internal/rpc/msg/as_read.go b/internal/rpc/msg/as_read.go index 5324ccba8..71e038b39 100644 --- a/internal/rpc/msg/as_read.go +++ b/internal/rpc/msg/as_read.go @@ -18,6 +18,7 @@ import ( "context" utils2 "github.com/OpenIMSDK/tools/utils" + cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" "github.com/redis/go-redis/v9" @@ -26,8 +27,6 @@ import ( "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" - - cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" ) func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *msg.GetConversationsHasReadAndMaxSeqReq) (resp *msg.GetConversationsHasReadAndMaxSeqResp, err error) { diff --git a/internal/rpc/msg/seq.go b/internal/rpc/msg/seq.go index 4f6a01e8d..c12f258b7 100644 --- a/internal/rpc/msg/seq.go +++ b/internal/rpc/msg/seq.go @@ -16,7 +16,6 @@ package msg import ( "context" - pbmsg "github.com/OpenIMSDK/protocol/msg" ) @@ -30,3 +29,27 @@ func (m *msgServer) GetConversationMaxSeq( } return &pbmsg.GetConversationMaxSeqResp{MaxSeq: maxSeq}, nil } + +func (m *msgServer) GetMaxSeqs(ctx context.Context, req *pbmsg.GetMaxSeqsReq) (*pbmsg.SeqsInfoResp, error) { + maxSeqs, err := m.MsgDatabase.GetMaxSeqs(ctx, req.ConversationIDs) + if err != nil { + return nil, err + } + return &pbmsg.SeqsInfoResp{MaxSeqs: maxSeqs}, nil +} + +func (m *msgServer) GetHasReadSeqs(ctx context.Context, req *pbmsg.GetHasReadSeqsReq) (*pbmsg.SeqsInfoResp, error) { + hasReadSeqs, err := m.MsgDatabase.GetHasReadSeqs(ctx, req.UserID, req.ConversationIDs) + if err != nil { + return nil, err + } + return &pbmsg.SeqsInfoResp{MaxSeqs: hasReadSeqs}, nil +} + +func (m *msgServer) GetMsgByConversationIDs(ctx context.Context, req *pbmsg.GetMsgByConversationIDsReq) (*pbmsg.GetMsgByConversationIDsResp, error) { + Msgs, err := m.MsgDatabase.FindOneByDocIDs(ctx, req.ConversationIDs, req.MaxSeqs) + if err != nil { + return nil, err + } + return &pbmsg.GetMsgByConversationIDsResp{MsgDatas: Msgs}, nil +} diff --git a/pkg/apistruct/manage.go b/pkg/apistruct/manage.go index a48f5253c..f9f542835 100644 --- a/pkg/apistruct/manage.go +++ b/pkg/apistruct/manage.go @@ -64,6 +64,30 @@ type SendMsgReq struct { SendMsg } +type GetConversationListReq struct { + // userID uniquely identifies the user. + UserID string `protobuf:"bytes,1,opt,name=userID,proto3" json:"userID,omitempty" binding:"required"` + + // ConversationIDs contains a list of unique identifiers for conversations. + ConversationIDs []string `protobuf:"bytes,2,rep,name=conversationIDs,proto3" json:"conversationIDs,omitempty"` +} + +type GetConversationListResp struct { + // ConversationElems is a map that associates conversation IDs with their respective details. + ConversationElems map[string]*ConversationElem `protobuf:"bytes,1,rep,name=conversationElems,proto3" json:"conversationElems,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +type ConversationElem struct { + // MaxSeq represents the maximum sequence number within the conversation. + MaxSeq int64 `protobuf:"varint,1,opt,name=maxSeq,proto3" json:"maxSeq,omitempty"` + + // UnreadSeq represents the number of unread messages in the conversation. + UnreadSeq int64 `protobuf:"varint,2,opt,name=unreadSeq,proto3" json:"unreadSeq,omitempty"` + + // LastSeqTime represents the timestamp of the last sequence in the conversation. + LastSeqTime int64 `protobuf:"varint,3,opt,name=LastSeqTime,proto3" json:"LastSeqTime,omitempty"` +} + // BatchSendMsgReq defines the structure for sending a message to multiple recipients. type BatchSendMsgReq struct { SendMsg diff --git a/pkg/common/db/controller/msg.go b/pkg/common/db/controller/msg.go index fb0a9c702..dfff5c61d 100644 --- a/pkg/common/db/controller/msg.go +++ b/pkg/common/db/controller/msg.go @@ -98,6 +98,7 @@ type CommonMsgDatabase interface { SetSendMsgStatus(ctx context.Context, id string, status int32) error GetSendMsgStatus(ctx context.Context, id string) (int32, error) SearchMessage(ctx context.Context, req *pbmsg.SearchMessageReq) (total int32, msgData []*sdkws.MsgData, err error) + FindOneByDocIDs(ctx context.Context, docIDs []string, seqs map[string]int64) (map[string]*sdkws.MsgData, error) // to mq MsgToMQ(ctx context.Context, key string, msg2mq *sdkws.MsgData) error @@ -1051,6 +1052,21 @@ func (db *commonMsgDatabase) SearchMessage(ctx context.Context, req *pbmsg.Searc return total, totalMsgs, nil } +func (db *commonMsgDatabase) FindOneByDocIDs(ctx context.Context, conversationIDs []string, seqs map[string]int64) (map[string]*sdkws.MsgData, error) { + totalMsgs := make(map[string]*sdkws.MsgData) + for _, conversationID := range conversationIDs { + seq := seqs[conversationID] + docID := db.msg.GetDocID(conversationID, seq) + msgs, err := db.msgDocDatabase.FindOneByDocID(ctx, docID) + if err != nil { + return nil, err + } + index := db.msg.GetMsgIndex(seq) + totalMsgs[conversationID] = convert.MsgDB2Pb(msgs.Msg[index].Msg) + } + return totalMsgs, nil +} + func (db *commonMsgDatabase) ConvertMsgsDocLen(ctx context.Context, conversationIDs []string) { db.msgDocDatabase.ConvertMsgsDocLen(ctx, conversationIDs) } diff --git a/pkg/rpcclient/msg.go b/pkg/rpcclient/msg.go index 03769dc74..abad0075a 100644 --- a/pkg/rpcclient/msg.go +++ b/pkg/rpcclient/msg.go @@ -17,7 +17,6 @@ package rpcclient import ( "context" "encoding/json" - "google.golang.org/grpc" "google.golang.org/protobuf/proto" @@ -157,6 +156,30 @@ func (m *MessageRpcClient) GetMaxSeq(ctx context.Context, req *sdkws.GetMaxSeqRe return resp, err } +func (m *MessageRpcClient) GetMaxSeqs(ctx context.Context, conversationIDs []string) (map[string]int64, error) { + log.ZDebug(ctx, "GetMaxSeqs", "conversationIDs", conversationIDs) + resp, err := m.Client.GetMaxSeqs(ctx, &msg.GetMaxSeqsReq{ + ConversationIDs: conversationIDs, + }) + return resp.MaxSeqs, err +} + +func (m *MessageRpcClient) GetHasReadSeqs(ctx context.Context, userID string, conversationIDs []string) (map[string]int64, error) { + resp, err := m.Client.GetHasReadSeqs(ctx, &msg.GetHasReadSeqsReq{ + UserID: userID, + ConversationIDs: conversationIDs, + }) + return resp.MaxSeqs, err +} + +func (m *MessageRpcClient) GetMsgByConversationIDs(ctx context.Context, docIDs []string, seqs map[string]int64) (map[string]*sdkws.MsgData, error) { + resp, err := m.Client.GetMsgByConversationIDs(ctx, &msg.GetMsgByConversationIDsReq{ + ConversationIDs: docIDs, + MaxSeqs: seqs, + }) + return resp.MsgDatas, err +} + func (m *MessageRpcClient) PullMessageBySeqList(ctx context.Context, req *sdkws.PullMessageBySeqsReq) (*sdkws.PullMessageBySeqsResp, error) { resp, err := m.Client.PullMessageBySeqs(ctx, req) return resp, err From c68a61d8c240143f1b4d58d5d82bb44e807fb5ad Mon Sep 17 00:00:00 2001 From: AndrewZuo01 <59896149+AndrewZuo01@users.noreply.github.com> Date: Fri, 29 Dec 2023 20:38:43 +0800 Subject: [PATCH 15/20] fix setUserInfoEx (#1635) * update set pin friends * update set pin friends * update set pin friends * update set pin friends * update set pin friends * update set pin friends * fix bugs * fix bugs * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * Update go.mod * Update friend.go * debug * debug * debug * add pin friend test * add pin friend test * add pin friend test * add pin friend test * add pin friend test * add pin friend test * add pin friend test * add pin friend test * add pin friend test * I cannot solve todo in test.sh * update user command * update user command * update user command * update user command * update user command * update user command * update user command * update user command * update user command * update user command * update user command * update user command * update user command * update user command * Update go.mod * fix group notification * fix group notification * update openimsdk tools * update openim server remove duplicate code * update openim server remove duplicate code * update user command get * update user command get * update response of callback response error * update black ex * update join group ex * update user pb2map * update go sum * update go sum * update updateUserInfoEx * update updateUserInfoEx * update updateUserInfoEx add callback functions * fix dismiss group function * fix dismiss group function * fix dismiss group function * fix dismiss group function * update pin friend to update friend * fix go mod * fix err golangci-lint * fix UserPb2DBMap * update comments, update go.mod check for register username * update comments, update go.mod check for register username * update comments, update go.mod check for register username * update comments, update go.mod check for register username * fix callback * fix go.mod * fix debug * fix bugs * update notification * update notification * update notification * update notification * update notification * update notification * update notification * update notification * fix updateUserInfoEx * fix updateUserInfoEx * modify go.mod * fix updateUserInfoEx * fix updateUserInfoEx --------- Co-authored-by: Xinwei Xiong <3293172751@qq.com> --- go.mod | 4 ++-- internal/rpc/user/callback.go | 9 ++++----- internal/rpc/user/user.go | 2 +- pkg/callbackstruct/user.go | 12 ++++++------ pkg/common/convert/user.go | 13 ++++++++----- 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 2db34a8b1..393e742de 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( firebase.google.com/go v3.13.0+incompatible - github.com/OpenIMSDK/protocol v0.0.41 + github.com/OpenIMSDK/protocol v0.0.42 github.com/OpenIMSDK/tools v0.0.21 github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/dtm-labs/rockscache v0.1.1 @@ -155,4 +155,4 @@ require ( go.uber.org/zap v1.24.0 // indirect golang.org/x/crypto v0.14.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect -) +) \ No newline at end of file diff --git a/internal/rpc/user/callback.go b/internal/rpc/user/callback.go index 9e02eb130..092a66a07 100644 --- a/internal/rpc/user/callback.go +++ b/internal/rpc/user/callback.go @@ -16,7 +16,6 @@ package user import ( "context" - pbuser "github.com/OpenIMSDK/protocol/user" "github.com/OpenIMSDK/tools/utils" @@ -67,16 +66,16 @@ func CallbackBeforeUpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserI cbReq := &cbapi.CallbackBeforeUpdateUserInfoExReq{ CallbackCommand: cbapi.CallbackBeforeUpdateUserInfoExCommand, UserID: req.UserInfo.UserID, - FaceURL: &req.UserInfo.FaceURL, - Nickname: &req.UserInfo.Nickname, + FaceURL: req.UserInfo.FaceURL, + Nickname: req.UserInfo.Nickname, } resp := &cbapi.CallbackBeforeUpdateUserInfoExResp{} if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackBeforeUpdateUserInfoEx); err != nil { return err } - utils.NotNilReplace(&req.UserInfo.FaceURL, resp.FaceURL) + utils.NotNilReplace(req.UserInfo.FaceURL, resp.FaceURL) utils.NotNilReplace(req.UserInfo.Ex, resp.Ex) - utils.NotNilReplace(&req.UserInfo.Nickname, resp.Nickname) + utils.NotNilReplace(req.UserInfo.Nickname, resp.Nickname) return nil } func CallbackAfterUpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserInfoExReq) error { diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index fdfd81ed2..5a79fbc91 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -168,7 +168,7 @@ func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUse if err != nil { return nil, err } - if req.UserInfo.Nickname != "" || req.UserInfo.FaceURL != "" { + if req.UserInfo.Nickname != nil || req.UserInfo.FaceURL != nil { if err := s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil { log.ZError(ctx, "NotificationUserInfoUpdate", err) } diff --git a/pkg/callbackstruct/user.go b/pkg/callbackstruct/user.go index bfb69bd38..98536882d 100644 --- a/pkg/callbackstruct/user.go +++ b/pkg/callbackstruct/user.go @@ -47,22 +47,22 @@ type CallbackAfterUpdateUserInfoResp struct { type CallbackBeforeUpdateUserInfoExReq struct { CallbackCommand `json:"callbackCommand"` UserID string `json:"userID"` - Nickname *string `json:"nickName"` - FaceURL *string `json:"faceURL"` + Nickname *wrapperspb.StringValue `json:"nickName"` + FaceURL *wrapperspb.StringValue `json:"faceURL"` Ex *wrapperspb.StringValue `json:"ex"` } type CallbackBeforeUpdateUserInfoExResp struct { CommonCallbackResp - Nickname *string `json:"nickName"` - FaceURL *string `json:"faceURL"` + Nickname *wrapperspb.StringValue `json:"nickName"` + FaceURL *wrapperspb.StringValue `json:"faceURL"` Ex *wrapperspb.StringValue `json:"ex"` } type CallbackAfterUpdateUserInfoExReq struct { CallbackCommand `json:"callbackCommand"` UserID string `json:"userID"` - Nickname string `json:"nickName"` - FaceURL string `json:"faceURL"` + Nickname *wrapperspb.StringValue `json:"nickName"` + FaceURL *wrapperspb.StringValue `json:"faceURL"` Ex *wrapperspb.StringValue `json:"ex"` } type CallbackAfterUpdateUserInfoExResp struct { diff --git a/pkg/common/convert/user.go b/pkg/common/convert/user.go index 6d43595bc..62f80e458 100644 --- a/pkg/common/convert/user.go +++ b/pkg/common/convert/user.go @@ -79,14 +79,17 @@ func UserPb2DBMapEx(user *sdkws.UserInfoWithEx) map[string]any { val := make(map[string]any) // Map fields from UserInfoWithEx to val - val["nickname"] = user.Nickname - val["face_url"] = user.FaceURL - + if user.Nickname != nil { + val["nickname"] = user.Nickname.Value + } + if user.FaceURL != nil { + val["face_url"] = user.FaceURL.Value + } if user.Ex != nil { val["ex"] = user.Ex.Value } - if user.GlobalRecvMsgOpt != 0 { - val["global_recv_msg_opt"] = user.GlobalRecvMsgOpt + if user.GlobalRecvMsgOpt != nil { + val["global_recv_msg_opt"] = user.GlobalRecvMsgOpt.Value } return val From 1e52376d7b6e8e10e0f0a01988faff1ad5a7b011 Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751@qq.com> Date: Fri, 29 Dec 2023 20:43:58 +0800 Subject: [PATCH 16/20] fix: fix openim scripts and ci add openim check (#1632) * fix: fix openim zk env set Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> * fix: fix openim zk env set Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> * feat: add openim docker prom Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> --------- Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> --- .github/workflows/openimci.yml | 1 - deployments/templates/env-template.yaml | 64 ++++++++----------------- docker-compose.yml | 2 +- scripts/check-all.sh | 12 +++-- scripts/install/environment.sh | 2 +- tools/component/component.go | 4 +- 6 files changed, 32 insertions(+), 53 deletions(-) diff --git a/.github/workflows/openimci.yml b/.github/workflows/openimci.yml index d8e988f0b..d16e91ba4 100644 --- a/.github/workflows/openimci.yml +++ b/.github/workflows/openimci.yml @@ -201,7 +201,6 @@ jobs: - name: Build, Start, Check Services and Print Logs for Ubuntu if: runner.os == 'Linux' run: | - sudo make init && \ sudo make build && \ sudo make start && \ sudo make check || \ diff --git a/deployments/templates/env-template.yaml b/deployments/templates/env-template.yaml index ab9c87c02..f98eda1c1 100644 --- a/deployments/templates/env-template.yaml +++ b/deployments/templates/env-template.yaml @@ -1,39 +1,20 @@ -# Copyright © 2023 OpenIM. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# ====================================== -# ========= Basic Configuration ======== -# ====================================== - -# The user for authentication or system operations. -# Default: OPENIM_USER=root -USER=${OPENIM_USER} - -# Password associated with the specified user for authentication. -# Default: PASSWORD=openIM123 -PASSWORD=${PASSWORD} - -# Base URL for the application programming interface (API). -# Default: API_URL=http://172.28.0.1:10002 -API_URL=${API_URL} - -# Directory path for storing data files or related information. -# Default: DATA_DIR=./ +# ----------------------------------------------------------------------------- +# General Configuration +# This section contains general configuration options for the entire environment. +# These options can be set via environment variables. If both environment variables +# and settings in this .env file exist, the environment variables take precedence. +# ----------------------------------------------------------------------------- + +# Local IP address for the service +# To modify, uncomment and replace with the actual IP address +OPENIM_IP=${OPENIM_IP} + +# Data storage directory DATA_DIR=${DATA_DIR} -# Choose the appropriate image address, the default is GITHUB image, -# you can choose docker hub, for Chinese users can choose Ali Cloud +# Choose the image address: GitHub (ghcr.io/openimsdk), Docker Hub (openim), +# or Ali Cloud (registry.cn-hangzhou.aliyuncs.com/openimsdk). +# Uncomment one of the following three options. Aliyun is recommended for users in China. # export IMAGE_REGISTRY="ghcr.io/openimsdk" # export IMAGE_REGISTRY="openim" # export IMAGE_REGISTRY="registry.cn-hangzhou.aliyuncs.com/openimsdk" @@ -47,10 +28,9 @@ IMAGE_REGISTRY=${IMAGE_REGISTRY} # Default: DOCKER_BRIDGE_SUBNET=172.28.0.0/16 DOCKER_BRIDGE_SUBNET=${DOCKER_BRIDGE_SUBNET} -# Gateway for the Docker network. -# Default: DOCKER_BRIDGE_GATEWAY=172.28.0.1 +# Set and specify the IP addresses of some containers. Generally speaking, +# you do not need to modify these configurations to facilitate debugging DOCKER_BRIDGE_GATEWAY=${DOCKER_BRIDGE_GATEWAY} - MONGO_NETWORK_ADDRESS=${MONGO_NETWORK_ADDRESS} REDIS_NETWORK_ADDRESS=${REDIS_NETWORK_ADDRESS} KAFKA_NETWORK_ADDRESS=${KAFKA_NETWORK_ADDRESS} @@ -65,15 +45,13 @@ NODE_EXPORTER_NETWORK_ADDRESS=${NODE_EXPORTER_NETWORK_ADDRESS} OPENIM_ADMIN_FRONT_NETWORK_ADDRESS=${OPENIM_ADMIN_FRONT_NETWORK_ADDRESS} ALERT_MANAGER_NETWORK_ADDRESS=${ALERT_MANAGER_NETWORK_ADDRESS} -# =============================================== -# = Component Extension Configuration = -# =============================================== +# ----------------------------------------------------------------------------- +# Database Configuration +# This section contains environment variable settings related to databases. +# ----------------------------------------------------------------------------- -# ============ Component Extension Configuration ========== # ----- ZooKeeper Configuration ----- # Address or hostname for the ZooKeeper service. -# Default: ZOOKEEPER_ADDRESS=172.28.0.1 -ZOOKEEPER_ADDRESS=${ZOOKEEPER_NETWORK_ADDRESS} # Port for ZooKeeper service. # Default: ZOOKEEPER_PORT=12181 diff --git a/docker-compose.yml b/docker-compose.yml index de3deaaea..bdfee3748 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -122,7 +122,7 @@ services: server: ipv4_address: ${OPENIM_WEB_NETWORK_ADDRESS:-172.28.0.7} - # Uncomment and configure the following services as needed + ## Uncomment and configure the following services as needed # openim-admin: # image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-admin-front:v3.4.0 # container_name: openim-admin diff --git a/scripts/check-all.sh b/scripts/check-all.sh index e1e07bd65..48c38331d 100755 --- a/scripts/check-all.sh +++ b/scripts/check-all.sh @@ -30,23 +30,25 @@ OPENIM_VERBOSE=4 openim::log::info "\n# Begin to check all openim service" -# OpenIM status # Elegant printing function print_services_and_ports() { - service_names=("$1[@]") - service_ports=("$2[@]") + local -n service_names=$1 + local -n service_ports=$2 echo "+-------------------------+----------+" echo "| Service Name | Port |" echo "+-------------------------+----------+" - for index in "${!service_names}"; do - printf "| %-23s | %-8s |\n" "${!service_names[$index]}" "${!service_ports[$index]}" + for index in "${!service_names[@]}"; do + printf "| %-23s | %-8s |\n" "${service_names[$index]}" "${service_ports[$index]}" done echo "+-------------------------+----------+" } +# Assuming OPENIM_SERVER_NAME_TARGETS and OPENIM_SERVER_PORT_TARGETS are defined +# Similarly for OPENIM_DEPENDENCY_TARGETS and OPENIM_DEPENDENCY_PORT_TARGETS + # Print out services and their ports print_services_and_ports OPENIM_SERVER_NAME_TARGETS OPENIM_SERVER_PORT_TARGETS diff --git a/scripts/install/environment.sh b/scripts/install/environment.sh index feb36c3c1..0e3a428e8 100755 --- a/scripts/install/environment.sh +++ b/scripts/install/environment.sh @@ -378,7 +378,7 @@ def "CALLBACK_TIMEOUT" "5" # 最长超时时间 def "CALLBACK_FAILED_CONTINUE" "true" # 失败后是否继续 ###################### Prometheus 配置信息 ###################### # 是否启用 Prometheus -readonly PROMETHEUS_ENABLE=${PROMETHEUS_ENABLE:-'false'} +readonly PROMETHEUS_ENABLE=${PROMETHEUS_ENABLE:-'true'} def "PROMETHEUS_URL" "${GRAFANA_ADDRESS}:${GRAFANA_PORT}" # Api 服务的 Prometheus 端口 readonly API_PROM_PORT=${API_PROM_PORT:-'20100'} diff --git a/tools/component/component.go b/tools/component/component.go index 616ffec2d..e09fa11e6 100644 --- a/tools/component/component.go +++ b/tools/component/component.go @@ -223,8 +223,8 @@ func checkMinio() (string, error) { defer cancel() if minioClient.IsOffline() { - str := fmt.Sprintf("Minio server is offline;%s", str) - return "", ErrComponentStart.Wrap(str) + // str := fmt.Sprintf("Minio server is offline;%s", str) + // return "", ErrComponentStart.Wrap(str) } // Check for localhost in API URL and Minio SignEndpoint From 959c5a7ae11186862743df1a2e3456341d3754c0 Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751@qq.com> Date: Fri, 29 Dec 2023 22:29:52 +0800 Subject: [PATCH 17/20] feat: add openim scripts fix and optimize config about openim source code deployment for this PR? openim ci and scripts (#1641) * fix: fix openim zk env set Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> * fix: fix openim zk env set Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> * feat: add openim docker prom Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> * feat: add openim docker prom Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> --------- Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> --- deployments/templates/env-template.yaml | 124 +++++++++++------------- 1 file changed, 57 insertions(+), 67 deletions(-) diff --git a/deployments/templates/env-template.yaml b/deployments/templates/env-template.yaml index f98eda1c1..e0fd7bc9f 100644 --- a/deployments/templates/env-template.yaml +++ b/deployments/templates/env-template.yaml @@ -4,20 +4,20 @@ # These options can be set via environment variables. If both environment variables # and settings in this .env file exist, the environment variables take precedence. # ----------------------------------------------------------------------------- +# ========================== +# General Configuration +# ========================== +# These settings apply to the overall environment. -# Local IP address for the service -# To modify, uncomment and replace with the actual IP address -OPENIM_IP=${OPENIM_IP} - -# Data storage directory +# Data storage directory for persistent data. +# Example: DATA_DIR=/path/to/data DATA_DIR=${DATA_DIR} -# Choose the image address: GitHub (ghcr.io/openimsdk), Docker Hub (openim), -# or Ali Cloud (registry.cn-hangzhou.aliyuncs.com/openimsdk). -# Uncomment one of the following three options. Aliyun is recommended for users in China. -# export IMAGE_REGISTRY="ghcr.io/openimsdk" -# export IMAGE_REGISTRY="openim" -# export IMAGE_REGISTRY="registry.cn-hangzhou.aliyuncs.com/openimsdk" +# Docker image registry. Uncomment the preferred one. +# Options: ghcr.io/openimsdk, openim, registry.cn-hangzhou.aliyuncs.com/openimsdk +# IMAGE_REGISTRY="ghcr.io/openimsdk" +# IMAGE_REGISTRY="openim" +# IMAGE_REGISTRY="registry.cn-hangzhou.aliyuncs.com/openimsdk" IMAGE_REGISTRY=${IMAGE_REGISTRY} # ====================================== @@ -45,23 +45,45 @@ NODE_EXPORTER_NETWORK_ADDRESS=${NODE_EXPORTER_NETWORK_ADDRESS} OPENIM_ADMIN_FRONT_NETWORK_ADDRESS=${OPENIM_ADMIN_FRONT_NETWORK_ADDRESS} ALERT_MANAGER_NETWORK_ADDRESS=${ALERT_MANAGER_NETWORK_ADDRESS} -# ----------------------------------------------------------------------------- -# Database Configuration -# This section contains environment variable settings related to databases. -# ----------------------------------------------------------------------------- +# ============================================================================== +# Configuration Update Instructions +# ============================================================================== +# This header outlines the methods to update common variables in config.yaml and .env files. +# These instructions are vital for maintaining the OpenIM environment's configuration. +# +# METHOD 1: Regenerate All Configurations +# ---------------------------------------- +# Use this method to regenerate all configurations. +# Steps: +# 1. Delete existing config files: +# - openim-server/config/config.yaml +# - openim-chat/config/config.yaml +# 2. Modify the .env file as required. +# 3. Run 'docker compose up -d'. This will regenerate: +# - config/config.yaml +# +# METHOD 2: Modify Individual Configuration Files +# ----------------------------------------------- +# Use this method to update specific configuration files. +# Steps: +# 1. Modify the .env file as necessary. +# 2. Update the corresponding entries in: +# - config/config.yaml +# 3. Restart the services with 'docker compose up -d'. +# 4. Special Note: If you modify OPENIM_IP, API_OPENIM_PORT, or MINIO_PORT in .env, +# ensure to update the corresponding services and configurations accordingly. +# +# It is essential to follow these methods to ensure consistent and correct application behavior. +# ============================================================================== +# Local IP address of the service. Modify if necessary. +# Example: OPENIM_IP=172.28.0.1, +OPENIM_IP=${OPENIM_IP} # ----- ZooKeeper Configuration ----- -# Address or hostname for the ZooKeeper service. - # Port for ZooKeeper service. # Default: ZOOKEEPER_PORT=12181 ZOOKEEPER_PORT=${ZOOKEEPER_PORT} -# ----- MongoDB Configuration ----- -# Address or hostname for the MongoDB service. -# Default: MONGO_ADDRESS=172.28.0.1 -MONGO_ADDRESS=${MONGO_NETWORK_ADDRESS} - # Port on which MongoDB service is running. # Default: MONGO_PORT=37017 # MONGO_PORT=${MONGO_PORT} @@ -79,9 +101,6 @@ MONGO_PASSWORD=${MONGO_PASSWORD} MONGO_DATABASE=${MONGO_DATABASE} # ----- Redis Configuration ----- -# Address or hostname for the Redis service. -# Default: REDIS_ADDRESS=172.28.0.1 -REDIS_ADDRESS=${REDIS_NETWORK_ADDRESS} # Port on which Redis in-memory data structure store is running. # Default: REDIS_PORT=16379 @@ -91,11 +110,6 @@ REDIS_PORT=${REDIS_PORT} # Default: REDIS_PASSWORD=openIM123 REDIS_PASSWORD=${REDIS_PASSWORD} -# ----- Kafka Configuration ----- -# Address or hostname for the Kafka service. -# Default: KAFKA_ADDRESS=172.28.0.1 -KAFKA_ADDRESS=${KAFKA_NETWORK_ADDRESS} - # Kakfa username to authenticate with the Kafka service. # KAFKA_USERNAME=${KAFKA_USERNAME} @@ -107,20 +121,13 @@ KAFKA_PORT=${KAFKA_PORT} # Default: KAFKA_LATESTMSG_REDIS_TOPIC=latestMsgToRedis KAFKA_LATESTMSG_REDIS_TOPIC=${KAFKA_LATESTMSG_REDIS_TOPIC} -# Topic in Kafka for pushing messages (e.g. notifications or updates). -# Default: KAFKA_MSG_PUSH_TOPIC=msgToPush -KAFKA_MSG_PUSH_TOPIC=${KAFKA_MSG_PUSH_TOPIC} - -# Topic in Kafka for storing offline messages in MongoDB. -# Default: KAFKA_OFFLINEMSG_MONGO_TOPIC=offlineMsgToMongoMysql -KAFKA_OFFLINEMSG_MONGO_TOPIC=${KAFKA_OFFLINEMSG_MONGO_TOPIC} - -# ----- MinIO Configuration ---- -# Address or hostname for the MinIO object storage service. -# Default: MINIO_ADDRESS=172.28.0.1 -MINIO_ADDRESS=${MINIO_NETWORK_ADDRESS} - -# Port on which MinIO object storage service is running. +# MINIO_PORT +# ---------- +# MINIO_PORT sets the port for the MinIO object storage service. +# Upon changing this port, the MinIO endpoint URLs in the `config/config.yaml` file must be updated +# to reflect this change. The endpoints include both the 'endpoint' and 'signEndpoint' +# under the MinIO configuration. +# # Default: MINIO_PORT=10005 MINIO_PORT=${MINIO_PORT} @@ -133,19 +140,11 @@ MINIO_PORT=${MINIO_PORT} MINIO_SECRET_KEY=${MINIO_SECRET_KEY} # ----- Prometheus Configuration ----- -# Address or hostname for the Prometheus service. -# Default: PROMETHEUS_ADDRESS=172.28.0.1 -PROMETHEUS_ADDRESS=${PROMETHEUS_NETWORK_ADDRESS} - # Port on which Prometheus service is running. # Default: PROMETHEUS_PORT=19090 PROMETHEUS_PORT=${PROMETHEUS_PORT} # ----- Grafana Configuration ----- -# Address or hostname for the Grafana service. -# Default: GRAFANA_ADDRESS=172.28.0.1 -GRAFANA_ADDRESS=${GRAFANA_NETWORK_ADDRESS} - # Port on which Grafana service is running. # Default: GRAFANA_PORT=13000 GRAFANA_PORT=${GRAFANA_PORT} @@ -162,23 +161,19 @@ OPENIM_WEB_DIST_PATH=${OPENIM_WEB_DIST_PATH} # Default: OPENIM_WEB_PORT=11001 OPENIM_WEB_PORT=${OPENIM_WEB_PORT} -# Address or hostname for the OpenIM web service. -# Default: OPENIM_WEB_ADDRESS=172.28.0.1 -OPENIM_WEB_ADDRESS=${OPENIM_WEB_NETWORK_ADDRESS} - # ====================================== # ========= OpenIM Server ============== # ====================================== - -# Address or hostname for the OpenIM server. -# Default: OPENIM_SERVER_ADDRESS=172.28.0.1 -OPENIM_SERVER_ADDRESS=${OPENIM_SERVER_NETWORK_ADDRESS} - # Port for the OpenIM WebSockets. # Default: OPENIM_WS_PORT=10001 OPENIM_WS_PORT=${OPENIM_WS_PORT} -# Port for the OpenIM API. +# API_OPENIM_PORT +# --------------- +# This variable defines the port on which the OpenIM API service will listen. +# When changing this port, it's essential to update the apiURL in the config.yaml file +# to ensure the API service is accessible at the new port. +# # Default: API_OPENIM_PORT=10002 API_OPENIM_PORT=${API_OPENIM_PORT} @@ -191,10 +186,6 @@ API_OPENIM_PORT=${API_OPENIM_PORT} # Default: CHAT_IMAGE_VERSION=main CHAT_IMAGE_VERSION=${CHAT_IMAGE_VERSION} -# Address or hostname for the OpenIM chat service. -# Default: OPENIM_CHAT_ADDRESS=172.28.0.1 -OPENIM_CHAT_ADDRESS=${OPENIM_CHAT_NETWORK_ADDRESS} - # Port for the OpenIM chat API. # Default: OPENIM_CHAT_API_PORT=10008 OPENIM_CHAT_API_PORT=${OPENIM_CHAT_API_PORT} @@ -203,7 +194,6 @@ OPENIM_CHAT_API_PORT=${OPENIM_CHAT_API_PORT} # Default: OPENIM_CHAT_DATA_DIR=./openim-chat/main OPENIM_CHAT_DATA_DIR=${OPENIM_CHAT_DATA_DIR} - # ====================================== # ========== OpenIM Admin ============== # ====================================== From a7138cb3b7d93f4c9462e8233a8fd9f52a5245bb Mon Sep 17 00:00:00 2001 From: xuexihuang <1339326187@qq.com> Date: Sat, 30 Dec 2023 10:40:42 +0800 Subject: [PATCH 18/20] feature:grafana web inside (#1636) * feature:grafana web inside * fix: using protocal v0.0.42 * fix: review values(but not use) --- config/templates/config.yaml.template | 2 +- deployments/templates/openim.yaml | 2 +- docker-compose.yml | 6 ++++++ internal/api/third.go | 2 +- pkg/common/config/config.go | 2 +- scripts/install/environment.sh | 3 +-- 6 files changed, 11 insertions(+), 6 deletions(-) diff --git a/config/templates/config.yaml.template b/config/templates/config.yaml.template index 89f11f359..9954a8863 100644 --- a/config/templates/config.yaml.template +++ b/config/templates/config.yaml.template @@ -506,7 +506,7 @@ callback: # The number of ports needs to be consistent with msg_transfer_service_num in script/path_info.sh prometheus: enable: false - prometheusUrl: 172.28.0.1:13000 + grafanaUrl: 172.28.0.1:13000 apiPrometheusPort: [20100] userPrometheusPort: [ 20110 ] friendPrometheusPort: [ 20120 ] diff --git a/deployments/templates/openim.yaml b/deployments/templates/openim.yaml index cbb4a8c6e..96d867e10 100644 --- a/deployments/templates/openim.yaml +++ b/deployments/templates/openim.yaml @@ -506,7 +506,7 @@ callback: # The number of ports needs to be consistent with msg_transfer_service_num in script/path_info.sh prometheus: enable: ${PROMETHEUS_ENABLE} - prometheusUrl: ${PROMETHEUS_URL} + grafanaUrl: ${GRAFANA_URL} apiPrometheusPort: [${API_PROM_PORT}] userPrometheusPort: [ ${USER_PROM_PORT} ] friendPrometheusPort: [ ${FRIEND_PROM_PORT} ] diff --git a/docker-compose.yml b/docker-compose.yml index bdfee3748..5b1e11d4e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -167,6 +167,12 @@ services: # hostname: grafana # user: root # restart: always + # environment: + # - GF_SECURITY_ALLOW_EMBEDDING=true + # - GF_SESSION_COOKIE_SAMESITE=none + # - GF_SESSION_COOKIE_SECURE=true + # - GF_AUTH_ANONYMOUS_ENABLED=true + # - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin # ports: # - "${GRAFANA_PORT:-13000}:3000" # volumes: diff --git a/internal/api/third.go b/internal/api/third.go index 37ec55098..0a1ef0fbe 100644 --- a/internal/api/third.go +++ b/internal/api/third.go @@ -130,5 +130,5 @@ func (o *ThirdApi) SearchLogs(c *gin.Context) { } func GetPrometheus(c *gin.Context) { - c.Redirect(http.StatusFound, config2.Config.Prometheus.PrometheusUrl) + c.Redirect(http.StatusFound, config2.Config.Prometheus.GrafanaUrl) } diff --git a/pkg/common/config/config.go b/pkg/common/config/config.go index ea26ca677..88e87e709 100644 --- a/pkg/common/config/config.go +++ b/pkg/common/config/config.go @@ -314,7 +314,7 @@ type configStruct struct { Prometheus struct { Enable bool `yaml:"enable"` - PrometheusUrl string `yaml:"prometheusUrl"` + GrafanaUrl string `yaml:"grafanaUrl"` ApiPrometheusPort []int `yaml:"apiPrometheusPort"` UserPrometheusPort []int `yaml:"userPrometheusPort"` FriendPrometheusPort []int `yaml:"friendPrometheusPort"` diff --git a/scripts/install/environment.sh b/scripts/install/environment.sh index 0e3a428e8..22a0996fc 100755 --- a/scripts/install/environment.sh +++ b/scripts/install/environment.sh @@ -285,7 +285,6 @@ readonly ALERTMANAGER_SEND_RESOLVED=${ALERTMANAGER_SEND_RESOLVED:-"{SEND_RESOLVE ###################### Grafana 配置信息 ###################### def "GRAFANA_PORT" "13000" # Grafana的端口 def "GRAFANA_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # Grafana的地址 - ###################### RPC Port Configuration Variables ###################### # For launching multiple programs, just fill in multiple ports separated by commas # For example: @@ -379,7 +378,7 @@ def "CALLBACK_FAILED_CONTINUE" "true" # 失败后是否继续 ###################### Prometheus 配置信息 ###################### # 是否启用 Prometheus readonly PROMETHEUS_ENABLE=${PROMETHEUS_ENABLE:-'true'} -def "PROMETHEUS_URL" "${GRAFANA_ADDRESS}:${GRAFANA_PORT}" +readonly GRAFANA_URL=${GRAFANA_URL:-"http://${OPENIM_IP}:${GRAFANA_PORT}/"} # Api 服务的 Prometheus 端口 readonly API_PROM_PORT=${API_PROM_PORT:-'20100'} # User 服务的 Prometheus 端口 From bed112d037333c4a78ba8cf17f490aefbe3ec239 Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751@qq.com> Date: Sat, 30 Dec 2023 11:12:19 +0800 Subject: [PATCH 19/20] Update check-all.sh (#1643) --- scripts/check-all.sh | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/scripts/check-all.sh b/scripts/check-all.sh index 48c38331d..7b53e4a89 100755 --- a/scripts/check-all.sh +++ b/scripts/check-all.sh @@ -30,16 +30,18 @@ OPENIM_VERBOSE=4 openim::log::info "\n# Begin to check all openim service" +# Elegant printing function # Elegant printing function print_services_and_ports() { - local -n service_names=$1 - local -n service_ports=$2 + local service_names=("$@") + local half_length=$((${#service_names[@]} / 2)) + local service_ports=("${service_names[@]:half_length}") echo "+-------------------------+----------+" echo "| Service Name | Port |" echo "+-------------------------+----------+" - for index in "${!service_names[@]}"; do + for ((index=0; index < half_length; index++)); do printf "| %-23s | %-8s |\n" "${service_names[$index]}" "${service_ports[$index]}" done @@ -50,11 +52,10 @@ print_services_and_ports() { # Similarly for OPENIM_DEPENDENCY_TARGETS and OPENIM_DEPENDENCY_PORT_TARGETS # Print out services and their ports -print_services_and_ports OPENIM_SERVER_NAME_TARGETS OPENIM_SERVER_PORT_TARGETS +print_services_and_ports "${OPENIM_SERVER_NAME_TARGETS[@]}" "${OPENIM_SERVER_PORT_TARGETS[@]}" # Print out dependencies and their ports -print_services_and_ports OPENIM_DEPENDENCY_TARGETS OPENIM_DEPENDENCY_PORT_TARGETS - +print_services_and_ports "${OPENIM_DEPENDENCY_TARGETS[@]}" "${OPENIM_DEPENDENCY_PORT_TARGETS[@]}" # OpenIM check echo "++ The port being checked: ${OPENIM_SERVER_PORT_LISTARIES[@]}" @@ -91,4 +92,4 @@ else echo "++++ Check all openim service ports successfully !" fi -set -e \ No newline at end of file +set -e From 49f4e3f0deaa4c165d1f3d2bcc1d35368544401f Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751@qq.com> Date: Mon, 1 Jan 2024 21:11:57 +0800 Subject: [PATCH 20/20] fix: fix Security vulnerability (#1646) Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> --- docker-compose.yml | 2 +- pkg/common/discoveryregister/kubernetes/kubernetes.go | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 5b1e11d4e..fd71896a7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -124,7 +124,7 @@ services: ## Uncomment and configure the following services as needed # openim-admin: - # image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-admin-front:v3.4.0 + # image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-admin:toc-base-open-docker.35 # container_name: openim-admin # restart: always # ports: diff --git a/pkg/common/discoveryregister/kubernetes/kubernetes.go b/pkg/common/discoveryregister/kubernetes/kubernetes.go index 2ff1539e6..c10518056 100644 --- a/pkg/common/discoveryregister/kubernetes/kubernetes.go +++ b/pkg/common/discoveryregister/kubernetes/kubernetes.go @@ -55,14 +55,17 @@ func (cli *K8sDR) Register(serviceName, host string, port int, opts ...grpc.Dial return nil } + func (cli *K8sDR) UnRegister() error { return nil } + func (cli *K8sDR) CreateRpcRootNodes(serviceNames []string) error { return nil } + func (cli *K8sDR) RegisterConf2Registry(key string, conf []byte) error { return nil @@ -123,6 +126,8 @@ func getMsgGatewayHost(ctx context.Context) []string { log.ZInfo(ctx, "getMsgGatewayHost", "instance", instance, "selfPodName", selfPodName, "replicas", replicas, "ns", ns, "ret", ret) return ret } + +// GetConns returns the gRPC client connections to the specified service. func (cli *K8sDR) GetConns(ctx context.Context, serviceName string, opts ...grpc.DialOption) ([]*grpc.ClientConn, error) { if serviceName != config.Config.RpcRegisterName.OpenImMessageGatewayName { @@ -142,6 +147,7 @@ func (cli *K8sDR) GetConns(ctx context.Context, serviceName string, opts ...grpc return ret, nil } } + func (cli *K8sDR) GetConn(ctx context.Context, serviceName string, opts ...grpc.DialOption) (*grpc.ClientConn, error) { return grpc.DialContext(ctx, serviceName, append(cli.options, opts...)...) @@ -151,9 +157,11 @@ func (cli *K8sDR) GetSelfConnTarget() string { return cli.rpcRegisterAddr } + func (cli *K8sDR) AddOption(opts ...grpc.DialOption) { cli.options = append(cli.options, opts...) } + func (cli *K8sDR) CloseConn(conn *grpc.ClientConn) { conn.Close() }