diff --git a/tools/codescan/config.yaml b/.github/code-language-detector.yml similarity index 100% rename from tools/codescan/config.yaml rename to .github/code-language-detector.yml diff --git a/.github/workflows/bot-auto-cherry-pick.yml b/.github/workflows/bot-auto-cherry-pick.yml index baafecad7..cdd7241e2 100644 --- a/.github/workflows/bot-auto-cherry-pick.yml +++ b/.github/workflows/bot-auto-cherry-pick.yml @@ -25,6 +25,7 @@ jobs: - name: Comment cherry-pick command uses: actions/github-script@v7 with: + github-token: ${{ secrets.BOT_GITHUB_TOKEN }} script: | const pr = context.payload.pull_request; if (!pr.merged) { @@ -63,5 +64,4 @@ jobs: repo: context.repo.repo, issue_number: pr.number, body: cherryPickCmd - }); - github-token: ${{ secrets.BOT_GITHUB_TOKEN }} \ No newline at end of file + }); \ No newline at end of file diff --git a/.github/workflows/bot-cherry-pick.yml b/.github/workflows/bot-cherry-pick.yml index e488e97de..71597189c 100644 --- a/.github/workflows/bot-cherry-pick.yml +++ b/.github/workflows/bot-cherry-pick.yml @@ -12,23 +12,57 @@ # See the License for the specific language governing permissions and # limitations under the License. -name: Github Rebot for Cherry Pick On Comment +name: Github Robot for Cherry Pick On Comment + on: issue_comment: types: [created] + jobs: cherry-pick: name: Cherry Pick - # && github.event.comment.user.login=='kubbot' if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/cherry-pick') runs-on: ubuntu-latest + steps: - name: Checkout the latest code uses: actions/checkout@v4 with: token: ${{ secrets.BOT_GITHUB_TOKEN }} - fetch-depth: 0 # otherwise, you will fail to push refs to dest repo + fetch-depth: 0 # To ensure all history is available for cherry-picking + - name: Automatic Cherry Pick uses: vendoo/gha-cherry-pick@v1 + with: + # Assuming the cherry-pick commit SHA is passed in the comment like '/cherry-pick sha' + commit-sha: ${{ github.event.comment.body }} env: - GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }} + + - name: Create a new branch for PR + run: | + PR_BRANCH="cherry-pick-${GITHUB_SHA}-to-${{ github.base_ref }}" + git checkout -b $PR_BRANCH + git push origin $PR_BRANCH + env: + GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }} + + - name: Create Pull Request + uses: actions/github-script@v5 + with: + script: | + const prTitle = "Cherry-pick to ${{ github.base_ref }}" + const prBody = "Automated cherry-pick of ${{ github.event.comment.body }}\n\n/cc @kubbot" + const base = "${{ github.base_ref }}" + const head = "cherry-pick-${{ github.sha }}-to-${{ github.base_ref }}" + const createPr = await github.rest.pulls.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: prTitle, + body: prBody, + head: head, + base: base, + maintainer_can_modify: true, // Allows maintainers to edit the PR + }) + env: + GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }} diff --git a/.github/workflows/code-language-detector.yml b/.github/workflows/code-language-detector.yml new file mode 100644 index 000000000..80ec94733 --- /dev/null +++ b/.github/workflows/code-language-detector.yml @@ -0,0 +1,27 @@ +# Copyright © 2024 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Language Check Workflow Test + +on: [pull_request] + +jobs: + comment-language-detector: + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Code Language Detector + uses: kubecub/comment-lang-detector@v1.0.0 \ No newline at end of file diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index b5f901d25..f98221e41 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -name: OpenIM Linux System E2E Test +name: OpenIM E2E And API Test on: workflow_dispatch: @@ -82,7 +82,7 @@ jobs: sudo make tidy sudo make tools.verify.go-gitlint - - name: Build, Start + - name: Build, Start(make build && make start) run: | sudo ./scripts/install/install.sh -i @@ -90,9 +90,8 @@ jobs: run: | sudo ./scripts/install/install.sh -s - - name: Exec OpenIM API test + - name: Exec OpenIM API test (make test-api) run: | - sudo make test-api mkdir -p ./tmp touch ./tmp/test.md echo "# OpenIM Test" >> ./tmp/test.md @@ -103,9 +102,10 @@ jobs: echo "" >> ./tmp/test.md echo "" >> ./tmp/test.md - - name: Exec OpenIM E2E Test + sudo make test-api + + - name: Exec OpenIM E2E Test (make test-e2e) run: | - sudo make test-e2e echo "" >> ./tmp/test.md echo "## OpenIM E2E Test" >> ./tmp/test.md echo "
Command Output for OpenIM E2E Test" >> ./tmp/test.md @@ -114,6 +114,8 @@ jobs: echo "" >> ./tmp/test.md echo "
" >> ./tmp/test.md + sudo make test-e2e + - name: Comment PR with file uses: thollander/actions-comment-pull-request@v2 with: @@ -143,4 +145,4 @@ jobs: PUBLISH_BRANCH: gh-pages env: GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }} - continue-on-error: true \ No newline at end of file + continue-on-error: true diff --git a/.github/workflows/milestone.yml b/.github/workflows/milestone.yml index ecd64656a..c74e7074a 100644 --- a/.github/workflows/milestone.yml +++ b/.github/workflows/milestone.yml @@ -41,6 +41,7 @@ jobs: steps: - uses: actions/github-script@v7 # v6 with: + github-token: ${{ secrets.BOT_GITHUB_TOKEN }} script: | if (!context.payload.pull_request.merged) { console.log('PR was not merged, skipping.'); @@ -56,9 +57,10 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, state: 'open', - sort: 'due_on', - direction: 'asc' + sort: 'title', + direction: 'desc' }) + if (milestones.data.length === 0) { console.log('There are no milestones, skipping.'); return; diff --git a/.golangci.yml b/.golangci.yml index 9c7960642..c262cfa2f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -745,7 +745,7 @@ linters: - misspell # Spelling mistakes - staticcheck # Static analysis - unused # Checks for unused code - - goimports # Checks if imports are correctly sorted and formatted + # - goimports # Checks if imports are correctly sorted and formatted - godot # Checks for comment punctuation - bodyclose # Ensures HTTP response body is closed - stylecheck # Style checker for Go code diff --git a/Dockerfile b/Dockerfile index 514a63db4..d4c2e2602 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,6 +11,7 @@ ENV GOPROXY=$GOPROXY # Set up the working directory WORKDIR /openim/openim-server + # Copy all files to the container ADD . . diff --git a/deployments/templates/env-template.yaml b/deployments/templates/env-template.yaml index 85619b422..b1fb8b78b 100644 --- a/deployments/templates/env-template.yaml +++ b/deployments/templates/env-template.yaml @@ -143,7 +143,7 @@ KAFKA_LATESTMSG_REDIS_TOPIC=${KAFKA_LATESTMSG_REDIS_TOPIC} # 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 +# 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. # diff --git a/docker-compose.yml b/docker-compose.yml index 8538eec83..ef0714329 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -186,23 +186,6 @@ services: # server: # ipv4_address: ${OPENIM_SERVER_NETWORK_ADDRESS:-172.28.0.8} -### TODO: mysql is required to deploy the openim-chat component - # mysql: - # image: mysql:${MYSQL_IMAGE_VERSION:-5.7} - # platform: linux/amd64 - # ports: - # - "${MYSQL_PORT:-13306}:3306" - # container_name: mysql - # volumes: - # - "${DATA_DIR:-./}/components/mysql/data:/var/lib/mysql" - # - "/etc/localtime:/etc/localtime" - # environment: - # MYSQL_ROOT_PASSWORD: "${MYSQL_PASSWORD:-openIM123}" - # restart: always - # networks: - # server: - # ipv4_address: ${MYSQL_NETWORK_ADDRESS:-172.28.0.15} - # openim-chat: # image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-chat:${CHAT_IMAGE_VERSION:-main} # container_name: openim-chat diff --git a/docs/contrib/version.md b/docs/contrib/version.md index 574badf59..337aa4f7d 100644 --- a/docs/contrib/version.md +++ b/docs/contrib/version.md @@ -96,7 +96,6 @@ We reinforce our approach to branch management and versioning with stringent tes This document describes the maximum version skew supported between various openim components. Specific cluster deployment tools may place additional restrictions on version skew. - ### Supported version skew In highly-available (HA) clusters, the newest and oldest `openim-api` instances must be within one minor version. @@ -210,6 +209,7 @@ git merge release-v3.1 # Push the updates to the main branch git push origin main ``` + ## Release Process ``` @@ -232,5 +232,7 @@ For more details on managing Docker image versions, visit [OpenIM Docker Images More on multi-branch version management design and version management design at helm charts: -+ https://github.com/openimsdk/open-im-server/issues/1695 -+ https://github.com/openimsdk/open-im-server/issues/1662 \ No newline at end of file +About Helm's version management strategy for Multiple Apps and multiple Services: + ++ [中文版本管理文档](https://github.com/openimsdk/helm-charts/blob/main/docs/contrib/version-zh.md) ++ [English version management documents](https://github.com/openimsdk/helm-charts/blob/main/docs/contrib/version.md) diff --git a/go.work b/go.work index 8dc91a631..56eb874d4 100644 --- a/go.work +++ b/go.work @@ -3,7 +3,6 @@ go 1.19 use ( . ./test/typecheck - ./tools/codescan ./tools/changelog ./tools/component ./tools/formitychecker diff --git a/internal/api/msg.go b/internal/api/msg.go index d38c14d4e..ad5001459 100644 --- a/internal/api/msg.go +++ b/internal/api/msg.go @@ -215,25 +215,22 @@ func (m *MessageApi) SendMessage(c *gin.Context) { // 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 apiresp.GinError(c, err) return } // Set the status to successful if the message is sent. - status = constant.MsgSendSuccessed + var status int = 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. apiresp.GinError(c, err) diff --git a/internal/msggateway/context.go b/internal/msggateway/context.go index 85fe5c734..ad679c1a1 100644 --- a/internal/msggateway/context.go +++ b/internal/msggateway/context.go @@ -141,7 +141,6 @@ func (c *UserConnContext) GetBackground() bool { b, err := strconv.ParseBool(c.Req.URL.Query().Get(BackgroundStatus)) if err != nil { return false - } else { - return b } + return b } diff --git a/internal/msggateway/user_map.go b/internal/msggateway/user_map.go index b4cec59fa..89eb20de6 100644 --- a/internal/msggateway/user_map.go +++ b/internal/msggateway/user_map.go @@ -19,7 +19,6 @@ import ( "sync" "github.com/OpenIMSDK/tools/log" - "github.com/OpenIMSDK/tools/utils" ) type UserMap struct { @@ -71,48 +70,65 @@ func (u *UserMap) Set(key string, v *Client) { } func (u *UserMap) delete(key string, connRemoteAddr string) (isDeleteUser bool) { + // Attempt to load the clients associated with the key. allClients, existed := u.m.Load(key) - if existed { - oldClients := allClients.([]*Client) - var a []*Client - for _, client := range oldClients { - if client.ctx.GetRemoteAddr() != connRemoteAddr { - a = append(a, client) - } - } - if len(a) == 0 { - u.m.Delete(key) - return true - } else { - u.m.Store(key, a) - return false + if !existed { + // Return false immediately if the key does not exist. + return false + } + + // Convert allClients to a slice of *Client. + oldClients := allClients.([]*Client) + var remainingClients []*Client + for _, client := range oldClients { + // Keep clients that do not match the connRemoteAddr. + if client.ctx.GetRemoteAddr() != connRemoteAddr { + remainingClients = append(remainingClients, client) } } - return existed + + // If no clients remain after filtering, delete the key from the map. + if len(remainingClients) == 0 { + u.m.Delete(key) + return true + } + + // Otherwise, update the key with the remaining clients. + u.m.Store(key, remainingClients) + return false } -func (u *UserMap) deleteClients(key string, clients []*Client) (isDeleteUser bool) { - m := utils.SliceToMapAny(clients, func(c *Client) (string, struct{}) { - return c.ctx.GetRemoteAddr(), struct{}{} - }) +func (u *UserMap) deleteClients(key string, clientsToDelete []*Client) (isDeleteUser bool) { + // Convert the slice of clients to delete into a map for efficient lookup. + deleteMap := make(map[string]struct{}) + for _, client := range clientsToDelete { + deleteMap[client.ctx.GetRemoteAddr()] = struct{}{} + } + + // Load the current clients associated with the key. allClients, existed := u.m.Load(key) - if existed { - oldClients := allClients.([]*Client) - var a []*Client - for _, client := range oldClients { - if _, ok := m[client.ctx.GetRemoteAddr()]; !ok { - a = append(a, client) - } - } - if len(a) == 0 { - u.m.Delete(key) - return true - } else { - u.m.Store(key, a) - return false + if !existed { + // If the key doesn't exist, return false. + return false + } + + // Filter out clients that are in the deleteMap. + oldClients := allClients.([]*Client) + var remainingClients []*Client + for _, client := range oldClients { + if _, shouldBeDeleted := deleteMap[client.ctx.GetRemoteAddr()]; !shouldBeDeleted { + remainingClients = append(remainingClients, client) } } - return existed + + // Update or delete the key based on the remaining clients. + if len(remainingClients) == 0 { + u.m.Delete(key) + return true + } + + u.m.Store(key, remainingClients) + return false } func (u *UserMap) DeleteAll(key string) { diff --git a/internal/msgtransfer/online_history_msg_handler.go b/internal/msgtransfer/online_history_msg_handler.go index b81bd12b8..50fc93369 100644 --- a/internal/msgtransfer/online_history_msg_handler.go +++ b/internal/msgtransfer/online_history_msg_handler.go @@ -184,12 +184,11 @@ func (och *OnlineHistoryRedisConsumerHandler) getPushStorageMsgList( options2 := msgprocessor.Options(msg.Options) if options2.IsHistory() { return true - } else { - // if !(!options2.IsSenderSync() && conversationID == msg.MsgData.SendID) { - // return false - // } - return false } + // if !(!options2.IsSenderSync() && conversationID == msg.MsgData.SendID) { + // return false + // } + return false } for _, v := range totalMsgs { options := msgprocessor.Options(v.message.Options) diff --git a/internal/push/offlinepush/getui/push.go b/internal/push/offlinepush/getui/push.go index 67f6292db..1a95727e5 100644 --- a/internal/push/offlinepush/getui/push.go +++ b/internal/push/offlinepush/getui/push.go @@ -90,9 +90,8 @@ func (g *Client) Push(ctx context.Context, userIDs []string, title, content stri for i, v := range s.GetSplitResult() { go func(index int, userIDs []string) { defer wg.Done() - if err := g.batchPush(ctx, token, userIDs, pushReq); err != nil { + if err = g.batchPush(ctx, token, userIDs, pushReq); err != nil { log.ZError(ctx, "batchPush failed", err, "index", index, "token", token, "req", pushReq) - err = err } }(i, v.Item) } diff --git a/internal/push/push_rpc_server.go b/internal/push/push_rpc_server.go index a8bb18974..b68b06666 100644 --- a/internal/push/push_rpc_server.go +++ b/internal/push/push_rpc_server.go @@ -90,9 +90,8 @@ func (r *pushServer) PushMsg(ctx context.Context, pbData *pbpush.PushMsgReq) (re if err != nil { if err != errNoOfflinePusher { return nil, err - } else { - log.ZWarn(ctx, "offline push failed", err, "msg", pbData.String()) } + log.ZWarn(ctx, "offline push failed", err, "msg", pbData.String()) } return &pbpush.PushMsgResp{}, nil } diff --git a/internal/push/tools.go b/internal/push/tools.go index 3242767b1..760c8c95b 100644 --- a/internal/push/tools.go +++ b/internal/push/tools.go @@ -26,7 +26,6 @@ func GetContent(msg *sdkws.MsgData) string { _ = proto.Unmarshal(msg.Content, &tips) content := tips.JsonDetail return content - } else { - return string(msg.Content) } + return string(msg.Content) } diff --git a/internal/rpc/friend/black.go b/internal/rpc/friend/black.go index 64c63eb73..4e130360c 100644 --- a/internal/rpc/friend/black.go +++ b/internal/rpc/friend/black.go @@ -79,9 +79,14 @@ func (s *friendServer) AddBlack(ctx context.Context, req *pbfriend.AddBlackReq) CreateTime: time.Now(), Ex: req.Ex, } + if err := s.blackDatabase.Create(ctx, []*relation.BlackModel{&black}); err != nil { return nil, err } - s.notificationSender.BlackAddedNotification(ctx, req) + + if err := s.notificationSender.BlackAddedNotification(ctx, req); err != nil { + return nil, err + } + return &pbfriend.AddBlackResp{}, nil } diff --git a/internal/rpc/friend/friend.go b/internal/rpc/friend/friend.go index 6403a4159..4df4085a9 100644 --- a/internal/rpc/friend/friend.go +++ b/internal/rpc/friend/friend.go @@ -114,26 +114,36 @@ func (s *friendServer) ApplyToAddFriend(ctx context.Context, req *pbfriend.Apply if err := authverify.CheckAccessV3(ctx, req.FromUserID, s.config); err != nil { return nil, err } + if req.ToUserID == req.FromUserID { return nil, errs.ErrCanNotAddYourself.Wrap("req.ToUserID", req.ToUserID) } + if err = CallbackBeforeAddFriend(ctx, s.config, req); err != nil && err != errs.ErrCallbackContinue { return nil, err } + if _, err := s.userRpcClient.GetUsersInfoMap(ctx, []string{req.ToUserID, req.FromUserID}); err != nil { return nil, err } + in1, in2, err := s.friendDatabase.CheckIn(ctx, req.FromUserID, req.ToUserID) if err != nil { return nil, err } + if in1 && in2 { return nil, errs.ErrRelationshipAlready.Wrap() } + if err = s.friendDatabase.AddFriendRequest(ctx, req.FromUserID, req.ToUserID, req.ReqMsg, req.Ex); err != nil { return nil, err } - s.notificationSender.FriendApplicationAddNotification(ctx, req) + + if err = s.notificationSender.FriendApplicationAddNotification(ctx, req); err != nil { + return nil, err + } + if err = CallbackAfterAddFriend(ctx, s.config, req); err != nil && err != errs.ErrCallbackContinue { return nil, err } @@ -197,7 +207,9 @@ func (s *friendServer) RespondFriendApply(ctx context.Context, req *pbfriend.Res if err != nil { return nil, err } - s.notificationSender.FriendApplicationAgreedNotification(ctx, req) + if err := s.notificationSender.FriendApplicationAgreedNotification(ctx, req); err != nil { + return nil, err + } return resp, nil } if req.HandleResult == constant.FriendResponseRefuse { diff --git a/internal/rpc/group/cache.go b/internal/rpc/group/cache.go index 54b60c554..35c631eff 100644 --- a/internal/rpc/group/cache.go +++ b/internal/rpc/group/cache.go @@ -33,10 +33,7 @@ func (s *groupServer) GetGroupInfoCache( return resp, nil } -func (s *groupServer) GetGroupMemberCache( - ctx context.Context, - req *pbgroup.GetGroupMemberCacheReq, -) (resp *pbgroup.GetGroupMemberCacheResp, err error) { +func (s *groupServer) GetGroupMemberCache(ctx context.Context, req *pbgroup.GetGroupMemberCacheReq) (resp *pbgroup.GetGroupMemberCacheResp, err error) { members, err := s.db.TakeGroupMember(ctx, req.GroupID, req.GroupMemberID) if err != nil { return nil, err diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index d966bfad8..92d9e27d6 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -637,6 +637,7 @@ func (s *groupServer) GetGroupMembersInfo(ctx context.Context, req *pbgroup.GetG return resp, nil } +// GetGroupApplicationList handles functions that get a list of group requests. func (s *groupServer) GetGroupApplicationList(ctx context.Context, req *pbgroup.GetGroupApplicationListReq) (*pbgroup.GetGroupApplicationListResp, error) { groupIDs, err := s.db.FindUserManagedGroupID(ctx, req.FromUserID) if err != nil { @@ -951,6 +952,7 @@ func (s *groupServer) SetGroupInfo(ctx context.Context, req *pbgroup.SetGroupInf return nil, errs.Wrap(errs.ErrDismissedAlready) } resp := &pbgroup.SetGroupInfoResp{} + count, err := s.db.FindGroupMemberNum(ctx, group.GroupID) if err != nil { return nil, err @@ -1077,6 +1079,7 @@ func (s *groupServer) GetGroups(ctx context.Context, req *pbgroup.GetGroupsReq) total, group, err = s.db.SearchGroup(ctx, req.GroupName, req.Pagination) resp.Total = uint32(total) } + if err != nil { return nil, err } @@ -1084,10 +1087,12 @@ func (s *groupServer) GetGroups(ctx context.Context, req *pbgroup.GetGroupsReq) groupIDs := utils.Slice(group, func(e *relationtb.GroupModel) string { return e.GroupID }) + ownerMembers, err := s.db.FindGroupsOwner(ctx, groupIDs) if err != nil { return nil, err } + ownerMemberMap := utils.SliceToMap(ownerMembers, func(e *relationtb.GroupMemberModel) string { return e.GroupID }) diff --git a/internal/rpc/msg/callback.go b/internal/rpc/msg/callback.go index 536402bf9..927bbe0c2 100644 --- a/internal/rpc/msg/callback.go +++ b/internal/rpc/msg/callback.go @@ -156,27 +156,27 @@ func callbackMsgModify(ctx context.Context, globalConfig *config.GlobalConfig, m return nil } func CallbackGroupMsgRead(ctx context.Context, globalConfig *config.GlobalConfig, req *cbapi.CallbackGroupMsgReadReq) error { - if !globalConfig.Callback.CallbackGroupMsgRead.Enable || req.ContentType != constant.Text { + if !globalConfig.Callback.CallbackGroupMsgRead.Enable { return nil } req.CallbackCommand = cbapi.CallbackGroupMsgReadCommand resp := &cbapi.CallbackGroupMsgReadResp{} - if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackMsgModify); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackGroupMsgRead); err != nil { return err } return nil } func CallbackSingleMsgRead(ctx context.Context, globalConfig *config.GlobalConfig, req *cbapi.CallbackSingleMsgReadReq) error { - if !globalConfig.Callback.CallbackSingleMsgRead.Enable || req.ContentType != constant.Text { + if !globalConfig.Callback.CallbackSingleMsgRead.Enable { return nil } req.CallbackCommand = cbapi.CallbackSingleMsgRead resp := &cbapi.CallbackSingleMsgReadResp{} - if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackMsgModify); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackSingleMsgRead); err != nil { return err } return nil diff --git a/internal/rpc/msg/sync_msg.go b/internal/rpc/msg/sync_msg.go index 379302e98..0a8b3f89e 100644 --- a/internal/rpc/msg/sync_msg.go +++ b/internal/rpc/msg/sync_msg.go @@ -137,6 +137,7 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq groupIDs = append(groupIDs, chatLog.GroupID) } } + // Retrieve sender and receiver information if len(sendIDs) != 0 { sendInfos, err := m.UserLocalCache.GetUsersInfo(ctx, sendIDs) if err != nil { @@ -155,6 +156,8 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq recvMap[recvInfo.UserID] = recvInfo.Nickname } } + + // Retrieve group information including member counts if len(groupIDs) != 0 { groupInfos, err := m.GroupLocalCache.GetGroupInfos(ctx, groupIDs) if err != nil { @@ -162,8 +165,14 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq } for _, groupInfo := range groupInfos { groupMap[groupInfo.GroupID] = groupInfo + // Get actual member count + memberIDs, err := m.GroupLocalCache.GetGroupMemberIDs(ctx, groupInfo.GroupID) + if err == nil { + groupInfo.MemberCount = uint32(len(memberIDs)) // Update the member count with actual number + } } } + // Construct response with updated information for _, chatLog := range chatLogs { pbchatLog := &msg.ChatLog{} utils.CopyStructFields(pbchatLog, chatLog) @@ -175,14 +184,14 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq switch chatLog.SessionType { case constant.SingleChatType, constant.NotificationChatType: pbchatLog.RecvNickname = recvMap[chatLog.RecvID] - case constant.GroupChatType, constant.SuperGroupChatType: - pbchatLog.SenderFaceURL = groupMap[chatLog.GroupID].FaceURL - pbchatLog.GroupMemberCount = groupMap[chatLog.GroupID].MemberCount - pbchatLog.RecvID = groupMap[chatLog.GroupID].GroupID - pbchatLog.GroupName = groupMap[chatLog.GroupID].GroupName - pbchatLog.GroupOwner = groupMap[chatLog.GroupID].OwnerUserID - pbchatLog.GroupType = groupMap[chatLog.GroupID].GroupType + groupInfo := groupMap[chatLog.GroupID] + pbchatLog.SenderFaceURL = groupInfo.FaceURL + pbchatLog.GroupMemberCount = groupInfo.MemberCount // Reflects actual member count + pbchatLog.RecvID = groupInfo.GroupID + pbchatLog.GroupName = groupInfo.GroupName + pbchatLog.GroupOwner = groupInfo.OwnerUserID + pbchatLog.GroupType = groupInfo.GroupType } resp.ChatLogs = append(resp.ChatLogs, pbchatLog) } diff --git a/internal/rpc/msg/verify.go b/internal/rpc/msg/verify.go index bdaa7fd87..5bfd7013e 100644 --- a/internal/rpc/msg/verify.go +++ b/internal/rpc/msg/verify.go @@ -158,9 +158,6 @@ func (m *msgServer) encapsulateMsgData(msg *sdkws.MsgData) { case constant.Custom: fallthrough case constant.Quote: - utils.SetSwitchFromOptions(msg.Options, constant.IsConversationUpdate, true) - utils.SetSwitchFromOptions(msg.Options, constant.IsUnreadCount, true) - utils.SetSwitchFromOptions(msg.Options, constant.IsSenderSync, true) case constant.Revoke: utils.SetSwitchFromOptions(msg.Options, constant.IsUnreadCount, false) utils.SetSwitchFromOptions(msg.Options, constant.IsOfflinePush, false) diff --git a/pkg/authverify/token.go b/pkg/authverify/token.go index 26c43532d..8127e08df 100644 --- a/pkg/authverify/token.go +++ b/pkg/authverify/token.go @@ -60,6 +60,7 @@ func CheckAdmin(ctx context.Context, config *config.GlobalConfig) error { } return errs.ErrNoPermission.Wrap(fmt.Sprintf("user %s is not admin userID", mcontext.GetOpUserID(ctx))) } + func CheckIMAdmin(ctx context.Context, config *config.GlobalConfig) error { if utils.IsContain(mcontext.GetOpUserID(ctx), config.IMAdmin.UserID) { return nil diff --git a/pkg/common/db/mgo/group.go b/pkg/common/db/mgo/group.go index a3d777f59..c63e3c376 100644 --- a/pkg/common/db/mgo/group.go +++ b/pkg/common/db/mgo/group.go @@ -16,9 +16,9 @@ package mgo import ( "context" - "github.com/OpenIMSDK/protocol/constant" "time" + "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" @@ -70,8 +70,12 @@ func (g *GroupMgo) Take(ctx context.Context, groupID string) (group *relation.Gr } func (g *GroupMgo) Search(ctx context.Context, keyword string, pagination pagination.Pagination) (total int64, groups []*relation.GroupModel, err error) { - return mgoutil.FindPage[*relation.GroupModel](ctx, g.coll, bson.M{"group_name": bson.M{"$regex": keyword}, - "status": bson.M{"$ne": constant.GroupStatusDismissed}}, pagination) + opts := options.Find().SetSort(bson.D{{Key: "created_at", Value: -1}}) + + return mgoutil.FindPage[*relation.GroupModel](ctx, g.coll, bson.M{ + "group_name": bson.M{"$regex": keyword}, + "status": bson.M{"$ne": constant.GroupStatusDismissed}, + }, pagination, opts) } func (g *GroupMgo) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) { diff --git a/pkg/rpcclient/group.go b/pkg/rpcclient/group.go index ec7aab695..3e9c93e7a 100644 --- a/pkg/rpcclient/group.go +++ b/pkg/rpcclient/group.go @@ -49,11 +49,7 @@ func NewGroupRpcClient(discov discoveryregistry.SvcDiscoveryRegistry, config *co return GroupRpcClient(*NewGroup(discov, config)) } -func (g *GroupRpcClient) GetGroupInfos( - ctx context.Context, - groupIDs []string, - complete bool, -) ([]*sdkws.GroupInfo, error) { +func (g *GroupRpcClient) GetGroupInfos(ctx context.Context, groupIDs []string, complete bool) ([]*sdkws.GroupInfo, error) { resp, err := g.Client.GetGroupsInfo(ctx, &group.GetGroupsInfoReq{ GroupIDs: groupIDs, }) @@ -184,11 +180,7 @@ func (g *GroupRpcClient) GetGroupInfoCache(ctx context.Context, groupID string) return resp.GroupInfo, nil } -func (g *GroupRpcClient) GetGroupMemberCache( - ctx context.Context, - groupID string, - groupMemberID string, -) (*sdkws.GroupMemberFullInfo, error) { +func (g *GroupRpcClient) GetGroupMemberCache(ctx context.Context, groupID string, groupMemberID string) (*sdkws.GroupMemberFullInfo, error) { resp, err := g.Client.GetGroupMemberCache(ctx, &group.GetGroupMemberCacheReq{ GroupID: groupID, GroupMemberID: groupMemberID, diff --git a/pkg/rpcclient/notification/friend.go b/pkg/rpcclient/notification/friend.go index dafca055a..751bdf475 100644 --- a/pkg/rpcclient/notification/friend.go +++ b/pkg/rpcclient/notification/friend.go @@ -126,10 +126,7 @@ func (f *FriendNotificationSender) UserInfoUpdatedNotification(ctx context.Conte return f.Notification(ctx, mcontext.GetOpUserID(ctx), changedUserID, constant.UserInfoUpdatedNotification, &tips) } -func (f *FriendNotificationSender) FriendApplicationAddNotification( - ctx context.Context, - req *pbfriend.ApplyToAddFriendReq, -) error { +func (f *FriendNotificationSender) FriendApplicationAddNotification(ctx context.Context, req *pbfriend.ApplyToAddFriendReq) error { tips := sdkws.FriendApplicationTips{FromToUserID: &sdkws.FromToUserID{ FromUserID: req.FromUserID, ToUserID: req.ToUserID, diff --git a/scripts/install/test.sh b/scripts/install/test.sh index 326307570..51d541ece 100755 --- a/scripts/install/test.sh +++ b/scripts/install/test.sh @@ -932,7 +932,7 @@ openim::test::set_group_info() { { "groupInfoForSet": { "groupID": "${1}", - "groupName": "new-name", + "groupName": "new group name", "notification": "new notification", "introduction": "new introduction", "faceURL": "www.newfaceURL.com", @@ -1076,6 +1076,7 @@ function openim::test::group() { local GROUP_ID=$RANDOM local GROUP_ID2=$RANDOM + # Assumes that TEST_GROUP_ID, USER_ID, and other necessary IDs are set as environment variables before running this suite. # 0. Register a friend user. openim::test::user_register "${USER_ID}" "group00" "new_face_url" diff --git a/scripts/lib/util.sh b/scripts/lib/util.sh index 7bcfbad97..dcce56c12 100755 --- a/scripts/lib/util.sh +++ b/scripts/lib/util.sh @@ -413,7 +413,7 @@ openim::util::check_process_names() { else # If there are PIDs, loop through each one for pid in "${pids[@]}"; do - local command=$(ps -p $pid -o cmd=) + local command=$(ps -p $pid -o comm=) local start_time=$(ps -p $pid -o lstart=) local port=$(get_port $pid) @@ -489,7 +489,7 @@ openim::util::check_process_names_for_stop() { else # If there are PIDs, loop through each one for pid in "${pids[@]}"; do - local command=$(ps -p $pid -o cmd=) + local command=$(ps -p $pid -o comm=) local start_time=$(ps -p $pid -o lstart=) local port=$(get_port $pid) diff --git a/scripts/verify-annotation-language.sh b/scripts/verify-annotation-language.sh deleted file mode 100755 index 6b863776c..000000000 --- a/scripts/verify-annotation-language.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash -# Copyright © 2023 OpenIM. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -# This script verifies whether codes follow golang convention. -# Usage: `scripts/verify-pkg-names.sh`. - -set -o errexit - -OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. -source "${OPENIM_ROOT}/scripts/lib/init.sh" - -openim::golang::verify_go_version - -openim::golang::verify_go_version - -OPENIM_OUTPUT_HOSTBIN_TOOLS="${OPENIM_ROOT}/_output/bin/tools/linux/amd64" -CODESCAN_BINARY="${OPENIM_OUTPUT_HOSTBIN_TOOLS}/codescan" - -if [[ ! -f "${CODESCAN_BINARY}" ]]; then - echo "codescan binary not found, building..." - pushd "${OPENIM_ROOT}" >/dev/null - make build BINS="codescan" - popd >/dev/null -fi - -if [[ ! -f "${CODESCAN_BINARY}" ]]; then - echo "Failed to build codescan binary." - exit 1 -fi - -CONFIG_PATH="${OPENIM_ROOT}/tools/codescan/config.yaml" - -"${CODESCAN_BINARY}" -config "${CONFIG_PATH}" \ No newline at end of file diff --git a/tools/codescan/checker/checker.go b/tools/codescan/checker/checker.go deleted file mode 100644 index ad724dd5b..000000000 --- a/tools/codescan/checker/checker.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright © 2024 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package checker - -import ( - "bufio" - "fmt" - "os" - "path/filepath" - "regexp" - "strings" - - "github.com/openimsdk/open-im-server/tools/codescan/config" -) - -type CheckResult struct { - FilePath string - Lines []int -} - -func checkFileForChineseComments(filePath string) ([]CheckResult, error) { - file, err := os.Open(filePath) - if err != nil { - return nil, err - } - defer file.Close() - - var results []CheckResult - scanner := bufio.NewScanner(file) - reg := regexp.MustCompile(`[\p{Han}]+`) - lineNumber := 0 - - var linesWithChinese []int - for scanner.Scan() { - lineNumber++ - if reg.FindString(scanner.Text()) != "" { - linesWithChinese = append(linesWithChinese, lineNumber) - } - } - - if len(linesWithChinese) > 0 { - results = append(results, CheckResult{ - FilePath: filePath, - Lines: linesWithChinese, - }) - } - - if err := scanner.Err(); err != nil { - return nil, err - } - - return results, nil -} - -func WalkDirAndCheckComments(cfg config.Config) error { - var allResults []CheckResult - err := filepath.Walk(cfg.Directory, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if info.IsDir() { - return nil - } - for _, fileType := range cfg.FileTypes { - if filepath.Ext(path) == fileType { - results, err := checkFileForChineseComments(path) - if err != nil { - return err - } - if len(results) > 0 { - allResults = append(allResults, results...) - } - } - } - return nil - }) - - if err != nil { - return err - } - - if len(allResults) > 0 { - var errMsg strings.Builder - errMsg.WriteString("Files containing Chinese comments:\n") - for _, result := range allResults { - errMsg.WriteString(fmt.Sprintf("%s: Lines %v\n", result.FilePath, result.Lines)) - } - return fmt.Errorf(errMsg.String()) - } - - return nil -} diff --git a/tools/codescan/codescan.go b/tools/codescan/codescan.go deleted file mode 100644 index a83e895fc..000000000 --- a/tools/codescan/codescan.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2024 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "log" - - "github.com/openimsdk/open-im-server/tools/codescan/checker" - "github.com/openimsdk/open-im-server/tools/codescan/config" -) - -func main() { - cfg, err := config.ParseConfig() - if err != nil { - log.Fatalf("Error parsing config: %v", err) - } - - err = checker.WalkDirAndCheckComments(cfg) - if err != nil { - panic(err) - } -} diff --git a/tools/codescan/config/config.go b/tools/codescan/config/config.go deleted file mode 100644 index aebf0d4f7..000000000 --- a/tools/codescan/config/config.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright © 2024 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package config - -import ( - "flag" - "log" - "os" - - "gopkg.in/yaml.v2" -) - -type Config struct { - Directory string `yaml:"directory"` - FileTypes []string `yaml:"file_types"` - Languages []string `yaml:"languages"` -} - -func ParseConfig() (Config, error) { - var configPath string - flag.StringVar(&configPath, "config", "./", "Path to config file") - flag.Parse() - - var config Config - if configPath != "" { - configFile, err := os.ReadFile(configPath) - if err != nil { - return Config{}, err - } - if err := yaml.Unmarshal(configFile, &config); err != nil { - return Config{}, err - } - } else { - log.Fatal("Config file must be provided") - } - return config, nil -} diff --git a/tools/codescan/go.mod b/tools/codescan/go.mod deleted file mode 100644 index 2ad132101..000000000 --- a/tools/codescan/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/openimsdk/open-im-server/tools/codescan - -go 1.19